--- /dev/null
+Janos Kovacs <janos.kovacs at intel dot com>
+Jaska Uimonen <jaska.uimonen at intel dot com>
+Ismo Puustinen <ismo.puustinen at intel dot com>
+Krisztian Litkey <krisztian.litkey at intel dot com>
--- /dev/null
+All Murphy source code files are licensed under the three-clause BSD
+license. See file LICENSE-BSD for the license text.
+
+It is possible to configure Murphy to use GPLv2-licensed libraries. At
+least libdbus is licensed under the GPLv2, and under some circumstances
+PulseAudio client library might also fall under the GPLv2. If Murphy is
+configured to use these GPL-licensed libraries, Murphy must be
+distributed under the restrictions defined in GPLv2 license text.
+
+To prevent mistakes in licensing, Murphy needs to be configured with
+--enable-gpl configuration option in order to use the GPLv2-licensed
+libaries. This is meant as a heads-up for the system integrator to
+double-check the licensing and distribution of possible proprietary
+Murphy plugins.
--- /dev/null
+Copyright (c) 2012, 2013, Intel Corporation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Intel Corporation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+AUTOMAKE_OPTIONS = foreign
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = . utils src doc
+doc_DATA = AUTHORS ChangeLog NEWS README
+
+# This is the only way with automake I know of to force 'check-git-hooks'
+# to be evaluated before 'all'. If there is a nicer way, I'm all ears...
+BUILT_SOURCES = install-git-hooks
+
+###################################
+# git hook management
+#
+
+install-git-hooks:
+ if test -d .git; then \
+ for hook in githooks/???*; do \
+ case $$hook in \
+ *.sample|*~|*.swp) continue;; \
+ esac; \
+ if test -x $$hook -a \
+ ! -x .git/hooks/$${hook##*/}; then \
+ echo "Installing git hook $${hook##*/}..."; \
+ cp $$hook .git/hooks; \
+ chmod a+x .git/hooks/$${hook##*/}; \
+ fi \
+ done \
+ fi
+
+check-git-hooks:
+ if test -d .git; then \
+ for hook in githooks/???*; do \
+ case $$hook in \
+ *.sample|*~|*.swp) continue;; \
+ esac; \
+ if test -x $$hook -a ! -e .git/hooks/$${hook##*/}; then \
+ echo ""; \
+ echo "WARNING:"; \
+ echo "WARNING: You have an uninstalled git hook $$hook"; \
+ echo "WARNING: Please, consider taking it into use by"; \
+ echo "WARNING: running 'make install-git-hooks'..."; \
+ echo "WARNING:"; \
+ echo ""; \
+ fi \
+ done \
+ fi
+
+# cleanup
+clean-local:
+ rm -f *~
--- /dev/null
+Please see README.md.
--- /dev/null
+Murphy resource policy manager
+==============================
+
+What is Murphy?
+---------------
+
+Murphy is a centralized resource policy daemon. Murphy assigns system
+resources to applications in an event-based and centralized way, meaning
+that resources are given to applications that request them in a priority
+order.
+
+Murphy works with many different resource domains and is intended to
+handle cross-domain resource conflicts. An example of this might be an
+interdependency between power management resources, audio resources, and
+video resources. If an application requires an instance of all the three
+resource types to work properly, taking a single one away from it should
+result in the release of the entire set of resources, since the
+application anyway requires the whole set to work properly.
+
+Murphy is designed to be extensively scripted and extended. The logic
+behind the decisions is encoded in the scipts in form of policy rules.
+These rules will be provided by the system intergrator. The extensions
+are implemented as Murphy plugins or external domain controllers.
+
+How does Murphy work?
+---------------------
+
+Murphy listens to three input event types:
+
+1. System events
+2. Application requests
+3. User-provided settings
+
+An input event (such as an application requesting permission to play
+audio) may change a value in Murphy internal database. This in turn
+triggers the decision making mechanism. The decision is made and
+communicated to domain controllers, which enforce the resource limits
+for different resource domains, and to the applications competing for
+the resources. Resource-aware applications are expected to comply with
+the decision, but for some domains the decisions can also be enforced.
+For the audio domain it might mean stopping or muting the stream that
+was not allowed to play.
+
+Why is Murphy needed?
+---------------------
+
+The main idea is to move policy responsibilities away from the
+applications. Automatic arbitration of the available resources is
+important especially in embedded systems with limited user interaction
+capabilities.
+
+The applications need to decide by themselves who can access the limited
+resources if there is no centralized resource manager. In order to do
+this, all applications have to:
+
+* understand the resource limits in the system (both software and
+ hardware)
+* follow other applications' access to the resources
+* define application priorities for resource access; who can access the
+ resource is there is a conflict?
+* handle exceptional cases, such as non-resource aware applications
+ accessing the limited resources
+
+It is clear that it is extremely difficult for the applications to
+cooperate in this way, and implementing and maintaining this support for
+every single application running on a system is a huge undertaking.
+Changing a policy would require changes to all applications. But if the
+applications offload the resource policing responsibilities to a central
+resource manager, the applications only need to use a well-defined
+resource API to request access to resources and follow the resource
+status. In order to do a policy change, the system integrator only needs
+to change the policy in resource manager configuration, and the desired
+behavior should automatically follow.
+
+Compilation of Murphy
+---------------------
+
+Detailed information on building Murphy, the dependencies and options
+can be found at the [documentation](https://01.org/murphy/documentation/compiling-and-installing-murphy).
+
+In general, Murphy is an Autotools-based project, so users who have
+used Autotools before should be relatively "at home" with the process
+of generating the configure script as well as configuring and compiling
+Murphy.
--- /dev/null
+#!/bin/bash
+
+aclocal -I m4 && \
+ autoheader && \
+ libtoolize --copy --force && \
+ autoconf && \
+ automake --add-missing --copy
+
+status=$?
+
+if [ $status == 0 ]; then
+ if [ -n "$1" ]; then
+ [ "$1" == "configure" ] && shift || :
+ ./configure $*
+ status=$?
+ fi
+else
+ echo "Failed to bootstrap."
+fi
+
+exit $status
--- /dev/null
+#!/bin/bash
+
+TOP="${0%/*}/.."
+LICENSE="$TOP/LICENSE-BSD"
+EXCLUDE=""
+
+show_usage () {
+ echo "usage: $0 [options]"
+ echo "The possible options are:"
+ echo " --dry-run|-n Just find files lacking license info."
+ echo " --license|-L <file> Use file to obtain license text."
+ echo " --git|-g Add license only to files in the repository."
+ echo " --exclude|-e <pat> Exclude files matching egrep pattern <pat>."
+ echo " --help|-h Show this help and exit."
+}
+
+fatal () {
+ local _err _msg
+
+ _err="$1"
+ shift
+ _msg="$*"
+
+ echo "fatal error: $_msg"
+ exit $_err
+}
+
+enlicense () {
+ local _file _in _out
+
+ case $1 in
+ *-func-info.c)
+ return 0
+ ;;
+ esac
+
+ _file="$1"
+ _in="$1.no-license"
+ _out="$1.license"
+
+ cp $_file $_in
+ echo "Inserting licensing information to $_file..."
+ echo "/*" > $_out
+ cat $LICENSE | sed 's/^ / /g;s/^/ * /g' \
+ | sed 's/ *$//g' >> $_out
+ echo " */" >> $_out
+ echo "" >> $_out
+ cat $_in >> $_out
+ cp $_out $_file
+}
+
+find_missing_licenses () {
+ local _lacking _files _f
+
+ _lacking=""
+ if [ -z "$EXCLUDE" ]; then
+ _files="`find . -name '*.[hc]'`"
+ else
+ _files="`find . -name '*.[hc]' | egrep -v -e $EXCLUDE`"
+ fi
+
+ for _f in $_files; do
+ _f="${_f#./}"
+ grep -ql 'Copyright .*Intel .*' $_f
+ if [ $? != 0 ]; then
+ if [ "$GIT" = "y" ]; then
+ git ls-files | grep -q "$_f\$" && _lacking="$_lacking $_f" || :
+ else
+ _lacking="$_lacking $_f"
+ fi
+ fi
+ done
+
+ echo "$_lacking"
+}
+
+
+DRY_RUN=""
+GIT=""
+
+while [ "${1#-}" != "$1" -a -n "$1" ]; do
+ case $1 in
+ --dry-run|-n)
+ DRY_RUN="y"
+ ;;
+ --license|-L)
+ if [ -n "$2" ]; then
+ shift
+ LICENSE="$1"
+ else
+ fatal 1 "missing license argument"
+ fi
+ ;;
+ --git|-g)
+ GIT="y"
+ ;;
+ --exclude|-e)
+ if [ -n "$2" ]; then
+ shift
+ EXCLUDE="$1"
+ else
+ fatal 1 "missing exclusion pattern"
+ fi
+ ;;
+ --help|-h)
+ show_usage
+ exit 0
+ ;;
+ *)
+ echo "Unknown command line option \'$1\'."
+ show_usage
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+if [ ! -f "$LICENSE" ]; then
+ fatal 1 "license file \'$LICENSE\' missing"
+fi
+
+pushd $TOP >& /dev/null
+
+lacking="`find_missing_licenses`"
+
+for f in $lacking; do
+ if [ "$DRY_RUN" != "y" ]; then
+ enlicense $f
+ else
+ echo "$f is lacking licensing information."
+ fi
+done
--- /dev/null
+#!/bin/bash
+
+######################################################################
+# Generate helper code for making builtin DSO plugins available.
+#
+# Sometimes it is necessary to split a plugin to several source
+# files. Usually this is the case when the functionality provided
+# by the plugin is complex enough to require subdividing the plugin
+# into internal modules for the best maintainability.
+#
+# In these cases we cannot avoid making several functions and
+# variables globally available within the plugin so we cannot just
+# limit the scope of visibility by making them static. Still, we do
+# want to avoid conflicts between these symbols of different plugins.
+# To accomplish this we compile and link such plugins into shared
+# libraries (with our normal symbol visibility conventions) and then
+# link the murphy daemon against them.
+#
+# Since none of the code linked into murphy proper references
+# any of the symbols in any of these plugins without any further
+# special arrangements these plugins wouldn't be demand-loaded
+# and consequently would be unavailable when the daemon starts up.
+#
+# To overcome this every such plugin gets automatically augmented
+# by a plugin-specifig globally visible symbol and the murphy daemon
+# gets augmented by a symbol that references these plugin-specific
+# symbols. This forces the dynamic loader to load the plugins and
+# the normal builtin-plugin autoregistration mechanism takes care of
+# the rest.
+#
+# A bit too spaceship-ish, I admit... but nevertheless necessary
+# unless we want to give up the idea of builtin/linkedin plugins...
+#
+
+
+error () {
+ echo "error: $*" 1>&2
+}
+
+info () {
+ echo "$*" 1>&2
+}
+
+warning () {
+ echo "warning: $*" 1>&2
+}
+
+usage () {
+ info "usage: $0 -p <plugin> -o <output-file>, or"
+ info "usage: $0 -o <output-file> -d <plugin-list>"
+ exit ${1:-1}
+}
+
+emit () {
+ echo "$*" >> $OUTPUT
+}
+
+emit_plugin_loader () {
+ case $OUTPUT in
+ *.c) emit "int mrp_linkedin_plugin_${1//-/_}_symbol = 0;"
+ ;;
+ *.h) emit "extern int mrp_linkedin_plugin_${1//-/_}_symbol;"
+ ;;
+ esac
+}
+
+emit_murphy_loader () {
+ local _p
+
+ for _p in $*; do
+ emit "#include \"linkedin-$_p-loader.h\""
+ done
+ emit ""
+ emit "int mrp_load_linkedin_plugins(void)"
+ emit "{"
+ emit " return \\"
+ for _p in $*; do
+ emit " mrp_linkedin_plugin_${_p//-/_}_symbol + \\"
+ done
+ emit " 0;"
+ emit "}"
+}
+
+
+OUTPUT=""
+PLUGIN=""
+PLUGIN_LIST=""
+daemon=no
+
+#echo "*** $0 $* ***"
+
+# parse command line
+while [ -n "${1#-}" ]; do
+ case $1 in
+ -o)
+ if [ -z "$OUTPUT" ]; then
+ shift
+ OUTPUT="$1"
+ else
+ error "Multiple output files requested."
+ usage
+ fi
+ ;;
+ -p)
+ if [ -z "$PLUGIN" -a "$daemon" = "no" ]; then
+ shift
+ PLUGIN="$1"
+ else
+ if [ -n "$PLUGIN" ]; then
+ error "Multiple builtin plugins specified."
+ else
+ error "Both builtin plugin and plugin list specified."
+ fi
+ usage
+ fi
+ ;;
+
+ -h)
+ usage 0
+ ;;
+ -q)
+ QUIET="yes"
+ ;;
+ -d)
+ daemon=yes
+ ;;
+
+ -*)
+ error "Unknown option '$1'."
+ usage
+ ;;
+
+ *)
+ if [ "$daemon" = "yes" ]; then
+ PLUGIN_LIST="$PLUGIN_LIST $1"
+ else
+ error "Unexpected argument '$1'."
+ usage
+ fi
+ ;;
+ esac
+ shift
+done
+
+# check that we've got everything mandatory
+if [ -z "$OUTPUT" ]; then
+ error "No output file specified (use the -o option)."
+ usage
+fi
+
+if [ -z "$PLUGIN" -a "$daemon" = "no" ]; then
+ error "Neither builtin plugin nor plugin list is specified."
+ usage
+fi
+
+# generate the output
+rm -f $OUTPUT
+touch $OUTPUT
+if [ "$daemon" = "no" ]; then
+ emit_plugin_loader $PLUGIN
+else
+ emit_murphy_loader $PLUGIN_LIST
+fi
--- /dev/null
+#!/bin/bash
+
+#LOG=/tmp/gen-linker-script.log
+#echo "$0 $*" > $LOG
+
+COLLECT_SYMBOLS="${0%gen-linker-script}../utils/collect-symbols"
+
+ARGS=""
+
+while [ -n "$*" ]; do
+# echo "[$ARGS]" 1>&2
+ case $1 in
+ -c) #echo " [$1] [$2]" 1>&2;
+ ARGS="$ARGS -c '$2'"; shift 2;;
+ *) #echo " [$1]" 1>&2;
+ ARGS="$ARGS '$1'" ; shift 1;;
+ esac
+done
+
+#echo "ARGS: [$ARGS]" 1>&2
+#echo "ARGS: [$ARGS]" >> $LOG
+#echo "$COLLECT_SYMBOLS -g $ARGS" >> $LOG
+
+eval "$COLLECT_SYMBOLS -g $ARGS"
--- /dev/null
+#!/bin/bash
+
+#LOG=/tmp/gen-linker-script.log
+#echo "$0 $*" > $LOG
+
+COLLECT_SYMBOLS="${0%gen-linker-script}../utils/collect-symbols"
+
+ARGS=""
+
+while [ -n "$*" ]; do
+# echo "[$ARGS]" 1>&2
+ case $1 in
+ -c) #echo " [$1] [$2]" 1>&2;
+ ARGS="$ARGS -c '$2'"; shift 2;;
+ *) #echo " [$1]" 1>&2;
+ ARGS="$ARGS '$1'" ; shift 1;;
+ esac
+done
+
+#echo "ARGS: [$ARGS]" 1>&2
+#echo "ARGS: [$ARGS]" >> $LOG
+#echo "$COLLECT_SYMBOLS -g $ARGS" >> $LOG
+
+eval "$COLLECT_SYMBOLS -g $ARGS"
--- /dev/null
+#!/bin/bash
+
+###############
+# Generate a linker script for GNU ld.
+#
+#
+#
+
+
+
+error () {
+ echo "error: $*" 1>&2
+}
+
+info () {
+ echo "$*" 1>&2
+}
+
+warning () {
+ echo "warning: $*" 1>&2
+}
+
+usage () {
+ info "usage: $0 [-p <pattern>] [-I <ignore-list>] -o <output> <inputs>"
+ exit ${1:-1}
+}
+
+emit () {
+ echo "$*" >> $OUTPUT
+}
+
+
+# set up defaults
+PATTERN="^mrp_|^_mrp_" # export everything prefixed with mrp_
+IGNORE="MRP_PRINTF_LIKE,MRP_NULLTERM" # ignore these symbols/macros
+IT="," # ignore-list is comma-separated
+SOURCES="" # no default input, must be specified
+OUTPUT="" # no default output, must be specified
+
+# parse command line
+while [ -n "${1#-}" ]; do
+ case $1 in
+ -o)
+ if [ -z "$OUTPUT" ]; then
+ shift
+ OUTPUT="$1"
+ else
+ error "Multiple output files requested."
+ usage
+ fi
+ ;;
+ -p)
+ shift;
+ PATTERN="$1"
+ ;;
+ -I)
+ shift
+ IGNORE="$IGNORE$IT$1"
+ IT=","
+ ;;
+ -h)
+ usage 0
+ ;;
+ -q)
+ QUIET="yes"
+ ;;
+ -c)
+ # This is only for command-line compatibility with collect-symbols
+ # to minimize the impact of switching back and forth (if needed).
+ # collect-symbols gets compilation flags passed using the -c
+ # option which we simply ignore here when using ctags.
+ shift
+ ;;
+ -*)
+ error "Unknown option '$1'."
+ usage
+ ;;
+ *)
+ SOURCES="$SOURCES $1"
+ ;;
+ esac
+ shift
+done
+
+# check that we've got everything mandatory
+if [ -z "$OUTPUT" ]; then
+ error "No output file specified (use the -o option)."
+ usage
+fi
+
+if [ -z "$SOURCES" ]; then
+ warning "No input files, generating local-only linker script."
+ emit "{"
+ emit " local:"
+ emit " *;"
+ emit "};"
+ exit 0
+fi
+
+if [ -z "$PATTERN" ]; then
+ PATTERN="^mrp_"
+fi
+
+if [ -n "$IGNORE" ]; then
+ ignore_opts="-I $IGNORE"
+else
+ ignore_opts=""
+fi
+
+# check that we have ctags
+which ctags >& /dev/null
+if [ "$?" != "0" ]; then
+ error "Needs ctags to regenerate linker script $OUTPUT..."
+ exit 1
+fi
+
+# generate the output
+[ -n "$QUIET" ] || info "Generating linker script $OUTPUT..."
+rm -f $OUTPUT
+touch $OUTPUT
+
+emit "{"
+emit " global:"
+ctags $ignore_opts -f - --c-kinds=px $SOURCES | \
+ awk "/$PATTERN/ { print \$1; }" | \
+ sort | \
+ while read sym; do
+ emit " $sym;"
+ done
+
+emit " local:"
+emit " *;"
+emit "};"
--- /dev/null
+#!/bin/sh
+# Print a version string.
+scriptversion=2008-04-08.07
+
+# Copyright (C) 2007-2008 Free Software Foundation
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+# This script is derived from GIT-VERSION-GEN from GIT: http://git.or.cz/.
+# It may be run two ways:
+# - from a git repository in which the "git describe" command below
+# produces useful output (thus requiring at least one signed tag)
+# - from a non-git-repo directory containing a .tarball-version file, which
+# presumes this script is invoked like "./git-version-gen .tarball-version".
+
+# In order to use intra-version strings in your project, you will need two
+# separate generated version string files:
+#
+# .tarball-version - present only in a distribution tarball, and not in
+# a checked-out repository. Created with contents that were learned at
+# the last time autoconf was run, and used by git-version-gen. Must not
+# be present in either $(srcdir) or $(builddir) for git-version-gen to
+# give accurate answers during normal development with a checked out tree,
+# but must be present in a tarball when there is no version control system.
+# Therefore, it cannot be used in any dependencies. GNUmakefile has
+# hooks to force a reconfigure at distribution time to get the value
+# correct, without penalizing normal development with extra reconfigures.
+#
+# .version - present in a checked-out repository and in a distribution
+# tarball. Usable in dependencies, particularly for files that don't
+# want to depend on config.h but do want to track version changes.
+# Delete this file prior to any autoconf run where you want to rebuild
+# files to pick up a version string change; and leave it stale to
+# minimize rebuild time after unrelated changes to configure sources.
+#
+# It is probably wise to add these two files to .gitignore, so that you
+# don't accidentally commit either generated file.
+#
+# Use the following line in your configure.ac, so that $(VERSION) will
+# automatically be up-to-date each time configure is run (and note that
+# since configure.ac no longer includes a version string, Makefile rules
+# should not depend on configure.ac for version updates).
+#
+# AC_INIT([GNU project],
+# m4_esyscmd([build-aux/git-version-gen .tarball-version]),
+# [bug-project@example])
+#
+# Then use the following lines in your Makefile.am, so that .version
+# will be present for dependencies, and so that .tarball-version will
+# exist in distribution tarballs.
+#
+# BUILT_SOURCES = $(top_srcdir)/.version
+# $(top_srcdir)/.version:
+# echo $(VERSION) > $@-t && mv $@-t $@
+# dist-hook:
+# echo $(VERSION) > $(distdir)/.tarball-version
+
+case $# in
+ 1) ;;
+ *) echo 1>&2 "Usage: $0 \$srcdir/.tarball-version"; exit 1;;
+esac
+
+tarball_version_file=$1
+nl='
+'
+
+# First see if there is a tarball-only version file.
+# then try "git describe", then default.
+if test -f $tarball_version_file
+then
+ v=`cat $tarball_version_file` || exit 1
+ case $v in
+ *$nl*) v= ;; # reject multi-line output
+ [0-9]*) ;;
+ *) v= ;;
+ esac
+ test -z "$v" \
+ && echo "$0: WARNING: $tarball_version_file seems to be damaged" 1>&2
+fi
+
+if test -n "$v"
+then
+ : # use $v
+elif test -d .git \
+ && v=`git describe --abbrev=4 --match='v*' HEAD 2>/dev/null \
+ || git describe --abbrev=4 HEAD 2>/dev/null` \
+ && case $v in
+ v[0-9]*) ;;
+ *) (exit 1) ;;
+ esac
+then
+ # Is this a new git that lists number of commits since the last
+ # tag or the previous older version that did not?
+ # Newer: v6.10-77-g0f8faeb
+ # Older: v6.10-g0f8faeb
+# case $v in
+# *-*-*) : git describe is okay three part flavor ;;
+# *-*)
+# : git describe is older two part flavor
+# # Recreate the number of commits and rewrite such that the
+# # result is the same as if we were using the newer version
+# # of git describe.
+# vtag=`echo "$v" | sed 's/-.*//'`
+# numcommits=`git rev-list "$vtag"..HEAD | wc -l`
+# v=`echo "$v" | sed "s/\(.*\)-\(.*\)/\1-$numcommits-\2/"`;
+# ;;
+# esac
+
+ # Change the first '-' to a '.', so version-comparing tools work properly.
+ # Remove the "g" in git describe's output string, to save a byte.
+# v=`echo "$v" | sed 's/-/./;s/\(.*\)-g/\1-/'`;
+ :
+else
+ #v=UNKNOWN
+ v="0.0.0"
+fi
+
+v=`echo "$v" |sed 's/^v//'`
+
+# Don't declare a version "dirty" merely because a time stamp has changed.
+git status > /dev/null 2>&1
+
+dirty=`sh -c 'git diff-index --name-only HEAD' 2>/dev/null` || dirty=
+case "$dirty" in
+ '') ;;
+ *) # Append the suffix only if there isn't one already.
+ case $v in
+ *-dirty) ;;
+ *) v="$v-dirty" ;;
+ esac ;;
+esac
+
+# Omit the trailing newline, so that m4_esyscmd can use the result directly.
+echo "$v" | tr -d '\012'
+
+# Local variables:
+# eval: (add-hook 'write-file-hooks 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-end: "$"
+# End:
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+#
+# 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.
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+ last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the real libtool to use
+LIBTOOL="$1"
+shift
+
+# if 1, don't print anything, the underlaying wrapper will do it
+pass_though=0
+
+# scan the arguments, keep the right ones for libtool, and discover the mode
+preserved_args=
+
+# have we seen the --tag option of libtool in the command line ?
+tag_seen=0
+
+while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --mode=*)
+ mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+ preserved_args="$preserved_args $opt"
+ ;;
+ -o)
+ lt_output="$1"
+ preserved_args="$preserved_args $opt"
+ ;;
+ --tag=*)
+ tag_seen=1
+ preserved_args="$preserved_args $opt"
+ ;;
+ *)
+ preserved_args="$preserved_args '$opt'"
+ ;;
+ esac
+done
+
+case "$mode" in
+compile)
+ # shave will be called and print the actual CC/CXX/LINK line
+ preserved_args="$preserved_args --shave-mode=$mode"
+ pass_though=1
+ ;;
+link)
+ preserved_args="$preserved_args --shave-mode=$mode"
+ Q=" LINK "
+ ;;
+*)
+ # let's u
+ # echo "*** libtool: Unimplemented mode: $mode, fill a bug report"
+ ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+# automake does not add a --tag switch to its libtool invocation when
+# assembling a .s file and rely on libtool to infer the right action based
+# on the compiler name. As shave is using CC to hook a wrapper, libtool gets
+# confused. Let's detect these cases and add a --tag=CC option.
+tag=""
+if test $tag_seen -eq 0 -a x"$mode" = xcompile; then
+ tag="--tag=CC"
+fi
+
+if test -z $V; then
+ if test $pass_though -eq 0; then
+ echo "$Q$output"
+ fi
+ eval "$LIBTOOL --silent $tag $preserved_args"
+else
+ echo $LIBTOOL $tag $preserved_args
+ eval "$LIBTOOL $tag $preserved_args"
+fi
--- /dev/null
+#!/bin/sh
+#
+# Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+#
+# 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.
+
+# we need sed
+SED=@SED@
+if test -z "$SED" ; then
+SED=sed
+fi
+
+lt_unmangle ()
+{
+ last_result=`echo $1 | $SED -e 's#.libs/##' -e 's#[0-9a-zA-Z_\-\.]*_la-##'`
+}
+
+# the tool to wrap (cc, cxx, ar, ranlib, ..)
+tool="$1"
+shift
+
+# the reel tool (to call)
+REEL_TOOL="$1"
+shift
+
+pass_through=0
+preserved_args=
+while test "$#" -gt 0; do
+ opt="$1"
+ shift
+
+ case $opt in
+ --shave-mode=*)
+ mode=`echo $opt | $SED -e 's/[-_a-zA-Z0-9]*=//'`
+ ;;
+ -o)
+ lt_output="$1"
+ preserved_args="$preserved_args $opt"
+ ;;
+ -out:*|/out:*)
+ lt_output="${opt#-out:}"
+ preserved_args="$preserved_args $opt"
+ ;;
+ *.l)
+ if [ "$tool" = "lex" ]; then
+ lt_output="$opt"
+ fi
+ preserved_args="$preserved_args $opt"
+ ;;
+ *.y)
+ if [ "$tool" = "yacc" ]; then
+ lt_output="$opt"
+ fi
+ preserved_args="$preserved_args $opt"
+ ;;
+ *)
+ preserved_args="$preserved_args '$opt'"
+ ;;
+ esac
+done
+
+# mode=link is handled in the libtool wrapper
+case "$mode,$tool" in
+link,*)
+ pass_through=1
+ ;;
+*,cxx)
+ Q=" CXX "
+ ;;
+*,ccas)
+ Q=" AS "
+ ;;
+*,cc)
+ Q=" CC "
+ ;;
+*,fc)
+ Q=" FC "
+ ;;
+*,f77)
+ Q=" F77 "
+ ;;
+*,objc)
+ Q=" OBJC "
+ ;;
+*,mcs)
+ Q=" MCS "
+ ;;
+*,lex)
+ Q=" LEX "
+ ;;
+*,yacc)
+ Q=" YACC "
+ ;;
+*,cc_for_build)
+ Q=" HOSTCC "
+ ;;
+*,*)
+ # should not happen
+ Q=" CC "
+ ;;
+esac
+
+lt_unmangle "$lt_output"
+output=$last_result
+
+if test -z $V; then
+ if test $pass_through -eq 0; then
+ echo "$Q$output"
+ fi
+ eval "$REEL_TOOL $preserved_args"
+else
+ echo $REEL_TOOL $preserved_args
+ eval "$REEL_TOOL $preserved_args"
+fi
--- /dev/null
+
+# -*- Autoconf -*-
+# Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.59)
+
+AC_INIT([murphy], m4_esyscmd([build-aux/git-version-gen .tarball-version]))
+
+AC_CONFIG_SRCDIR([src])
+AC_CONFIG_MACRO_DIR([m4])
+AC_CONFIG_HEADER([src/config.h])
+AM_INIT_AUTOMAKE([-Wno-portability])
+
+AC_SUBST(ACLOCAL_AMFLAGS, "-I m4")
+
+m4_define(version_major, `echo $VERSION | cut -d. -f1 | cut -d- -f1`)
+m4_define(version_minor, `echo $VERSION | cut -d. -f2 | cut -d- -f1`)
+m4_define(version_patch, `echo $VERSION | cut -d. -f3 | cut -d- -f1`)
+
+AC_SUBST(VERSION)
+AC_SUBST(VERSION_MAJOR, version_major)
+AC_SUBST(VERSION_MINOR, version_minor)
+AC_SUBST(VERSION_PATCH, version_patch)
+AC_SUBST(VERSION_FULL, version_major.version_minor.version_patch)
+
+MURPHY_VERSION_INFO="0:0:0"
+AC_SUBST(MURPHY_VERSION_INFO)
+
+# Disable static libraries.
+AC_DISABLE_STATIC
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_CC_C99
+# We need AC_PROG_CXX if Qt support is enabled but (at least some
+# versions of autotools) cannot handle conditional use of this.
+AC_PROG_CXX
+AC_PROG_AWK
+AC_PROG_INSTALL
+AM_PROG_CC_C_O
+AM_PROG_LIBTOOL
+AC_PROG_LEX
+AC_PROG_YACC
+AM_PROG_LEX
+AC_SUBST(LEXLIB)
+
+# Check that we have flex/bison and not lex/yacc.
+AC_MSG_CHECKING([for flex vs. lex])
+case $LEX in
+ *missing\ flex*)
+ AC_MSG_ERROR([looks like you're missing flex])
+ ;;
+ *flex*)
+ AC_MSG_RESULT([ok, looks like we have flex])
+ ;;
+ *)
+ AC_MSG_ERROR([flex is required])
+ ;;
+esac
+
+AC_MSG_CHECKING([for bison vs. yacc])
+case $YACC in
+ *missing\ *)
+ AC_MSG_ERROR([looks like you're missing bison])
+ ;;
+ *bison*)
+ AC_MSG_RESULT([ok, looks like we have bison])
+ ;;
+ *)
+ AC_MSG_ERROR([bison is required])
+ ;;
+esac
+
+# Guesstimate native compiler if we're cross-compiling.
+if test "$cross_compiling" != "no"; then
+ AC_MSG_NOTICE([Looks like we're being cross-compiled...])
+ if test -z "$CC_FOR_BUILD"; then
+ CC_FOR_BUILD=cc
+ fi
+else
+ AC_MSG_NOTICE([Looks like we're doing a native compilation...])
+ CC_FOR_BUILD='$(CC)'
+fi
+AC_SUBST(CC_FOR_BUILD)
+UNSHAVED_CC_FOR_BUILD="$CC_FOR_BUILD"
+
+# Make first invocation of PKG_CHECK_MODULES 'if-then-else-fi'-safe.
+PKG_PROG_PKG_CONFIG
+
+# Checks for libraries.
+AC_CHECK_LIB([dl], [dlopen dlclose dlsym dlerror])
+
+# Checks for header files.
+AC_PATH_X
+AC_CHECK_HEADERS([fcntl.h stddef.h stdint.h stdlib.h string.h sys/statvfs.h sys/vfs.h syslog.h unistd.h])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+AC_C_INLINE
+AC_TYPE_INT16_T
+AC_TYPE_INT32_T
+AC_TYPE_INT64_T
+AC_TYPE_MODE_T
+AC_TYPE_PID_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_CHECK_MEMBERS([struct stat.st_rdev])
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINT8_T
+AC_CHECK_TYPES([ptrdiff_t])
+
+# Checks for library functions.
+AC_FUNC_ERROR_AT_LINE
+AC_HEADER_MAJOR
+if test "$cross_compiling" = "no"; then
+ AC_FUNC_MALLOC
+fi
+AC_FUNC_STRTOD
+AC_CHECK_FUNCS([clock_gettime memmove memset regcomp strcasecmp strchr strdup strrchr strtol strtoul])
+
+# Check and enable extra compiler warnings if they are supported.
+AC_ARG_ENABLE(extra-warnings,
+ [ --enable-extra-warnings enable extra compiler warnings],
+ [extra_warnings=$enableval], [extra_warnings=auto])
+
+WARNING_CFLAGS=""
+warncflags="-Wall -Wextra"
+if test "$extra_warnings" != "no"; then
+ save_CPPFLAGS="$CPPFLAGS"
+ for opt in $warncflags; do
+ AC_PREPROC_IFELSE([AC_LANG_PROGRAM([])],
+ [WARNING_CFLAGS="$WARNING_CFLAGS $opt"])
+ done
+ CPPFLAGS="$save_CPPFLAGS"
+fi
+
+AC_SUBST(WARNING_CFLAGS)
+
+# By default try to find the system default Lua (assumed to be
+# called lua(.pc). If that is not found, try to look for
+# packages lua5.2 and lua5.1, which can be found in Debian-based
+# distributions.
+#
+# You can override this using the --with-lua option. For instance
+# to use Lua 5.1 on Ubuntu while having 5.2 installed, you'd use
+# --with-lua=lua5.1.
+AC_ARG_WITH(lua,
+ [ --with-lua build with specified Lua (pkgconfig filename without .pc suffix)],
+ [with_lua=$withval], [with_lua=default])
+
+if test "x$with_lua" = "xdefault"; then
+ # Check for "lua" first, then "lua5.2" and finally for "lua5.1"
+ AC_MSG_NOTICE([Checking for an installed Lua...])
+ PKG_CHECK_MODULES([LUA], [lua >= 5.1.1],
+ [with_lua=lua],
+ [PKG_CHECK_MODULES([LUA52], [lua5.2],
+ [with_lua=lua5.2
+ LUA_CFLAGS=$LUA52_CFLAGS
+ LUA_LIBS=$LUA52_LIBS],
+ [PKG_CHECK_MODULES([LUA51], [lua5.1 >= 5.1.1],
+ [with_lua=lua5.1
+ LUA_CFLAGS=$LUA51_CFLAGS
+ LUA_LIBS=$LUA51_LIBS],
+ [AC_MSG_ERROR(Package requirement (lua >= 5.1.1) was not met!)])
+ ])
+ ])
+else
+ # Check for pre-defined Lua.
+ AC_MSG_NOTICE([Compiling with Lua package $with_lua.])
+ PKG_CHECK_MODULES(LUA, $with_lua >= 5.1.1)
+fi
+
+AC_SUBST(LUA_CFLAGS)
+AC_SUBST(LUA_LIBS)
+
+# Check if potentially GPL bits are allowed to be enabled.
+AC_ARG_ENABLE(gpl,
+ [ --enable-gpl enable linking against GPL code],
+ [enable_gpl=$enableval], [enable_gpl=no])
+
+# Check if original libdbus-based DBUS support was enabled.
+AC_ARG_ENABLE(libdbus,
+ [ --enable-libdbus enable libdbus-based D-BUS support],
+ [enable_libdbus=$enableval], [enable_libdbus=no])
+
+if test "$enable_libdbus" = "yes"; then
+ if test "$enable_gpl" = "no"; then
+ AC_MSG_ERROR([libdbus D-Bus support requires the --enable-gpl option.])
+ fi
+ PKG_CHECK_MODULES(LIBDBUS, dbus-1 >= 0.70)
+
+ DBUS_SESSION_DIR="`pkg-config --variable session_bus_services_dir dbus-1`"
+ AC_SUBST(DBUS_SESSION_DIR)
+
+ AC_DEFINE([LIBDBUS_ENABLED], 1, [Enable libdbus D-Bus support ?])
+else
+ AC_MSG_NOTICE([libdbus-based D-Bus support is disabled.])
+fi
+
+AM_CONDITIONAL(LIBDBUS_ENABLED, [test "$enable_libdbus" = "yes"])
+AC_SUBST(LIBDBUS_ENABLED)
+AC_SUBST(LIBDBUS_CFLAGS)
+AC_SUBST(LIBDBUS_LIBS)
+
+# Check if systemd-bus-based D-Bus support was enabled.
+AC_ARG_ENABLE(sdbus,
+ [ --enable-sdbus enable systemd-based D-BUS support],
+ [enable_sdbus=$enableval], [enable_sdbus=no])
+
+if test "$enable_sdbus" = "yes"; then
+ PKG_CHECK_MODULES(SDBUS, libsystemd-bus)
+ AC_DEFINE([SDBUS_ENABLED], 1, [Enable systemd-bus support ?])
+
+ if test -z "$DBUS_SESSION_DIR"; then
+ # Try to determine the session bus service directory.
+ DBUS_SESSION_DIR="`pkg-config --variable \
+ session_bus_services_dir dbus-1`"
+ if test "$?" != "0" -o -z "$DBUS_SESSION_DIR"; then
+ DBUS_SESSION_DIR="/usr/share/dbus-1/services"
+ fi
+ AC_SUBST(DBUS_SESSION_DIR)
+ fi
+else
+ AC_MSG_NOTICE([libsystemd-bus based D-Bus support is disabled.])
+fi
+
+AM_CONDITIONAL(SDBUS_ENABLED, [test "$enable_sdbus" = "yes"])
+AC_SUBST(SDBUS_ENABLED)
+AC_SUBST(SDBUS_CFLAGS)
+AC_SUBST(SDBUS_LIBS)
+
+# Check if PulseAudio mainloop support was enabled.
+AC_ARG_ENABLE(pulse,
+ [ --enable-pulse enable PulseAudio mainloop support],
+ [enable_pulse=$enableval], [enable_pulse=auto])
+
+if test "$enable_pulse" != "no"; then
+ PKG_CHECK_MODULES(PULSE, libpulse >= 0.9.22,
+ [have_pulse=yes], [have_pulse=no])
+ if test "$have_pulse" = "no" -a "$enable_pulse" = "yes"; then
+ AC_MSG_ERROR([PulseAudio development libraries not found.])
+ fi
+
+ if test "$enable_gpl" = "no"; then
+ if test "$enable_pulse" = "yes"; then
+ AC_MSG_ERROR([PulseAudio support requires the --enable-gpl option.])
+ else
+ enable_pulse="no"
+ fi
+ else
+ enable_pulse="$have_pulse"
+ fi
+else
+ AC_MSG_NOTICE([PulseAudio mainloop support is disabled.])
+fi
+
+if test "$enable_pulse" = "yes"; then
+ AC_DEFINE([PULSE_ENABLED], 1, [Enable PulseAudio mainloop support ?])
+fi
+AM_CONDITIONAL(PULSE_ENABLED, [test "$enable_pulse" = "yes"])
+AC_SUBST(PULSE_ENABLED)
+AC_SUBST(PULSE_CFLAGS)
+AC_SUBST(PULSE_LIBS)
+
+# Check if EFL/ecore mainloop support was enabled.
+AC_ARG_ENABLE(ecore,
+ [ --enable-ecore enable EFL/ecore mainloop support],
+ [enable_ecore=$enableval], [enable_ecore=auto])
+
+
+if test "$enable_ecore" != "no"; then
+ # We are using features which are present only at ecore 1.2 onwards.
+ PKG_CHECK_MODULES(ECORE, ecore >= 1.2,
+ [have_ecore=yes], [have_ecore=no])
+ if test "$have_ecore" = "no" -a "$enable_ecore" = "yes"; then
+ AC_MSG_ERROR([EFL/ecore development libraries not found.])
+ fi
+
+ enable_ecore="$have_ecore"
+else
+ AC_MSG_NOTICE([EFL/ecore mainloop support is disabled.])
+fi
+
+if test "$enable_ecore" = "yes"; then
+ AC_DEFINE([ECORE_ENABLED], 1, [Enable EFL/ecore mainloop support ?])
+fi
+AM_CONDITIONAL(ECORE_ENABLED, [test "$enable_ecore" = "yes"])
+AC_SUBST(ECORE_ENABLED)
+AC_SUBST(ECORE_CFLAGS)
+AC_SUBST(ECORE_LIBS)
+
+# Check if glib mainloop support was enabled.
+AC_ARG_ENABLE(glib,
+ [ --enable-glib enable glib mainloop support],
+ [enable_glib=$enableval], [enable_glib=auto])
+
+if test "$enable_glib" != "no"; then
+ PKG_CHECK_MODULES(GLIB, glib-2.0,
+ [have_glib=yes], [have_glib=no])
+ if test "$have_glib" = "no" -a "$enable_glib" = "yes"; then
+ AC_MSG_ERROR([glib development libraries not found.])
+ fi
+
+ enable_glib="$have_glib"
+else
+ AC_MSG_NOTICE([glib mainloop support is disabled.])
+fi
+
+if test "$enable_glib" = "yes"; then
+ AC_DEFINE([GLIB_ENABLED], 1, [Enable glib mainloop support ?])
+fi
+AM_CONDITIONAL(GLIB_ENABLED, [test "$enable_glib" = "yes"])
+AC_SUBST(GLIB_ENABLED)
+AC_SUBST(GLIB_CFLAGS)
+AC_SUBST(GLIB_LIBS)
+
+# Check if qt mainloop support was enabled.
+AC_ARG_ENABLE(qt,
+ [ --enable-qt enable qt mainloop support],
+ [enable_qt=$enableval], [enable_qt=auto])
+
+if test "$enable_qt" != "no"; then
+ PKG_CHECK_MODULES(QTCORE, QtCore,
+ [have_qt=yes], [have_qt=no])
+ if test "$have_qt" = "no" -a "$enable_qt" = "yes"; then
+ AC_MSG_ERROR([Qt(Core) development libraries not found.])
+ fi
+
+ enable_qt="$have_qt"
+else
+ AC_MSG_NOTICE([Qt mainloop support is disabled.])
+fi
+
+if test "$enable_qt" = "yes"; then
+ AC_DEFINE([QT_ENABLED], 1, [Enable qt mainloop support ?])
+ QT_MOC="`pkg-config --variable moc_location QtCore`"
+ AC_SUBST(QT_MOC)
+fi
+AM_CONDITIONAL(QT_ENABLED, [test "$enable_qt" = "yes"])
+AC_SUBST(QT_ENABLED)
+AC_SUBST(QTCORE_CFLAGS)
+AC_SUBST(QTCORE_LIBS)
+
+# Check if building murphy-console was enabled.
+AC_ARG_ENABLE(console,
+ [ --enable-console build Murphy console],
+ [enable_console=$enableval], [enable_console=yes])
+
+if test "$enable_console" = "no"; then
+ AC_MSG_NOTICE([Murphy console binary is disabled.])
+else
+ AC_MSG_NOTICE([Murphy console binary is enabled.])
+fi
+
+if test "$enable_console" = "yes"; then
+ AC_DEFINE([CONSOLE_ENABLED], 1, [Build Murphy console ?])
+fi
+AM_CONDITIONAL(CONSOLE_ENABLED, [test "$enable_console" = "yes"])
+AC_SUBST(CONSOLE_ENABLED)
+AC_SUBST(READLINE_CFLAGS)
+AC_SUBST(READLINE_LIBS)
+
+# Check for json(-c).
+PKG_CHECK_MODULES(JSON, [json], [have_json=yes], [have_json=no])
+
+if test "$have_json" = "no"; then
+ PKG_CHECK_MODULES(JSON, [json-c >= 0.11])
+fi
+
+AC_MSG_CHECKING([if json-c has headers under json-c include path])
+saved_CFLAGS="$CFLAGS"
+saved_LIBS="$LIBS"
+CFLAGS="${JSON_CFLAGS}"
+LIBS="${JSON_LIBS}"
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <../json-c/json.h>]],
+ [[return 0;]])],
+ [json_include_jsonc=yes],
+ [json_include_jsonc=no])
+AC_MSG_RESULT([$json_include_jsonc])
+CFLAGS="$saved_CFLAGS"
+LIBS="$saved_LIBS"
+
+if test "$json_include_jsonc" = "yes"; then
+ AC_DEFINE([JSON_INCLUDE_PATH_JSONC], 1, [json headers under json-c ?])
+fi
+
+AC_MSG_CHECKING([for json_tokener_get_error()])
+saved_CFLAGS="$CFLAGS"
+saved_LIBS="$LIBS"
+CFLAGS="${JSON_CFLAGS}"
+LIBS="${JSON_LIBS}"
+AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <json.h>]],
+ [[json_tokener *tok = NULL;
+ if (json_tokener_get_error(tok) != json_tokener_success)
+ return 0;
+ else
+ return 1;]])],
+ [have_json_tokener_get_error=yes],
+ [have_json_tokener_get_error=no])
+AC_MSG_RESULT([$have_json_tokener_get_error])
+CFLAGS="$saved_CFLAGS"
+LIBS="$saved_LIBS"
+
+if test "$have_json_tokener_get_error" = "yes"; then
+ AC_DEFINE([HAVE_JSON_TOKENER_GET_ERROR], 1, [json_tokener_get_error ?])
+fi
+
+# Check if websocket support was/can be enabled.
+CHECK_WEBSOCKETS()
+
+# Check if SMACK support should be enabled.
+AC_ARG_ENABLE(smack,
+ [ --enable-smack enable SMACK support],
+ [enable_smack=$enableval], [enable_smack=auto])
+
+if test "$enable_smack" != "no"; then
+ PKG_CHECK_MODULES(SMACK, libsmack, [have_smack=yes], [have_smack=no])
+ if test "$have_smack" = "no" -a "$enable_smack" = "yes"; then
+ AC_MSG_ERROR([SMACK development libraries not found.])
+ fi
+
+ enable_smack="$have_smack"
+else
+ AC_MSG_NOTICE([SMACK support is disabled.])
+fi
+
+if test "$enable_smack" = "yes"; then
+ AC_DEFINE([SMACK_ENABLED], 1, [Enable SMACK support ?])
+fi
+AM_CONDITIONAL(SMACK_ENABLED, [test "$enable_smack" = "yes"])
+AC_SUBST(SMACK_ENABLED)
+AC_SUBST(SMACK_CFLAGS)
+AC_SUBST(SMACK_LIBS)
+
+# Check if systemd support should be enabled.
+AC_ARG_ENABLE(systemd,
+ [ --enable-systemd enable systemd support],
+ [enable_systemd=$enableval], [enable_systemd=auto])
+
+if test "$enable_systemd" != "no"; then
+ PKG_CHECK_MODULES(SYSTEMD, libsystemd-journal libsystemd-daemon,
+ [have_systemd=yes], [have_systemd=no])
+ if test "$have_systemd" = "no" -a "$enable_systemd" = "yes"; then
+ AC_MSG_ERROR([systemd development libraries not found.])
+ fi
+
+ enable_systemd="$have_systemd"
+else
+ AC_MSG_NOTICE([systemd support is disabled.])
+fi
+
+if test "$enable_systemd" = "yes"; then
+ AC_DEFINE([SYSTEMD_ENABLED], 1, [Enable systemd support ?])
+fi
+AM_CONDITIONAL(SYSTEMD_ENABLED, [test "$enable_systemd" = "yes"])
+AC_SUBST(SYSTEMD_ENABLED)
+AC_SUBST(SYSTEMD_CFLAGS)
+AC_SUBST(SYSTEMD_LIBS)
+
+# Set up murphy CFLAGS and LIBS.
+MURPHY_CFLAGS=""
+MURPHY_LIBS=""
+AC_SUBST(MURPHY_CFLAGS)
+AC_SUBST(MURPHY_LIBS)
+
+# Allow substitution for LIBDIR and SYSCONFDIR.
+AC_MSG_CHECKING([libdir])
+AC_MSG_RESULT([$libdir])
+AC_SUBST(LIBDIR, [$libdir])
+AC_MSG_CHECKING([sysconfdir])
+AC_MSG_RESULT([$sysconfdir])
+AC_SUBST(SYSCONFDIR, [$sysconfdir])
+
+#Check whether we build resources or not
+AC_ARG_WITH(resources,
+ [ --with-resources wheter to build resource management support],
+ [with_resources=$withval],[with_resources=yes])
+
+AM_CONDITIONAL(BUILD_RESOURCES, [ test x$with_resources = "xyes" ])
+
+
+# Check which plugins should be disabled.
+AC_ARG_WITH(disabled-plugins,
+ [ --with-disabled-plugins=<plugin-list> specify which plugins to disable],
+ [disabled_plugins=$withval],[disabled_plugins=none])
+
+# Check which plugins should be compiled as standalone DSOs.
+AC_ARG_WITH(dynamic-plugins,
+ [ --with-dynamic-plugins=<plugin-list> specify which plugins compile as DSOs],
+ [dynamic_plugins=$withval],[dynamic_plugins=none])
+
+all_plugins=$(find src/plugins/. -name plugin-*.c 2>/dev/null | \
+ sed 's#^.*/plugin-##g;s#\.c$##g' | tr '\n' ' ')
+
+#echo "all plugins: [$all_plugins]"
+
+case $dynamic_plugins in
+ all) dynamic_plugins="$all_plugins";;
+ none) dynamic_plugins="";;
+esac
+
+internal=""; it=""
+external=""; et=""
+disabled=""; dt=""
+for plugin in $all_plugins; do
+ type=internal
+
+ for p in ${dynamic_plugins//,/ }; do
+ if test "$plugin" = "$p"; then
+ type=external
+ fi
+ done
+
+ for p in ${disabled_plugins//,/ }; do
+ if test "$plugin" = "$p"; then
+ type=disabled
+ fi
+ done
+
+ case $type in
+ internal) internal="$internal$it$plugin"; it=" ";;
+ external) external="$external$et$plugin"; et=" ";;
+ disabled) disabled="$disabled$dt$plugin"; dt=" ";;
+ esac
+done
+
+DISABLED_PLUGINS="$disabled"
+INTERNAL_PLUGINS="$internal"
+EXTERNAL_PLUGINS="$external"
+
+
+function check_if_disabled() {
+ for p in $DISABLED_PLUGINS; do
+ if test "$1" = "$p"; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+function check_if_internal() {
+ for p in $INTERNAL_PLUGINS; do
+ if test "$1" = "$p"; then
+ return 0
+ fi
+ done
+
+ return 1
+}
+
+AM_CONDITIONAL(DISABLED_PLUGIN_TEST, [check_if_disabled test])
+AM_CONDITIONAL(DISABLED_PLUGIN_DBUS, [check_if_disabled dbus])
+AM_CONDITIONAL(DISABLED_PLUGIN_GLIB, [check_if_disabled glib])
+AM_CONDITIONAL(DISABLED_PLUGIN_CONSOLE, [check_if_disabled console])
+AM_CONDITIONAL(DISABLED_PLUGIN_RESOURCE_DBUS, [check_if_disabled resource-dbus])
+AM_CONDITIONAL(DISABLED_PLUGIN_RESOURCE_WRT, [check_if_disabled resource-wrt])
+AM_CONDITIONAL(DISABLED_PLUGIN_DOMAIN_CONTROL,
+ [check_if_disabled domain-control])
+AM_CONDITIONAL(DISABLED_PLUGIN_SYSTEMD, [check_if_disabled systemd])
+
+AM_CONDITIONAL(BUILTIN_PLUGIN_TEST, [check_if_internal test])
+AM_CONDITIONAL(BUILTIN_PLUGIN_DBUS, [check_if_internal dbus])
+AM_CONDITIONAL(BUILTIN_PLUGIN_GLIB, [check_if_internal glib])
+AM_CONDITIONAL(BUILTIN_PLUGIN_CONSOLE, [check_if_internal console])
+AM_CONDITIONAL(BUILTIN_PLUGIN_RESOURCE_DBUS, [check_if_internal resource-dbus])
+AM_CONDITIONAL(BUILTIN_PLUGIN_RESOURCE_WRT, [check_if_internal resource-wrt])
+AM_CONDITIONAL(BUILTIN_PLUGIN_DOMAIN_CONTROL,
+ [check_if_internal domain-control])
+AM_CONDITIONAL(BUILTIN_PLUGIN_LUA, [check_if_internal lua])
+AM_CONDITIONAL(BUILTIN_PLUGIN_SYSTEMD, [check_if_internal systemd])
+
+# Check for Check (unit test framework).
+PKG_CHECK_MODULES(CHECK,
+ check >= 0.9.4,
+ [has_check="yes"], [has_check="no"])
+AM_CONDITIONAL(HAVE_CHECK, test "x$has_check" = "xyes")
+
+AC_SUBST(CHECK_CFLAGS)
+AC_SUBST(CHECK_LIBS)
+
+if test "x$has_check" = "xno"; then
+ AC_MSG_WARN([Check framework not found, unit tests are DISABLED.])
+fi
+
+# Check for documentation tools
+AC_ARG_WITH([documentation],
+ [AS_HELP_STRING([--with-documentation],
+ [generate pdf, html and other doc files])],
+ [],
+ [with_documentation=auto]
+)
+
+AS_IF( [ test x$with_documentation = xno ],
+ [ has_doc_tools="no" ],
+ [ AC_PATH_TOOL([MRP_DOXYGEN], doxygen)
+ AC_PATH_TOOL([MRP_LYX], lyx)
+ AC_PATH_TOOL([MRP_INKSCAPE], inkscape)
+ AC_PATH_TOOL([MRP_PYTHON], python)
+ AC_PATH_TOOL([MRP_TOUCH], touch)
+ AC_PATH_TOOL([MRP_DBLATEX], dblatex)
+ AC_PATH_TOOL([MRP_XMLTO], xmlto)
+
+ AS_IF( [ test x$MRP_DOXYGEN = x -o x$MRP_LYX = x -o \
+ x$MRP_INKSCAPE = x -o x$MRP_PYTHON = x -o \
+ x$MRP_TOUCH = x],
+ [ has_doc_tools="no";
+ AC_MSG_WARN([Some essential doc-tool is missing]) ],
+ [ has_doc_tools="yes";
+ MRP_DOCINIT() ]
+ ) ]
+)
+
+AS_IF( [ test x$has_doc_tools == "xno" -o x$MRP_DBLATEX = x ],
+ [ can_make_pdfs="no";
+ AC_WARN([No PDF documentation will be generated]) ],
+ [ can_make_pdfs="yes"]
+)
+
+AS_IF([ test x$has_doc_tools == "xno" -o x$MRP_XMLTO = x ],
+ [ can_make_html="no";
+ AC_WARN([No HTML documentation will be generated]) ],
+ [ can_make_html="yes" ]
+)
+
+
+AM_CONDITIONAL(BUILD_DOCUMENTATION, [ test x$has_doc_tools = "xyes" ])
+AM_CONDITIONAL(BUILD_PDF_DOCUMENTS, [ test x$can_make_pdfs = "xyes" ])
+AM_CONDITIONAL(BUILD_HTML_DOCUMENTS, [ test x$can_make_html = "xyes" ])
+
+AC_SUBST(MRP_DOCDIR, [`pwd`/doc])
+AC_SUBST(MRP_FIGDIR, [$MRP_DOCDIR/common/figures])
+AC_SUBST(MRP_MAKE_DOCRULES, [$MRP_DOCDIR/Makefile.rules])
+AC_SUBST(MRP_DOCSCRIPT_DIR, [$MRP_DOCDIR/scripts])
+AC_SUBST(MRP_ABNF, [$MRP_DOCSCRIPT_DIR/abnf.py])
+AC_SUBST(MRP_DBLYXFIX, [$MRP_DOCSCRIPT_DIR/dblyxfix.py])
+AC_SUBST(MRP_DOXML2DB, [$MRP_DOCSCRIPT_DIR/doxml2db.py])
+AC_SUBST(MRP_DOXYDEPS, [$MRP_DOCSCRIPT_DIR/doxydeps.py])
+
+
+# Shave by default.
+SHAVE_INIT([build-aux], [enable])
+
+# Create murphy symlink to match domain controller's
+# placing with how it is installed.
+if test ! -L murphy/domain-control; then
+ AC_MSG_NOTICE([Symlinking src/plugins/domain-control to src/domain-control...])
+ cd src
+ ln -s plugins/domain-control domain-control
+ cd ..
+fi
+
+# Create murphy symlink to src.
+if test ! -L murphy; then
+ AC_MSG_NOTICE([Symlinking src to murphy...])
+ ln -s src murphy
+fi
+
+# Generate output.
+AC_CONFIG_FILES([build-aux/shave
+ build-aux/shave-libtool
+ Makefile
+ utils/Makefile
+ src/Makefile
+ src/common/tests/Makefile
+ src/core/tests/Makefile
+ src/core/lua-decision/tests/Makefile
+ src/daemon/tests/Makefile
+ src/plugins/tests/Makefile
+ src/common/murphy-common.pc
+ src/common/murphy-libdbus.pc
+ src/common/murphy-dbus-libdbus.pc
+ src/common/murphy-dbus-sdbus.pc
+ src/common/murphy-pulse.pc
+ src/common/murphy-ecore.pc
+ src/common/murphy-glib.pc
+ src/common/murphy-qt.pc
+ src/core/murphy-core.pc
+ src/core/lua-utils/murphy-lua-utils.pc
+ src/core/lua-decision/murphy-lua-decision.pc
+ src/breedline/breedline.pc
+ src/breedline/breedline-murphy.pc
+ src/breedline/breedline-glib.pc
+ src/breedline/tests/Makefile
+ src/murphy-db/Makefile
+ src/murphy-db/mdb/Makefile
+ src/murphy-db/mqi/Makefile
+ src/murphy-db/mql/Makefile
+ src/murphy-db/include/Makefile
+ src/murphy-db/tests/Makefile
+ src/resolver/murphy-resolver.pc
+ src/resolver/tests/Makefile
+ src/plugins/domain-control/murphy-domain-controller.pc
+ doc/Makefile
+ doc/plugin-developer-guide/Makefile
+ doc/plugin-developer-guide/db/Makefile
+ doc/plugin-developer-guide/doxml/Makefile
+ src/plugins/resource-native/libmurphy-resource/murphy-resource.pc
+ ])
+AC_OUTPUT
+
+
+# Display the configuration.
+echo "----- configuration -----"
+echo "Extra C warnings flags: $WARNING_CFLAGS"
+echo "Cross-compiling: $cross_compiling"
+if test "$cross_compiling" != "no"; then
+ echo " * native compiler: $UNSHAVED_CC_FOR_BUILD"
+fi
+echo "Lua (pkgconfig file) to use: $with_lua"
+echo " * cflags: $LUA_CFLAGS"
+echo " * libs: $LUA_LIBS"
+echo "D-Bus (libdbus) support: $enable_libdbus"
+echo "D-Bus (systemd-bus) support: $enable_sdbus"
+echo "PulseAudio mainloop support: $enable_pulse"
+echo "EFL/ecore mainloop support: $enable_ecore"
+echo "glib mainloop support: $enable_glib"
+echo "Qt mainloop support: $enable_qt"
+echo "Murphy console plugin and client: $enable_console"
+echo "Resource management support: $with_resources"
+echo "Websockets support: $enable_websockets"
+echo "systemd support: $enable_systemd"
+echo "Plugins:"
+echo " - linked-in:"
+for plugin in ${INTERNAL_PLUGINS:-none}; do
+ echo " $plugin"
+done
+echo " - dynamic:"
+for plugin in ${EXTERNAL_PLUGINS:-none}; do
+ echo " $plugin"
+done
+echo " - disabled:"
+for plugin in ${DISABLED_PLUGINS:-none}; do
+ echo " $plugin"
+done
--- /dev/null
+
+1. General Style
+
+Indentation level is 4. The indentation character is space, regardless of
+the overall indentation level. IOW, even if nested indentation causes the
+overall indentation level to be greater or equal to 8 use spaces only.
+Don't leave trailing white space at the end of lines.
+
+Line length is 80 columns. You need to break up longer lines to multiple
+chunks at reasonable boundaries. What counts as a reasonable boundary
+depends on what you are breaking up. There is very seldom, if ever, a
+good enough reason to break the 80-column line rule.
+
+
+2. Layout and Tabulation
+
+2.1 Curly Braces
+
+Opening curly braces are put last on the line, while closing curly braces
+are on lines of their own. Generally this applies to all control-flow
+blocks, IOW to if, for, while, do, and switch statements. The most notable
+exceptions are the while in a do-while statement, if-else-if chains and
+functions.
+
+Some examples:
+
+ if (!condition) {
+ foo();
+ foobar();
+ }
+ else {
+ bar();
+ barfoo();
+ }
+
+ while (check) {
+ do_something();
+ and_something_else();
+ }
+
+Exceptions:
+
+ do {
+ process();
+ and_more();
+ } while (need_to);
+
+ if (foo < bar) {
+ foo();
+ bar();
+ } else if (bar < foo) {
+ foobar();
+ barfoo();
+ } else {
+ frobnicate();
+ xyzzy();
+ }
+
+In a function you put both the opening and the closing braces on lines
+of their own:
+
+ int foo(int x)
+ {
+ if (x < 10)
+ return foobar(x);
+ else
+ return barfoo(x);
+ }
+
+
+If you have a single statement in a block, you can omit the opening
+and closing braces. However in an if-else it is preferred to keep the
+braces unless both branches are single statements. Also, do not omit
+the braces for do-while statements.
+
+Examples:
+
+ if (x < 0)
+ foo(x);
+ else
+ bar(x);
+
+Note however, that we prefer to keep braces here:
+
+ if (y < 10) {
+ foo(y);
+ }
+ else {
+ foobar(y);
+ barfoo(x);
+ }
+
+ do {
+ x = xyzzy();
+ } while (x < 10);
+
+
+2.2 Indentation and Spaces
+
+Individual cases and the default case is indented to align with the
+switch statement itself:
+
+ switch (foo) {
+ case 1:
+ so_its_one();
+ break;
+ case 2:
+ uh_two();
+ break;
+ case 3:
+ oh_boy_three();
+ break;
+ default:
+ oh_no(foo);
+ }
+
+If you have a more complex set of statements in the cases, it is preferable
+to include an extra empty line for clarity before all but the first case and
+the default branch.
+
+ switch (x) {
+ case 1:
+ if (y < 10)
+ horribly(x);
+ else if (y < 20)
+ complex(x);
+ else if (x + y > 200)
+ processing(x, y);
+ break;
+
+ case 2:
+ ...
+ break;
+
+ ...
+ default:
+ ...
+ }
+
+The general rule for space usage is the following:
+
+ - use one space after control-flow keywords (if, for, while, do,
+ switch, case)
+ - use one space around binary and and ternary operators (=, +, -,
+ *, /, %, |, &, ^, <, >, <=, >=, ==, !=, ? :)
+ - don't use space after unary operators (&, *, +, -, ~, !)
+ - don't use space after the following keywords: sizeof, typeof,
+ alignof, __attribute__
+ - don't use spaces before postincrement/postdecrement or pre-
+ increment/predecrement operators (++, --)
+ - don't use spaces around structure or union member operators (., ->)
+ - don't use spaces after the preprocessor directive 'defined'
+
+
+Deviating from these basic preferences is okay if it helps increasing
+readibility, for instance by resulting in more compact code and hence
+more code per editor surface area. For example, one might choose an
+alternative, more compact layout for a switch statement that consists
+only of trivially simple case branches, like this:
+
+ switch (f->value.type) {
+ case SI_TYPE_INT16: f->value.i16 = va_arg(ap, int32_t); break;
+ case SI_TYPE_UINT16: f->value.u16 = va_arg(ap, uint32_t); break;
+ case SI_TYPE_INT32: f->value.i32 = va_arg(ap, int32_t); break;
+ case SI_TYPE_UINT32: f->value.u32 = va_arg(ap, uint32_t); break;
+ case SI_TYPE_INT64: f->value.i64 = va_arg(ap, int64_t); break;
+ case SI_TYPE_UINT64: f->value.u64 = va_arg(ap, uint64_t); break;
+ case SI_TYPE_BOOL: f->value.bln = va_arg(ap, int); break;
+ case SI_TYPE_DOUBLE: f->value.dbl = va_arg(ap, double); break;
+ default:
+ return FALSE;
+}
+
+
+2.3 Accepted Tabulation Schemes
+
+There are two tabulation schemes you can choose from:
+
+ 1) obsessive alignitis: the manic alignment of variable definitions,
+ structure and union member definitions, variable assignments within
+ a code block end so forth
+
+ 2) traditional K&R-ish reductionist style, minimizing the amount of
+ white space while adhering to the rules of the above sections
+
+But be consistent with yourself, do not try to mix these. Choose one
+and stick with it within a component. Also if you are touching any
+existing code obey the existing style.
+
+
+2.4 Code Layout
+
+Put licensing information to the beginning of every file.
+Within a module put includes and macro definitions in the beginning
+(of course). Then put type definitions, any necessary static function
+prototypes and finally module-local variables before the actual
+functions of the module.
+
+Within a function, define variables in the beginning of the function.
+You can also put variable definitions at the beginning of code blocks
+with curly braces, eg. within if, else, for, while, and switch-constructs.
+Please do not pull variables out of your arse^H^H^H^Hstack in the middle
+of a function at any other place.
+
+
+2.5 Error Handling
+
+There are two acceptable error-indication mechanism from functions:
+boolean style and libc style. It depends on your component which one makes
+sense. If upon failure it is of no use to the caller to get more
+information than the mere fact of failure use boolean style error
+indication returning true on success and false otherwise. In this case,
+include stdbool.h in your public header and use bool as the return type
+of your function to indicate the boolean convention to your readers. If
+you choose the libc-style, return 0 on success, return -1 on failure and
+also set errno to some reasonable error code in this case. Never ever
+clear errno on success. Naturally, functions that return objects should
+return NULL on failure with both of these styles.
+
+Whenever you have a non-trivial function with complex control-flow try
+to organise your code so that there is a single common error cleanup
+and return path. You can put this at the end of your function, label it
+as 'fail', and use goto's to jump from the middle of the function to the
+error-handling branch.
+
+
+2.6 Function Prototypes
+
+Use function prototypes with variable names in public header files.
+
+
+3. Naming Conventions
+
+The basic general naming rule is no CamelCase, use only lower-case
+letters, use underscore ('_') as a word separator within symbol names
+for functions and variables. Macros are all upper-case with the exception
+of function-like macros that take arguments. Just like functions, these
+can be all-lower case at will if this makes more sense. Typical cases
+when this makes sense would be a macro that wraps a function call and
+extends the argument list with additional parameters, or a guarding
+convenience wrapper macro with extra error checks for calling a function
+in some external component that does not handle some common error cases,
+such as being called with a NULL pointer on cleanup code paths.
+
+For Murphy core, all externally visible symbols must be prefixed with
+mrp_ to avoid name-space collisions with other components.
+
+3.1 Type Names
+
+Externally visible types (at least structs, unions and enums) must be
+typedef'd. The chosen type name must be prefixed consistently with the
+other publicly visible types from the same component. All typedef'd
+names are suffixed with _t. If you need to also have a non-typedef'd
+struct, or union for internal use for some reason, suffix it with _s
+or _u depending on its type.
+
+3.2 Function and Variable Names
+
+All externally visible functions and variables must be prefixed with
+a component-specific prefix-. For Murphy core this is mrp_. For libraries
+that can be easily re-used without Murphy, you can chose any prefix you
+like, but keep it short, preferably up to 3 or 4 characters max.
+
+For global variables always use fulle/long descriptive names, keeping in
+mind the lower-case only and underscore restrictions. For local variables
+and function arguments try to strike the right balance between compactness
+and descriptiveness. On one hand too long names hurt readability and waste
+a lot of precious real estate on our 80 character character terminals from
+the 70's. On the other hand too short or unintuitively shortened names
+fail to provide the right association about the usage of the variables for
+the reader. So this is not that black and white and it is not easy to give
+a set of strict rules that one could blindly follow. If someone familiar
+with the subject your code is addressing gets constantly confused with
+what your variables are for, you are probably overdoing the compactness
+part. If the same person gets constantly frustrated/tired reading your
+variable names, you are probably underdoing it...
+
+Library global function and variable names, ie. symbols whose visibility
+is limited to the scope of the library, should be prefixed with the module
+prefix within the library they are defined in.
+
+3.3 File Naming Conventions
+
+Don't prefix your file names with a component name. Use dashes instead
+of underscores in file and directory names.
+
+
+4. Miscallanea
+
+4.1 Use of Goto
+
+There are cases when the careful use of gotos make the code both easier
+to read/follow and less error-prone to modify/extend than with any of
+the alternatives. Typical examples are consolidated bailout/cleanup code
+on error paths from complex functions with nested blocks of if/for/while
+statements. Also it is often cleaner to use directly gotos instead of
+emulating them with an while-(0)-break non-loop construct.
+
+
+
+
+
+
+
+
+Generally speaking, it would be beneficial to come up with a basic set
+of rules that can also be expressed as a set of command line options to
+indent (or any other available alternative). That would provide a canonical
+way for people to check their code for indentation-conformance. Also we
+could use it ourselves in git hooks to warn about code with non-conformant
+style which otherwise might be difficult to avoid in the beginning.
+
--- /dev/null
+MANDEFS = plugin-developer-guide.x
+MANUALS = $(MANDEFS:.x=)
+TARGETS =
+DOCFILES =
+
+vpath %.xml plugin-developer-guide/db
+
+include $(MRP_MAKE_DOCRULES)
+
+
+if BUILD_DOCUMENTATION
+DOCDIRS = plugin-developer-guide
+endif
+
+if BUILD_PDF_DOCUMENTS
+TARGETS += pdf_targets
+DOCFILES += $(MANDEFS:.x=.pdf)
+endif
+
+
+SUBDIRS = $(DOCDIRS)
+
+doc_DATA = CODING-STYLE # $(DOCFILES)
+
+
+all-am: $(TARGETS)
+
+pdf_targets: plugin-developer-guide.pdf
+
+plugin-developer-guide.pdf: plugin-developer-guide.xml
+
+clean-local:
+ rm -f *.pdf scripts/*~
+
--- /dev/null
+.SUFFIXES: .svg .pdf .png .lyx .xml
+
+vpath %.svg $(MRP_FIGDIR)
+
+.svg:
+ echo " CP $@" 1>&2
+
+.svg.pdf:
+ echo " INKSC $@" 1>&2
+ $(MRP_INKSCAPE) --export-area-drawing --export-pdf=$@ $< \
+ > /dev/null 2>&1
+
+.svg.png:
+ echo " INKSC $@" 1>&2
+ $(MRP_INKSCAPE) --export-area-drawing --export-png=$@ $< \
+ > /dev/null 2>&1
+
+.lyx.xml:
+ $(MRP_LYX) --export docbook-xml $< 2> /tmp/dblyx.log
+ lyx_file=$< ; lyxml_file=$${lyx_file/.lyx/.xml} ; \
+ if [ -f "$$lyxml_file" ] ; then \
+ $(MRP_DBLYXFIX) $$lyxml_file $@ && rm -f $$lyxml_file ; \
+ else \
+ cat /tmp/dblyx.log ; \
+ fi ; \
+ rm -f /tmp/dblyx.log
+
+.xml.pdf:
+ echo " DBPDF $@" 1>&2
+ rm -f $@
+ $(MRP_DBLATEX) --pdf -P figure.title.top=0 -P doc.section.depth=2 \
+ -o $@ $< 2> /tmp/dblatex.log 1>&2
+ [ -f "$@" ] || cat /tmp/dblatex.log 1>&2
+ rm -f /tmp/dblatex.log
+
+$(DEPDIR)/Doxyfile.P: Doxyfile
+ $(MRP_DOXYDEPS) doxml_files $< $(DEPDIR)
+
+doxml_files: $(DEPDIR)/Doxyfile.P
+ echo " DOXYG $@" 1>&2
+ $(MRP_DOXYGEN) Doxyfile
+ $(MRP_TOUCH) doxml_files
+
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:osb="http://www.openswatchbook.org/uri/2009/osb"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="297mm"
+ height="210mm"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.1 r9760"
+ sodipodi:docname="db-layers.svg">
+ <defs
+ id="defs2987">
+ <linearGradient
+ id="linearGradient3783"
+ osb:paint="solid">
+ <stop
+ style="stop-color:#000000;stop-opacity:1;"
+ offset="0"
+ id="stop3785" />
+ </linearGradient>
+ <filter
+ id="filter3857"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3859"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3861"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset3863"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3865">
+ <feMergeNode
+ id="feMergeNode3867"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3869"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter3871"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3873"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3875"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset3877"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3879">
+ <feMergeNode
+ id="feMergeNode3881"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3883"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter3885"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3887"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3889"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset3891"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3893">
+ <feMergeNode
+ id="feMergeNode3895"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3897"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter3899"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3901"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3903"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset3905"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3907">
+ <feMergeNode
+ id="feMergeNode3909"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3911"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter3913"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3915"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3917"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset3919"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3921">
+ <feMergeNode
+ id="feMergeNode3923"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3925"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter3985"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur3987"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix3989"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset3991"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge3993">
+ <feMergeNode
+ id="feMergeNode3995"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode3997"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter4081"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur4083"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix4085"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset4087"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge4089">
+ <feMergeNode
+ id="feMergeNode4091"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode4093"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ color-interpolation-filters="sRGB"
+ id="filter4081-7"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-0.25"
+ y="-0.25">
+ <feGaussianBlur
+ id="feGaussianBlur4083-6"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix4085-9"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset4087-4"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge4089-0">
+ <feMergeNode
+ id="feMergeNode4091-9"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode4093-5"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter4253"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur4255"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix4257"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset4259"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge4261">
+ <feMergeNode
+ id="feMergeNode4263"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode4265"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ <filter
+ id="filter4267"
+ inkscape:label="Drop shadow"
+ width="1.5"
+ height="1.5"
+ x="-.25"
+ y="-.25">
+ <feGaussianBlur
+ id="feGaussianBlur4269"
+ in="SourceAlpha"
+ stdDeviation="5.7"
+ result="blur" />
+ <feColorMatrix
+ id="feColorMatrix4271"
+ result="bluralpha"
+ type="matrix"
+ values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0.493 0 " />
+ <feOffset
+ id="feOffset4273"
+ in="bluralpha"
+ dx="11.6"
+ dy="10.5"
+ result="offsetBlur" />
+ <feMerge
+ id="feMerge4275">
+ <feMergeNode
+ id="feMergeNode4277"
+ in="offsetBlur" />
+ <feMergeNode
+ id="feMergeNode4279"
+ in="SourceGraphic" />
+ </feMerge>
+ </filter>
+ </defs>
+ <sodipodi:namedview
+ inkscape:document-units="mm"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="676.99722"
+ inkscape:cy="373.08197"
+ inkscape:current-layer="layer1"
+ id="namedview2989"
+ showgrid="false"
+ inkscape:snap-grids="true"
+ inkscape:window-width="1511"
+ inkscape:window-height="1051"
+ inkscape:window-x="249"
+ inkscape:window-y="49"
+ inkscape:window-maximized="0"
+ showguides="true"
+ inkscape:guide-bbox="true">
+ <inkscape:grid
+ type="xygrid"
+ id="grid3999" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata2991">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <path
+ style="fill:#a18689;fill-opacity:1;stroke:none;filter:url(#filter3985)"
+ d="m 193,698.09448 1,-371 310,0 0,-128 344,0 -2,500 z"
+ id="path3983"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc"
+ inkscape:label="#mdb-path" />
+ <rect
+ style="fill:#a02c2c;fill-opacity:1;stroke:none;filter:url(#filter3913)"
+ id="rect3001"
+ width="602.41913"
+ height="116"
+ x="220.72061"
+ y="356.09448"
+ ry="0.5"
+ inkscape:label="#mql-rect" />
+ <rect
+ style="fill:#a05a2c;fill-opacity:1;stroke:none;filter:url(#filter3899)"
+ id="rect3797"
+ width="286.07703"
+ height="110"
+ x="220"
+ y="499.09448"
+ ry="0.5" />
+ <rect
+ style="fill:#a05a2c;fill-opacity:1;stroke:none;filter:url(#filter3885)"
+ id="rect3797-5"
+ width="290.4006"
+ height="110"
+ x="533.09937"
+ y="500.09448"
+ ry="0.5" />
+ <rect
+ style="fill:#c8ab37;fill-opacity:1;stroke:none;filter:url(#filter3871)"
+ id="rect3797-5-8"
+ width="290.4006"
+ height="110"
+ x="531.6582"
+ y="222.09448"
+ ry="0.5" />
+ <path
+ style="fill:#d0b69d;fill-opacity:1;stroke:none;filter:url(#filter3857)"
+ d="m 172.78059,290.09448 282.57583,-1 -0.7206,-129 390.50156,0 -1.3394,-134 -671.01739,0 z"
+ id="path3835"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccccc"
+ transform="matrix(0.97088079,0,0,1,26.971249,10)" />
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4081);font-family:Sans"
+ x="429"
+ y="665.09448"
+ id="text4077"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4079"
+ x="429"
+ y="665.09448"
+ style="font-size:32px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Bold">Murphy-DB</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter4081-7);font-family:Sans"
+ x="399.42969"
+ y="117.92261"
+ id="text4077-2"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4079-4"
+ x="399.42969"
+ y="117.92261"
+ style="font-size:32px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;font-family:Sans;-inkscape-font-specification:Sans Bold">Murphy Plugin</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="676.5293"
+ y="266.09448"
+ id="text4137"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4139"
+ x="676.5293"
+ y="266.09448"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'">MQL</tspan><tspan
+ sodipodi:role="line"
+ x="676.5293"
+ y="296.09448"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4141">Murphy Query Language</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="529.13477"
+ y="403.63159"
+ id="text4137-2"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4139-2"
+ x="529.13477"
+ y="403.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'">MQI</tspan><tspan
+ sodipodi:role="line"
+ x="529.13477"
+ y="433.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4141-2">Murphy Query Interface</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="365.13477"
+ y="500.63159"
+ id="text4137-2-8"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4139-2-7"
+ x="365.13477"
+ y="500.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'" /><tspan
+ sodipodi:role="line"
+ x="365.13477"
+ y="530.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4195">Temporary Data Backend</tspan><tspan
+ sodipodi:role="line"
+ x="365.13477"
+ y="560.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4201">MDE</tspan><tspan
+ sodipodi:role="line"
+ x="365.13477"
+ y="590.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4141-2-4">Memory Database Engine</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="678.13477"
+ y="502.83667"
+ id="text4137-2-8-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4139-2-7-1"
+ x="678.13477"
+ y="502.83667"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'" /><tspan
+ sodipodi:role="line"
+ x="678.13477"
+ y="532.83667"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4195-5">Persistent Data Backend</tspan><tspan
+ sodipodi:role="line"
+ x="678.13477"
+ y="562.83667"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4201-0" /><tspan
+ sodipodi:role="line"
+ x="678.13477"
+ y="592.83667"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4141-2-4-8">SQLite</tspan></text>
+ <path
+ style="fill:#c8ab37;fill-opacity:1;stroke:none;filter:url(#filter4267)"
+ d="m 867,184.09448 30,-49 -1,20 141,-1 0,61 -141,0 0,19 z"
+ id="path4233"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc" />
+ <path
+ style="fill:#a02c2c;fill-opacity:1;stroke:#7b5555;stroke-opacity:1;filter:url(#filter4253)"
+ d="m 177,314.09448 -30,-49 1,20 -141,-1 0,61 141,0 0,19 z"
+ id="path4233-0"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc" />
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="965.13477"
+ y="177.63159"
+ id="text4137-8"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="965.13477"
+ y="177.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4141-24">High level</tspan><tspan
+ sodipodi:role="line"
+ x="965.13477"
+ y="207.63159"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4307">API</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Sans"
+ x="80.035217"
+ y="307.42651"
+ id="text4137-8-0"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="80.035217"
+ y="307.42651"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4141-24-3">Low level</tspan><tspan
+ sodipodi:role="line"
+ x="80.035217"
+ y="337.42651"
+ style="font-size:24px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:center;line-height:125%;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;font-family:Times New Roman;-inkscape-font-specification:'Times New Roman,'"
+ id="tspan4307-8">API</tspan></text>
+ </g>
+</svg>
--- /dev/null
+SUBDIRS = doxml db
+
+clean-local:
+ rm -f *~
+
+
--- /dev/null
+TOP_SRCDIR = $(abs_top_srcdir)/src
+
+vpath %.lyx $(MRP_DOCDIR)/plugin-developer-guide/lyx
+vpath %.xml $(MRP_DOCDIR)/plugin-developer-guide/doxml
+
+include $(MRP_MAKE_DOCRULES)
+
+
+FIGURES_SVG = db-layers.svg
+FIGURES_PNG = $(FIGURES_SVG:.svg=.png)
+FIGURES_PDF = $(FIGURES_SVG:.svg=.pdf)
+
+FIGURES = $(FIGURES_SVG) $(FIGURES_PNG) $(FIGURES_PDF)
+
+
+high_level_api_definition_SRC = mql.h
+high_level_api_definition_DOXML = $(high_level_api_definition_SRC:.h=_8h.xml)
+
+
+TARGETS = $(FIGURES) plugin-developer-guide.xml
+
+
+xmldir = $(datadir)/doc/@PACKAGE@
+nodist_xml_DATA = $(TARGETS)
+
+
+all-am: $(TARGETS) copy_svg
+
+
+copy_svg:
+ for f in $(FIGURES_SVG) ; do \
+ echo " CP $$f" ; \
+ cp $(MRP_FIGDIR)/$$f . ; \
+ done 1>&2
+
+mql-grammar.xml: $(TOP_SRCDIR)/murphy-db/mql/mql-scanner.l \
+ $(TOP_SRCDIR)/murphy-db/mql/mql-parser.y
+ $(MRP_ABNF) $+ > $@
+
+high-level-api-definition.xml: $(high_level_api_definition_DOXML)
+ $(MRP_DOXML2DB) ../doxml $^ $@
+
+murphy-db-introduction.xml: murphy-db-introduction.lyx
+
+murphy-db-high-level-api.xml: murphy-db-high-level-api.lyx \
+ high-level-api-definition.xml
+
+murphy-db-query-language.xml: murphy-db-query-language.lyx \
+ mql-grammar.xml
+
+
+plugin-developer-guide.xml: plugin-developer-guide.lyx \
+ murphy-db-introduction.xml \
+ murphy-db-high-level-api.xml \
+ murphy-db-query-language.xml
+
+clean-local:
+ rm -f *~ $(TARGETS) \
+ murphy-db-introduction.xml \
+ murphy-db-high-level-api.xml high-level-api-definition.xml \
+ murphy-db-query-language.xml mql-grammar.xml \
+ ../lyx/*.xml ../lyx/*~
--- /dev/null
+# Doxyfile 1.7.4
+
+# 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 a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME =
+
+# 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 =
+
+# 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 =
+
+# 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 =
+
+# 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.
+
+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 =
+
+# 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 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 = NO
+
+# 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 = NO
+
+# 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 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
+
+#---------------------------------------------------------------------------
+# 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_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 = NO
+
+# 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 = YES
+
+# 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 = YES
+
+# 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 sectionname ... \endif.
+
+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 = NO
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = NO
+
+# 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 = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. The 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 =
+
+#---------------------------------------------------------------------------
+# 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 = YES
+
+# 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 = NO
+
+# 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 = ../../../src/murphy-db/include/murphy-db
+
+# 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 = *.h
+
+# 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 = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# 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.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = YES
+
+# 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 =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty or if
+# non of the patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any)
+# and it is also possible to disable source filtering for a specific pattern
+# using *.ext= (so without naming a filter). This option only has effect when
+# FILTER_SOURCE_FILES is enabled.
+
+FILTER_SOURCE_PATTERNS =
+
+#---------------------------------------------------------------------------
+# 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 = YES
+
+# 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 and C++ 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 = NO
+
+# 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 = NO
+
+# 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 adviced 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 =
+
+# If the HTML_TIMESTAMP tag is set to YES then the generated HTML documentation will contain the timesstamp.
+
+HTML_TIMESTAMP = NO
+
+# 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 the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_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 stylesheet 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 = YES
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# 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. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated
+# that can be used as input for Qt's qhelpgenerator to generate a
+# Qt Compressed Help (.qch) of the generated HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to
+# add. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">
+# Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">
+# Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files
+# will be generated, which together with the HTML files, form an Eclipse help
+# plugin. To install this plugin and make it available under the help contents
+# menu in Eclipse, the contents of the directory containing the HTML and XML
+# files needs to be copied into the plugins directory of eclipse. The name of
+# the directory within the plugins directory should be the same as
+# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before
+# the help appears.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have
+# this name.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = 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
+
+# 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.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# 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 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 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.org site, so you can quickly see the result without installing
+# MathJax, but it is strongly recommended to install a local copy of MathJax
+# before deployment.
+
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+
+# 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 PHP enabled web server instead of at the web client
+# using Javascript. Doxygen will generate the search PHP script and index
+# file to put on the web server. The advantage of the server
+# based approach is that it scales better to large projects and allows
+# full text search. The disadvantages are that it is more difficult to setup
+# and does not have live searching capabilities.
+
+SERVER_BASED_SEARCH = NO
+
+#---------------------------------------------------------------------------
+# 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 = NO
+
+# 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
+
+#---------------------------------------------------------------------------
+# 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 stylesheet 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 = YES
+
+# 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 = .
+
+# 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.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. 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. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# 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 = NO
+
+# 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 write a font called Helvetica to the output
+# directory and reference it in 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 output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+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
+# 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 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, SHOW_DIRECTORIES 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.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the
+# \mscfile command).
+
+MSCFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
--- /dev/null
+SOURCES= mql.h
+
+include $(MRP_MAKE_DOCRULES)
+
+
+all-am: doxml_files
+
+clean-local:
+ rm -f *~ *.doxml *.xml *.xsd *.xslt .deps/Doxyfile.P doxml_files
+
+
+-include .deps/Doxyfile.P
--- /dev/null
+#LyX 2.0 created this file. For more info see http://www.lyx.org/
+\lyxformat 413
+\begin_document
+\begin_header
+\textclass docbook-chapter
+\use_default_options true
+\maintain_unincluded_children false
+\language english
+\language_package default
+\inputencoding auto
+\fontencoding global
+\font_roman default
+\font_sans default
+\font_typewriter default
+\font_default_family default
+\use_non_tex_fonts false
+\font_sc false
+\font_osf false
+\font_sf_scale 100
+\font_tt_scale 100
+
+\graphics default
+\default_output_format default
+\output_sync 0
+\bibtex_command default
+\index_command default
+\paperfontsize default
+\spacing single
+\use_hyperref true
+\pdf_bookmarks true
+\pdf_bookmarksnumbered false
+\pdf_bookmarksopen false
+\pdf_bookmarksopenlevel 1
+\pdf_breaklinks false
+\pdf_pdfborder false
+\pdf_colorlinks true
+\pdf_backref section
+\pdf_pdfusetitle true
+\papersize default
+\use_geometry false
+\use_amsmath 1
+\use_esint 1
+\use_mhchem 1
+\use_mathdots 1
+\cite_engine basic
+\use_bibtopic false
+\use_indices false
+\paperorientation portrait
+\suppress_date false
+\use_refstyle 1
+\index Index
+\shortcut idx
+\color #008000
+\end_index
+\secnumdepth 2
+\tocdepth 3
+\paragraph_separation skip
+\defskip smallskip
+\quotes_language english
+\papercolumns 1
+\papersides 1
+\paperpagestyle default
+\bullet 1 0 9 -1
+\bullet 2 0 15 -1
+\bullet 3 0 7 -1
+\tracking_changes false
+\output_changes false
+\html_math_output 0
+\html_css_as_file 0
+\html_be_strict false
+\end_header
+
+\begin_body
+
+\begin_layout Title
+High level API
+\end_layout
+
+\begin_layout Standard
+blah blah
+\end_layout
+
+\begin_layout Standard
+\begin_inset CommandInset include
+LatexCommand input
+filename "high-level-api-definition.xml"
+
+\end_inset
+
+
+\end_layout
+
+\end_body
+\end_document
--- /dev/null
+#LyX 2.0 created this file. For more info see http://www.lyx.org/
+\lyxformat 413
+\begin_document
+\begin_header
+\textclass docbook-chapter
+\use_default_options true
+\maintain_unincluded_children false
+\language english
+\language_package default
+\inputencoding auto
+\fontencoding global
+\font_roman default
+\font_sans default
+\font_typewriter default
+\font_default_family default
+\use_non_tex_fonts false
+\font_sc false
+\font_osf false
+\font_sf_scale 100
+\font_tt_scale 100
+
+\graphics default
+\default_output_format default
+\output_sync 0
+\bibtex_command default
+\index_command default
+\paperfontsize default
+\spacing single
+\use_hyperref true
+\pdf_bookmarks true
+\pdf_bookmarksnumbered false
+\pdf_bookmarksopen false
+\pdf_bookmarksopenlevel 1
+\pdf_breaklinks false
+\pdf_pdfborder false
+\pdf_colorlinks true
+\pdf_backref section
+\pdf_pdfusetitle true
+\papersize default
+\use_geometry false
+\use_amsmath 1
+\use_esint 1
+\use_mhchem 1
+\use_mathdots 1
+\cite_engine basic
+\use_bibtopic false
+\use_indices false
+\paperorientation portrait
+\suppress_date false
+\use_refstyle 1
+\index Index
+\shortcut idx
+\color #008000
+\end_index
+\secnumdepth 2
+\tocdepth 3
+\paragraph_separation skip
+\defskip smallskip
+\quotes_language english
+\papercolumns 1
+\papersides 1
+\paperpagestyle default
+\bullet 1 0 9 -1
+\bullet 2 0 15 -1
+\bullet 3 0 7 -1
+\tracking_changes false
+\output_changes false
+\html_math_output 0
+\html_css_as_file 0
+\html_be_strict false
+\end_header
+
+\begin_body
+
+\begin_layout Title
+Introduction
+\end_layout
+
+\begin_layout Standard
+blah blah
+\end_layout
+
+\begin_layout Standard
+\begin_inset Float figure
+wide false
+sideways false
+status open
+
+\begin_layout Plain Layout
+\begin_inset Caption
+
+\begin_layout Plain Layout
+Murphy DB components
+\end_layout
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Plain Layout
+\begin_inset Graphics
+ filename ../../common/figures/db-layers.svg
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Plain Layout
+
+\end_layout
+
+\end_inset
+
+
+\end_layout
+
+\end_body
+\end_document
--- /dev/null
+#LyX 2.0 created this file. For more info see http://www.lyx.org/
+\lyxformat 413
+\begin_document
+\begin_header
+\textclass docbook-chapter
+\use_default_options true
+\maintain_unincluded_children false
+\language english
+\language_package default
+\inputencoding auto
+\fontencoding global
+\font_roman default
+\font_sans default
+\font_typewriter default
+\font_default_family default
+\use_non_tex_fonts false
+\font_sc false
+\font_osf false
+\font_sf_scale 100
+\font_tt_scale 100
+
+\graphics default
+\default_output_format default
+\output_sync 0
+\bibtex_command default
+\index_command default
+\paperfontsize default
+\spacing single
+\use_hyperref true
+\pdf_bookmarks true
+\pdf_bookmarksnumbered false
+\pdf_bookmarksopen false
+\pdf_bookmarksopenlevel 1
+\pdf_breaklinks false
+\pdf_pdfborder false
+\pdf_colorlinks true
+\pdf_backref section
+\pdf_pdfusetitle true
+\papersize default
+\use_geometry false
+\use_amsmath 1
+\use_esint 1
+\use_mhchem 1
+\use_mathdots 1
+\cite_engine basic
+\use_bibtopic false
+\use_indices false
+\paperorientation portrait
+\suppress_date false
+\use_refstyle 1
+\index Index
+\shortcut idx
+\color #008000
+\end_index
+\secnumdepth 2
+\tocdepth 3
+\paragraph_separation skip
+\defskip smallskip
+\quotes_language english
+\papercolumns 1
+\papersides 1
+\paperpagestyle default
+\bullet 1 0 9 -1
+\bullet 2 0 15 -1
+\bullet 3 0 7 -1
+\tracking_changes false
+\output_changes false
+\html_math_output 0
+\html_css_as_file 0
+\html_be_strict false
+\end_header
+
+\begin_body
+
+\begin_layout Title
+Murphy Query Language
+\end_layout
+
+\begin_layout Section
+Overview
+\end_layout
+
+\begin_layout Standard
+blah blah
+\end_layout
+
+\begin_layout Section
+Grammar
+\end_layout
+
+\begin_layout Standard
+\begin_inset CommandInset include
+LatexCommand input
+filename "mql-grammar.xml"
+
+\end_inset
+
+
+\end_layout
+
+\end_body
+\end_document
--- /dev/null
+#LyX 2.0 created this file. For more info see http://www.lyx.org/
+\lyxformat 413
+\begin_document
+\begin_header
+\textclass docbook-book
+\use_default_options true
+\maintain_unincluded_children false
+\language english
+\language_package default
+\inputencoding auto
+\fontencoding global
+\font_roman default
+\font_sans default
+\font_typewriter default
+\font_default_family default
+\use_non_tex_fonts false
+\font_sc false
+\font_osf false
+\font_sf_scale 100
+\font_tt_scale 100
+
+\graphics default
+\default_output_format default
+\output_sync 0
+\bibtex_command default
+\index_command default
+\paperfontsize default
+\spacing single
+\use_hyperref true
+\pdf_bookmarks true
+\pdf_bookmarksnumbered false
+\pdf_bookmarksopen false
+\pdf_bookmarksopenlevel 1
+\pdf_breaklinks false
+\pdf_pdfborder false
+\pdf_colorlinks true
+\pdf_backref section
+\pdf_pdfusetitle true
+\papersize default
+\use_geometry false
+\use_amsmath 1
+\use_esint 1
+\use_mhchem 1
+\use_mathdots 1
+\cite_engine basic
+\use_bibtopic false
+\use_indices false
+\paperorientation portrait
+\suppress_date false
+\use_refstyle 1
+\index Index
+\shortcut idx
+\color #008000
+\end_index
+\secnumdepth 2
+\tocdepth 3
+\paragraph_separation skip
+\defskip smallskip
+\quotes_language english
+\papercolumns 1
+\papersides 1
+\paperpagestyle default
+\bullet 1 0 9 -1
+\bullet 2 0 15 -1
+\bullet 3 0 7 -1
+\tracking_changes false
+\output_changes false
+\html_math_output 0
+\html_css_as_file 0
+\html_be_strict false
+\end_header
+
+\begin_body
+
+\begin_layout Title
+Plugin Developer Guide
+\end_layout
+
+\begin_layout Part
+Introduction
+\end_layout
+
+\begin_layout Chapter
+Murphy in Brief
+\end_layout
+
+\begin_layout Standard
+blah blah bla ....
+\end_layout
+
+\begin_layout Description
+This will be the first among description thing
+\end_layout
+
+\begin_layout Description
+here we have the second
+\end_layout
+
+\begin_layout Description
+and the third
+\end_layout
+
+\begin_layout Part
+Basic Infrastructure
+\end_layout
+
+\begin_layout Part
+Murphy Database
+\end_layout
+
+\begin_layout Standard
+\begin_inset CommandInset include
+LatexCommand input
+filename "murphy-db-introduction.xml"
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+\begin_inset CommandInset include
+LatexCommand input
+filename "murphy-db-high-level-api.xml"
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Standard
+\begin_inset CommandInset include
+LatexCommand input
+filename "murphy-db-query-language.xml"
+
+\end_inset
+
+
+\end_layout
+
+\begin_layout Part
+Plugin writing conventions
+\end_layout
+
+\end_body
+\end_document
--- /dev/null
+D-Bus API for Murphy resource handling
+======================================
+
+
+Service org.murphy
+
+Interface org.murphy.manager
+Path /org/murphy/resource
+
+methods:
+
+ dict getProperties()
+ ObjectPath createResourceSet()
+
+signals:
+
+ propertyChanged(String, Variant)
+
+properties:
+
+ RO [ObjectPath] resourceSets: default []
+ RO [String] availableClasses
+
+
+
+Interface org.murphy.resourceset
+Path varies (from createResourceSet)
+
+methods:
+
+ dict getProperties()
+ void setProperty(String, Variant)
+ ObjectPath addResource(String)
+ void request()
+ void release()
+ void delete()
+
+signals:
+
+ propertyChanged(String, Variant)
+ updatedResources([ObjectPath]) # not yet implemented
+
+properties:
+
+ RW String class: default "default"
+ RO String status: default "pending"
+ RO [String] availableResources
+ RO [ObjectPath] resources: default []
+
+
+
+Interface org.murphy.resource
+Path varies (from addResource)
+
+methods:
+
+ dict getProperties()
+ void setProperty(String, Variant)
+ void delete()
+
+signals:
+
+ propertyChanged(String, Variant)
+
+properties:
+
+ RO String status: default "pending"
+ RO String name
+ RW Boolean mandatory
+ RW Boolean shared
+
+ RO dict attributes (string: variant) {
+ ... properties specified by resource type ...
+ }
+
+ RW dict conf (string: variant) {
+ ... properties specified by resource type ...
+ }
+
+
+
+
+Explanation of values
+=====================
+
+
+The "status" variable on resource set objects can have four different values:
+
+ * "pending", meaning that the request() method call hasn't been called or
+ processed yet
+ * "acquired", meaning that the client is allowed to use the requested
+ resource
+ * "lost", meaning that the client has lost the resource set and is not
+ allowed to use it
+ * "available", meaning that the client is not allowed to use the resource
+ set, but based on the current status the client would get the resource
+ set if it did a request() method call
+
+The difference between "lost" and "available" is subtle. One way to
+think of the difference is this: when a media player resource set having
+an audio resource goes to "lost" state, the media player can dim or gray
+out the play button, since the media player is unable to get access to
+the resources under any circumstances. This can be the case for instance
+during a phone call, depending on murphy configuration. However, if the
+media player resource set is in "available" state, the audio playback
+can continue when the user or the application wishes so. This can be the
+case when another media player application is playing audio. The user
+can control which media player will play audio by pushing play button on
+the UI of the application that the user wants to play.
+
+The "status" variable on resource objects can have three values:
+"pending", "acquired" and "lost". The "available" value is not needed,
+since the resources cannot be indiviually requested, only resource sets.
+
--- /dev/null
+#!/usr/bin/env python
+
+# Copyright (c) 2012, Intel Corporation
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# * Neither the name of Intel Corporation nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+# This script is an example on how to use the Murphy D-Bus resource API.
+
+
+from __future__ import print_function
+
+import dbus
+import gobject
+import glib
+import sys
+import fcntl
+import os
+from dbus.mainloop.glib import DBusGMainLoop
+
+from itertools import combinations
+from random import choice
+
+USAGE = """
+Available commands:
+
+help
+
+createSet
+
+deleteSet <set>
+changeSetClass <set> <class>
+acquireSet <set>
+releaseSet <set>
+showSet <set>
+
+createResource <set> <type>
+
+deleteResource <set> <resource>
+changeResource <set> <resource> <attribute> <value>
+showResource <set> <resource>
+
+quit"""
+
+manager = None
+bus = None
+mainloop = None
+interactive = None
+n_iterations = None
+limit = None
+
+# mapping from numbers to object paths
+rsets = {}
+resources = {}
+
+
+# pretty printing of D-Bus properties
+
+def pretty_str_dbus_value(val, level=0, suppress=False):
+ if type(val) == dbus.Array:
+ return pretty_str_dbus_array(val, level)
+ elif type(val) == dbus.Dictionary:
+ return pretty_str_dbus_dict(val, level)
+ else:
+ s = ""
+ if not suppress:
+ s += level * "\t"
+ if type(val) == dbus.Boolean:
+ if val:
+ s += "True"
+ else:
+ s += "False"
+ else:
+ s += str(val)
+ return s
+
+
+def pretty_str_dbus_array(arr, level=0):
+ prefix = level * "\t"
+ s = "[\n"
+ for v in arr:
+ s += pretty_str_dbus_value(v, level+1)
+ s += "\n"
+ s += prefix + "]"
+ return s
+
+
+def pretty_str_dbus_dict(d, level=0):
+ prefix = level * "\t"
+ s = "{\n"
+ for k, v in d.items():
+ s += prefix + "\t"
+ s += str(k) + ": "
+ s += pretty_str_dbus_value(v, level+1, True)
+ s += "\n"
+ s += prefix + "}"
+ return s
+
+
+# methods for getting a D-Bus object from path
+
+def get_rset(path):
+ set_obj = bus.get_object('org.Murphy', path)
+ return dbus.Interface(set_obj, dbus_interface='org.murphy.resourceset')
+
+
+def get_res(path):
+ resource_obj = bus.get_object('org.Murphy', path)
+ return dbus.Interface(resource_obj, dbus_interface='org.murphy.resource')
+
+
+# prompt handling
+
+prompt_needed = True
+
+def add_prompt():
+ global prompt_needed
+ global interactive
+
+ if prompt_needed and interactive:
+ print("")
+ print("> ", end="")
+ sys.stdout.flush()
+ prompt_needed = False
+
+
+def add_prompt_later():
+ global prompt_needed
+ global interactive
+
+ if not interactive:
+ return
+
+ prompt_needed = True
+
+ # add in idle loop so that we first process all queued signals
+ glib.idle_add(add_prompt)
+
+
+# signal handlers
+
+def resource_handler(prop, value, path):
+
+ res = int(path.split("/")[-1]) # the resource number
+ set = int(path.split("/")[-2]) # the set number
+
+ print("(%d/%d) property %s -> %s" % (set, res, str(prop), pretty_str_dbus_value(value)))
+ add_prompt_later()
+
+
+def rset_handler(prop, value, path):
+
+ set = int(path.split("/")[-1]) # the set number
+
+ print("(%d) property %s -> %s" % (set, str(prop), pretty_str_dbus_value(value)))
+ add_prompt_later()
+
+
+def mgr_handler(prop, value, path):
+
+ print("(manager) property %s -> %s" % (str(prop), pretty_str_dbus_value(value)))
+ add_prompt_later()
+
+
+# helper functions
+
+def get_set_path(setId):
+ try:
+ return rsets[setId]
+ except:
+ return None
+
+
+def get_res_path(setId, resId):
+ try:
+ return resources[(setId, resId)]
+ except:
+ return None
+
+
+def get_resource_set(set):
+ try:
+ id = int(set)
+ except ValueError:
+ print("ERROR: wrong resource set id type")
+ return None
+
+ set_path = get_set_path(int(set))
+
+ if set_path:
+ return get_rset(set_path)
+ else:
+ print("ERROR: resource set doesn't exist")
+ return None
+
+
+def get_resource(set, res):
+ try:
+ id_s = int(set)
+ id_r = int(res)
+ except ValueERROR:
+ print("ERROR: wrong resource or resource set id type")
+ return None
+
+ res_path = get_res_path(int(set), int(res))
+
+ if res_path:
+ return get_res(res_path)
+ else:
+ print("ERROR: resource doesn't exist")
+ return None
+
+
+# UI functions
+
+def help():
+ print(USAGE)
+
+
+def createSet():
+ try:
+ set_path = manager.createResourceSet()
+ id = int(set_path.split("/")[-1]) # the set number
+ rsets[id] = set_path
+ rset = get_rset(set_path)
+ if interactive:
+ rset.connect_to_signal("propertyChanged", rset_handler, path_keyword='path')
+ print("(%s) Created resource set" % str(id))
+ return id
+ except:
+ print("ERROR: failed to create a new resource set")
+
+
+def deleteSet(set):
+
+ rset = get_resource_set(set)
+
+ if rset:
+ try:
+ rset.delete()
+ del rsets[int(set)]
+ print("(%s) Deleted resource set" % set)
+ except:
+ print("ERROR (%s): failed to delete resource set" % set)
+
+
+def createResource(set, rType):
+ rset = get_resource_set(set)
+
+ if rset:
+ try:
+ res_path = rset.addResource(rType)
+ res = int(res_path.split("/")[-1]) # the resource id number
+ resources[(int(set), res)] = res_path
+
+ resource = get_res(res_path)
+ if interactive:
+ resource.connect_to_signal("propertyChanged", resource_handler, path_keyword='path')
+ print("(%s/%d) added resource '%s'" % (set, res, rType))
+ return res
+ except:
+ print("ERROR (%s): failed to add resource to resource set" % set)
+
+
+def changeSetClass(set, klass):
+
+ rset = get_resource_set(set)
+
+ if rset:
+ try:
+ rset.setProperty("class", dbus.String(klass, variant_level=1))
+ print("(%s) changed the application class to %s" % (set, klass))
+ except:
+ print("ERROR (%s): failed to change resource set class to '%s'" % (set, klass))
+
+
+def acquireSet(set):
+
+ rset = get_resource_set(set)
+
+ if rset:
+ try:
+ rset.request()
+ print("(%s) asked (asynchronously) to acquire" % set)
+ except:
+ print("ERROR (%s): failed to acquire resource set" % set)
+
+
+def releaseSet(set):
+
+ rset = get_resource_set(set)
+
+ if rset:
+ try:
+ rset.release()
+ print("(%s) asked (asynchronously) to release" % set)
+ except:
+ print("ERROR (%s): failed to release resource set" % set)
+
+
+def showSet(set):
+
+ rset = get_resource_set(set)
+
+ if rset:
+ try:
+ print("(%s)" % set)
+ values = rset.getProperties()
+ print(pretty_str_dbus_value(values))
+ except:
+ print("ERROR (%s): failed to query properties of resource set" % set)
+
+
+def deleteResource (set, resource):
+
+ res = get_resource(set, resource)
+
+ if res:
+ try:
+ res.delete()
+ del resources[(int(set), int(resource))]
+ print("(%s/%s) deleted resource" % (set, resource))
+ except:
+ print("ERROR (%s/%s): failed to delete resource" % (set, resource))
+
+
+def changeResource(set, resource, attribute, value):
+
+ res = get_resource(set, resource)
+
+ if res:
+ try:
+ if attribute == "shared" or attribute == "mandatory":
+ val = False
+ if value == "True" or value == "1":
+ val = True
+
+ res.setProperty(attribute, dbus.Boolean(val, variant_level=1))
+ print("(%s/%s) set attribute '%s' to '%s'" % (set, resource, attribute, val))
+ else:
+ attrs = res.getProperties()["attributes"]
+
+ if attribute in attrs:
+
+ if type(attrs[attribute]) is dbus.String:
+ attrs[attribute] = dbus.String(value)
+ elif type(attrs[attribute]) is dbus.Int32:
+ attrs[attribute] = dbus.Int32(value)
+ elif type(attrs[attribute]) is dbus.UInt32:
+ attrs[attribute] = dbus.UInt32(value)
+ elif type(attrs[attribute]) is dbus.Double:
+ attrs[attribute] = dbus.Double(value)
+
+ res.setProperty("attributes_conf", attrs)
+ print("(%s/%s) set attribute '%s' to '%s'" %
+ (set, resource, attribute, str(attrs[attribute])))
+ else:
+ print("ERROR (%s/%s): attribute '%s' not supported in resource" % (set, resource, attribute))
+ except dbus.DBusException as e:
+ print("ERROR (%s/%s): failed to set attribute '%s' in resource: %s" % (set, resource, attribute, e))
+ except:
+ print("ERROR (%s/%s): failed to convert value type to D-Bus" % (set, resource))
+
+
+
+def showResource(set, resource):
+
+ res = get_resource(set, resource)
+
+ if res:
+ try:
+ print("(%s/%s)" % (set, resource))
+ values = res.getProperties()
+ print(pretty_str_dbus_value(values))
+ except dbus.DBusException, e:
+ print("ERROR (%s/%s): failed to query properties of resource: %s" % (set, resource, e))
+
+
+def stdin_cb(fd, condition):
+ data = fd.read()
+ tokens = data.split()
+
+ set = None
+ resource = None
+
+ if len(tokens) == 0 or len(tokens) > 5:
+ return True
+
+ if tokens[0] == "createSet" and len(tokens) == 1:
+ createSet()
+ elif tokens[0] == "deleteSet" and len(tokens) == 2:
+ deleteSet(*tokens[1:])
+ elif tokens[0] == "createResource" and len(tokens) == 3:
+ createResource(*tokens[1:])
+ elif tokens[0] == "changeSetClass" and len(tokens) == 3:
+ changeSetClass(*tokens[1:])
+ elif tokens[0] == "acquireSet" and len(tokens) == 2:
+ acquireSet(*tokens[1:])
+ elif tokens[0] == "releaseSet" and len(tokens) == 2:
+ releaseSet(*tokens[1:])
+ elif tokens[0] == "showSet" and len(tokens) == 2:
+ showSet(*tokens[1:])
+ elif tokens[0] == "deleteResource" and len(tokens) == 3:
+ deleteResource(*tokens[1:])
+ elif tokens[0] == "changeResource" and len(tokens) == 5:
+ changeResource(*tokens[1:])
+ elif tokens[0] == "showResource" and len(tokens) == 3:
+ showResource(*tokens[1:])
+ elif tokens[0] == "quit":
+ mainloop.quit()
+ return False
+ else:
+ help()
+
+ add_prompt_later()
+
+ return True
+
+
+def fuzz_test():
+ """ randomly create, acquire, release and destroy resource sets """
+
+ global rsets
+ global n_iterations
+
+ resource_names = [ "audio_playback", "audio_recording" ]
+ operations = [ "create", "delete", "acquire", "release" ]
+ attributes={"pid":range(1, 1000),"role":["music","navigator","game"],"policy":["relaxed","strict"]}
+ coin = [True, False]
+
+ print("fuzz_test, left", n_iterations, "iterations")
+
+ if n_iterations > 0:
+ n_iterations = n_iterations - 1
+
+ if len(rsets) == 0:
+ # no sets, have to create
+ op = "create"
+ elif limit and len(rsets) >= limit:
+ # limit was reached
+ op = "delete"
+ else:
+ op = choice(operations)
+
+ if op == "create":
+ rset_id = createSet()
+
+ if (rset_id != None):
+ n_res = choice(range(1, len(resource_names)))
+ res_choice = choice(list(combinations(resource_names, n_res)))
+
+ print("created rset", str(rset_id))
+
+ for res in res_choice:
+ res_id = createResource(str(rset_id), res)
+ print("resource", res)
+
+ # change some resources
+
+ # this many attributes
+ n_attrs = choice(range(1, len(attributes)))
+ attr_choice = choice(list(combinations(attributes.keys(), n_attrs)))
+
+ for attr in attr_choice:
+ values = attributes[attr]
+ value = choice(values)
+ changeResource(str(rset_id), str(res_id), attr, value)
+
+ changeResource(str(rset_id), str(res_id), "mandatory", str(choice(coin)));
+ changeResource(str(rset_id), str(res_id), "shared", str(choice(coin)));
+
+
+ elif op == "delete":
+ if len(rsets) > 0:
+ id = choice(rsets.keys())
+ print("deleting rset", id)
+ deleteSet(str(id))
+
+ elif op == "acquire":
+ if len(rsets) > 0:
+ id = choice(rsets.keys())
+ print("acquiring rset", id)
+ acquireSet(str(id))
+
+ elif op == "release":
+ if len(rsets) > 0:
+ id = choice(rsets.keys())
+ print("releasing rset", id)
+ releaseSet(str(id))
+
+ return True
+
+ else:
+ return False # do not call again
+
+
+def main(args):
+ global manager
+ global bus
+ global mainloop
+ global interactive
+ global n_iterations
+
+ # D-Bus initialization
+
+ DBusGMainLoop(set_as_default=True)
+ mainloop = gobject.MainLoop()
+
+ bus = dbus.SystemBus()
+
+ if not bus:
+ print("ERROR: failed to get system bus")
+ exit(1)
+
+ # Create the manager for handling resource sets.
+
+ manager_obj = None
+
+ # TODO: get service and manager path from command line?
+ try:
+ manager_obj = bus.get_object('org.Murphy', '/org/murphy/resource')
+ except:
+ pass
+
+ if not manager_obj:
+ print("ERROR: failed get Murphy resource manager object")
+ exit(1)
+
+ manager = dbus.Interface(manager_obj, dbus_interface='org.murphy.manager')
+
+ if (len(args) > 0 and args[0] == "fuzz"):
+ interactive = False
+ n_iterations = 1000
+ if (len(args) == 2):
+ n_iterations = int(args[1])
+ if (len(args) == 3):
+ limit = int(args[2])
+ glib.idle_add(fuzz_test)
+
+ else:
+ # interactive mode
+ interactive = True
+ manager.connect_to_signal("propertyChanged", mgr_handler, path_keyword='path')
+ # make STDIN non-blocking
+ fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
+ # listen for user input
+ glib.io_add_watch(sys.stdin, glib.IO_IN, stdin_cb)
+ add_prompt()
+
+ mainloop.run()
+
+ # TODO: cleanup
+
+main(sys.argv[1:])
\ No newline at end of file
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: latin-1 -*-
+
+#
+# this script produces ABNF description of grammars implemented by flex/bison
+# ABNF stands for Augmented Backus-Naur Form as it is specified by the IETF
+# RFC 5234
+#
+#
+
+import os, sys, re
+
+def regexp_to_abnf(string):
+ return regexp_parse(string, 0)[0]
+
+def regexp_parse(string, index):
+ stack = []
+ precedence = 0
+ escape = False
+
+ while index < len(string):
+ c = string[index]
+
+ insert = False
+
+ if c == "\\":
+ escape = True
+ else:
+ if c.isalnum() or escape:
+ if c == "\"":
+ new_string = "DQUOTE"
+ elif c == " ":
+ new_string = "SP"
+ else:
+ new_string = "\"%s\"" % c
+ new_precedence = 100
+ escape = False
+ elif c == ".":
+ new_precedence = 90
+ new_string = "VCHAR"
+ elif c == "{":
+ new_precedence = 100
+ if index+1 >= len(string) or string.find("}", index+1) < 0:
+ new_string = "\"{\""
+ else:
+ begin = index+1
+ end = string.find("}", begin)
+ new_string = string[begin:end]
+ index = end
+ elif c == "(":
+ new_precedence = 80
+ result, index = regexp_parse(string, index+1)
+ if len(result) < 1:
+ index += 1
+ continue
+ new_string = "(" + result + ")"
+ elif c == ")":
+ break
+ elif c == "[":
+ new_precedence = 70
+ new_string, index = regexp_character_class(string, index)
+ elif c == "*":
+ new_precedence = 43
+ new_string = "*"
+ insert = True
+ elif c == "+":
+ new_precedence = 42
+ new_string = "1*"
+ insert = True
+ elif c == "?":
+ new_precedence = 40
+ new_string = "0*1"
+ insert = True
+ elif c == "|":
+ new_precedence = 30
+ new_string = "/"
+ else:
+ new_precedence = 100
+ if c == "\"":
+ new_string = "DQUOTE"
+ elif ord(c) < ord(' '):
+ new_string = "\%x%x" % ord(c)
+ else:
+ new_string = "\"" + c + "\""
+
+ if insert:
+ last = len(stack) - 1
+ if last >= 0:
+ stack.insert(last, (stack[last][0], new_string))
+ stack = regexp_merge(new_precedence, stack)
+ else:
+ stack = regexp_merge(new_precedence, stack)
+ stack.append((new_precedence, new_string))
+
+ precedence = new_precedence
+ index += 1
+
+ if len(stack) < 1:
+ result = ""
+ else:
+ result = regexp_merge(-1, stack)[0][1]
+
+
+ if result.startswith("(") and result.endswith(")"):
+ strip = True
+ balance = 0
+ for c in result[1:-1]:
+ if c == "(":
+ balance += 1
+ elif c == ")":
+ balance -= 1
+ if balance < 0:
+ strip = False
+ break
+ if strip and balance == 0:
+ result = result[1:-1]
+
+ return (result, index)
+
+def regexp_character_class(string, index):
+ cnt = 0
+ result = ""
+ backslash = False
+ sep = ""
+
+ if string[index+1] == "^":
+ index += 1
+ ranges = [(32, 126)]
+ escape = False
+ while index+1 < len(string):
+ index += 1
+ char = string[index]
+
+ if char == "]":
+ break
+
+ if char == "\\":
+ escape = True
+ continue
+
+ c = ord(char)
+
+ if escape:
+ if char == "n":
+ c = 10
+ if char == "t":
+ c = 9
+ escape = False
+
+ for r in ranges:
+ if c >= r[0] and c <= r[1]:
+ ranges.remove(r)
+ if c == r[0]:
+ if c != r[1]:
+ ranges.append((r[0]+1, r[1]))
+ break
+ elif c == r[1]:
+ if c != r[0]:
+ ranges.append((r[0], r[1]-1))
+ break
+ else:
+ ranges.append((r[0], c-1))
+ ranges.append((c+1, r[1]))
+ break
+
+ lower = False
+ upper = False
+ digit = False
+ for r in ranges:
+ if r[0] <= 65 and r[1] >= 90:
+ upper = r
+ if r[0] <= 97 and r[1] >= 122:
+ lower = r
+ if r[0] <= 48 and r[1] >= 57:
+ digit = r
+ if lower and upper:
+ ranges = regexp_range_extract(ranges, 97,122)
+ ranges = regexp_range_extract(ranges, 65,90)
+ result += sep + "ALPHA"
+ sep = " / "
+ cnt += 1
+ if digit:
+ ranges = regexp_range_extract(ranges, 48,57)
+ result += sep + "DIGIT"
+ sep = " / "
+ cnt += 1
+ ranges.sort(regexp_range_sort)
+ for r in ranges:
+ if r[0] == r[1]:
+ result += sep + "%x" + "%x" % r[0]
+ else:
+ result += sep + "%x" + "%x-%x" % r
+ sep = " / "
+ cnt += 1
+ else:
+ while index+1 < len(string):
+ index += 1
+ c = string[index]
+
+ if c == "]":
+ break
+
+ if c == "\\":
+ if not backslash:
+ backslash = True
+ continue
+ else:
+ if backslash:
+ backslash = False
+ c = regexp_escape(c)
+ elif c == " ":
+ c = "SP"
+ elif c == "\"":
+ c = "DQUOTE"
+ else:
+ if string[index:index+3] == "0-9":
+ index += 2
+ c = "DIGIT"
+ elif string[index:index+6] == "a-zA-Z" or \
+ string[index:index+6] == "A-Za-z":
+ index += 5
+ c = "ALPHA"
+ elif index < len(string)-2 and string[index+1] == "-" and \
+ ((string[index].isalpha() and \
+ string[index+2].isalpha()) or \
+ (string[index].isdigit() and \
+ string[index+2].isdigit())):
+ index += 2
+ start = ord(c)
+ end = ord(string[index])+1
+ sep2 = ""
+ c = ""
+
+ for i in range(start, end):
+ c += sep2 + "\"" + chr(i) + "\""
+ sep2 = " / "
+ else:
+ c = "\"" + c + "\""
+
+ result += sep + c
+ sep = " / "
+ cnt += 1
+
+ if cnt > 1:
+ result = "(" + result + ")"
+
+ return (result, index)
+
+def regexp_escape(char):
+ if char == "n":
+ return "CRLF"
+ elif char == "t":
+ return "HTAB"
+ elif char == " ":
+ return "SP"
+ elif char == "\"":
+ return "DQUOTE"
+ elif char == "\\":
+ return "\"\\\""
+ elif char == "^":
+ return "\"^\""
+
+ return "\"" + char + "\""
+
+
+def regexp_merge(new_precedence, stack):
+ last = len(stack) - 1
+ precedence = 0
+
+ if last >= 0:
+ for merge in range(last,-1,-1):
+ precedence = stack[merge][0]
+
+ if new_precedence >= precedence:
+ break
+
+ append = False
+ string = ""
+ sep = ""
+
+ for i in range(merge,last+1):
+ element = stack[i][1]
+ string += sep + element
+ if element.find("*") == len(element)-1:
+ sep = ""
+ else:
+ sep = " "
+ append = True
+
+
+ for i in range(last,merge-1,-1):
+ stack.pop()
+
+ if append:
+ stack.append((new_precedence, string))
+
+ return stack
+
+def regexp_range_extract(ranges, l,h):
+ for r in ranges:
+ if r[0] <= l and r[1] >= h:
+ ranges.remove(r)
+ if l == r[0] and h < r[1]:
+ ranges.append((h+1, r[1]))
+ elif l > r[0] and h == r[1]:
+ ranges.append((r[0], l-1))
+ elif l > r[0] and h < r[1]:
+ ranges.append((r[0],l-1))
+ ranges.append((h+1,r[1]))
+ break
+ return ranges
+
+def regexp_range_sort(a,b):
+ if a[0] < b[0]:
+ return -1
+ elif a[0] > b[0]:
+ return +1
+ return 0
+
+
+def component_list(input_list):
+ output_list = ""
+ sep = ""
+ if len(input_list) > 0:
+ components = input_list.split(' ')
+ ncomponent = len(components)
+ if ncomponent > 0:
+ for component in components:
+ name = component.strip()
+ if len(name) < 1:
+ continue
+ output_list += sep
+ sep = " "
+ if name.startswith("TKN_"):
+ output_list += name[4:]
+ else:
+ output_list += name
+ return output_list
+
+
+def rule_list(rule_def):
+ stripped_rule_def = rule_def.strip()
+ rlist = ""
+ sep = ""
+ if len(stripped_rule_def) > 0:
+ rules = stripped_rule_def.split('|')
+ nrule = len(rules)
+ if nrule == 1:
+ stripped_rule = rules[0].strip()
+ rlist += component_list(stripped_rule)
+ elif nrule > 1:
+ if len(rules[0].strip()) < 1:
+ rlist = "["
+ close = "]"
+ else:
+ rlist = "("
+ close = ")"
+ for rule in rules:
+ stripped_rule = rule.strip()
+ if len(stripped_rule) > 0:
+ rlist += sep + component_list(stripped_rule)
+ sep = "|"
+ rlist += close
+ return rlist
+
+
+def print_abnf_rules():
+ name_len = 0
+ prologue = "<![CDATA["
+ epilogue = "]]>"
+ extra_linefeed = ""
+
+ for rule in abnf:
+ name_len = max(len(rule[0]), name_len)
+
+ for rule in abnf:
+ line = rule[0].ljust(name_len) + " ="
+ words = rule[1].split(' ')
+ margin = len(line)
+ width = margin
+
+ for word in words:
+ wl = len(word) + 1
+ if width + wl > line_width:
+ line += "\n".ljust(margin+2)
+ width = margin
+ line += " " + word
+ width += wl
+
+ if rule[0].isupper():
+ extra_linefeed = ""
+ else:
+ if len(extra_linefeed) < 1:
+ print "\n"
+ extra_linefeed = "\n"
+
+ print prologue + line + extra_linefeed
+ prologue = ""
+ print epilogue
+
+def make_abnf_rules(topresults):
+ for result in topresults:
+ abnf.append( (result, abnf_rule(topresults, result, 0, " ")) )
+
+
+def abnf_rule(topresults, result, rdepth, gap):
+ canonic = ""
+ component = result.strip()
+ toplevel = component in topresults
+
+ if component in results:
+ rule_list = results[component]
+ if rule_list.startswith("(") and rule_list.endswith(")"):
+ stripped_rule_list = rule_list[1:-1].strip()
+ if rdepth < 1:
+ close = ""
+ else:
+ canonic += " ("
+ close = ")"
+ gap = ""
+ elif rule_list.startswith("[") and rule_list.endswith("]"):
+ stripped_rule_list = rule_list[1:-1].strip()
+ canonic += " ["
+ close = "]"
+ gap = ""
+ else:
+ stripped_rule_list = rule_list.strip()
+ close = ""
+
+ escaped_rule_list = stripped_rule_list.replace("\"|\"","Ö")
+ rules = escaped_rule_list.split("|")
+ sep = ""
+ for rule in rules:
+ components = rule.replace("Ö","\"|\"").split(' ')
+
+ if components[0].strip() == result:
+ rept = ")"
+ else:
+ rept = ""
+ canonic += sep
+ sep = " /"
+
+ first = True
+ for component in components:
+ component_name = component.strip()
+ if len(component_name) < 1:
+ continue
+ if first and component_name == result:
+ canonic += " *("
+ gap = ""
+ elif component_name in topresults:
+ canonic += gap + component_name
+ gap = " "
+ else:
+ canonic += abnf_rule(topresults, component_name, \
+ rdepth+1,gap)
+ gap = " "
+ first = False
+ canonic += rept
+
+ canonic += close
+ else:
+ canonic += gap + component
+
+ return canonic
+
+
+lfile = sys.argv[1]
+yfile = sys.argv[2]
+line_width = 78
+inputdir = os.path.dirname(yfile)
+scriptdir = sys.path[0]
+lname = os.path.basename(lfile)
+yname = os.path.basename(yfile)
+start = ""
+grammar = False
+prologue = False
+comment = False
+toplevel = False
+depth = 0
+result = ""
+result_def = ""
+results = {}
+top_results = []
+abnf = []
+pos = 0
+end = 0
+
+
+
+if not inputdir == "" and not inputdir.endswith("/"):
+ inputdir = "%s/" % inputdir
+
+if not scriptdir == "" and not scriptdir.endswith("/"):
+ scriptdir = "%s/" % scriptdir
+
+
+with open(lfile, "r") as l:
+ skip = False
+ for line in l.xreadlines():
+ if skip:
+ if line.startswith("%}"):
+ skip = False
+ else:
+ if line.startswith("%{"):
+ skip = True
+ elif line.startswith("%%"):
+ break
+ elif line.startswith("/*"):
+ continue
+
+ stripped_line = line[:-1].strip()
+
+ if len(stripped_line) < 3:
+ continue
+
+ sp = stripped_line.find(" ")
+ tab = stripped_line.find("\t")
+
+ if sp > 0 and (tab < 0 or tab > sp):
+ delim = sp
+ elif tab > 0 and (sp < 0 or sp > tab):
+ delim = tab
+ else:
+ continue
+
+ key = stripped_line[:delim].strip()
+ value = stripped_line[delim:].strip()
+
+ if key[0] == "%" or len(value) < 1:
+ continue
+
+ if value.isalpha():
+ abnf.append( (key, " \"" + value.upper() + "\"") )
+ else:
+ if value[0] == "\\" and len(value) == 2:
+ results[key] = "\"%s\"" % value[1]
+ elif len(value) == 1:
+ results[key] = "\"%s\"" % value[0]
+ else:
+ if value.find("[") < 0 and value.find("(") < 0:
+ results[key] = "\"%s\"" % value.replace("/","")
+ else:
+ abnf.append( (key.lower(), " "+regexp_to_abnf(value)) )
+ results[key] = key.lower()
+
+ l.close()
+
+
+with open(yfile, "r") as y:
+ for line in y.xreadlines():
+ if grammar:
+ if line.startswith("%%"):
+ break
+ elif line.startswith("/*#") and line.endswith("#*/\n"):
+ key = line[3:-4].strip()
+ if key == "toplevel":
+ toplevel = True
+ else:
+ pos = 0
+ end = len(line)
+ if depth == 0:
+ colon = line.find(":")
+ slash_star = line.find("/*")
+ if colon > 0 and (slash_star < 0 or slash_star > colon):
+ result = line[:colon]
+ result_def = ""
+ pos = colon + 1
+ while pos < end-1:
+ if comment:
+ star_slash = line.find("*/", pos)
+ if star_slash < 0:
+ break
+ pos = star_slash + 2
+ comment = False
+ else:
+ slash_star = line.find("/*", pos)
+ open_brace = line.find("{" , pos)
+ close_brace = line.find("}" , pos)
+ if slash_star >= 0 and \
+ (open_brace < 0 or slash_star < open_brace) and \
+ (close_brace < 0 or slash_star < close_brace):
+
+ comment = True
+ pos = slash_star + 2
+ continue
+ if open_brace >= 0 and \
+ (close_brace < 0 or open_brace < close_brace) and \
+ (slash_star < 0 or open_brace < slash_star ):
+
+ if depth == 0:
+ result_def += line[pos: open_brace]
+ depth += 1
+ pos = open_brace + 1
+ continue
+ if close_brace >= 0 and \
+ (open_brace < 0 or close_brace < open_brace) and \
+ (slash_star < 0 or close_brace < slash_star):
+ depth -= 1
+ pos = close_brace + 1
+ continue
+ if depth == 0:
+ semicolon = line.find(";", pos)
+ if semicolon >= pos:
+ result_def += line[pos: semicolon]
+ results[result] = rule_list(result_def)
+ if toplevel:
+ top_results.append(result)
+ result = ""
+ result_def = ""
+ toplevel = False
+ else:
+ result_def += line[pos: -1]
+ break
+ else:
+ if prologue:
+ if line.startswith("%}"):
+ prologue = False
+ elif line.startswith("%{"):
+ prologue = True
+ elif line.startswith("%start"):
+ start = line[6:].strip()
+ elif line.startswith("%%"):
+ grammar = True
+ y.close()
+
+
+
+make_abnf_rules(top_results)
+
+print "<!-- XML file was automatically generated from %s and from %s -->\n" % \
+ (lname, yname)
+
+print "<screen>"
+print_abnf_rules()
+print "</screen>"
+
+
+
+
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: latin-1 -*-
+
+
+import os, sys, re
+from lxml import etree
+
+def fix_dummy(broken_xml):
+ start = 0
+ end = len(broken_xml)
+ fixed_xml = ""
+
+ for match in re.finditer(dummy_pattern, broken_xml):
+ fixed_xml += broken_xml[start:match.start()]
+ start = match.end()
+
+ if start < end:
+ fixed_xml += broken_xml[start:end]
+ return fixed_xml
+
+def fix_graphs(broken_xml):
+ start = 0
+ end = len(broken_xml)
+ fixed_xml = ""
+
+ for match in re.finditer(graph_pattern, broken_xml):
+ fixed_xml += fix_dummy(broken_xml[start:match.start()])
+ fixed_xml += "<!ENTITY graph%s \"%s\">" % \
+ (match.group(1), os.path.basename(match.group(2)))
+ start = match.end()
+
+ if start < end:
+ fixed_xml += fix_dummy(broken_xml[start:end])
+ return fixed_xml
+
+def fix_files(broken_xml):
+ start = 0
+ end = len(broken_xml)
+ fixed_xml = ""
+
+ for match in re.finditer(file_pattern, broken_xml):
+ fixed_xml += fix_graphs(broken_xml[start:match.start()])
+ fixed_xml += "<!ENTITY file%s SYSTEM \"%s\">" % \
+ (match.group(1), match.group(2))
+ start = match.end()
+
+ if start < end:
+ fixed_xml += fix_graphs(broken_xml[start:end])
+ return fixed_xml
+
+
+if len(sys.argv) >= 2:
+ path = sys.argv[1]
+ fnam = os.path.basename(path)
+else:
+ fnam = "<unknown>"
+
+sys.stderr.write(" DBLYX %s\n" % fnam)
+
+try:
+ input = open(path, "r")
+ input_xml = input.read()
+ input.close()
+except IOError as (errno, strerror):
+ print "Input error %d - %s" % (errno, strerror)
+ exit(errno)
+
+dummy_pattern = re.compile("<[/]?dummy>")
+file_pattern = re.compile("<!ENTITY file([0-9]*) \"([^\"]*)\">")
+graph_pattern = re.compile("<!ENTITY graph([0-9]*) \"([^\"]*)\">")
+
+modified_xml = fix_files(input_xml)
+
+parser = etree.XMLParser(strip_cdata=False)
+xmltree = etree.XML(modified_xml, parser)
+
+output_xml = etree.tostring(xmltree, pretty_print=True)
+
+if len(sys.argv) == 3:
+ try:
+ output = open(sys.argv[2], "w")
+ output.write(output_xml)
+ output.close()
+ except IOError as (errno, strerror):
+ print "Output error %d - %s" % (errno, strerror)
+else:
+ print output_xml
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Doxygen XML to DocBook converter
+ ================================
+
+ Usage: doxml2db.py [OPTIONS] <dir> <doxml-files> {<docbook-file>|--}
+
+ <dir> - is the directory where the doxml files are
+ <doxml-file> - list of one or more whitespace separated doxml input files
+ <docbook-file> - the output file
+ -- - docbook is printed to stdout
+
+ OPTIONS
+ -h or --help - print this help message
+ --title=<title> - main chapter or section title
+ --depth=<depth> - 0 = chapter, 1+ = section
+
+ :license: BSD.
+"""
+import sys
+import os
+import re
+import lxml.etree as ET
+from copy import deepcopy
+
+_files = set()
+_sources = {}
+_comment_pattern = re.compile(r"(/\*.*\*/)|(//.*)")
+
+def _main():
+ title = None
+ depth = 1
+ sect = {'enum':[], 'struct':[], 'union':[],
+ 'typedef':[], 'function':[], 'define':[]}
+
+ fidx = 1
+ for arg in sys.argv[fidx:]:
+ if arg.startswith('-') and arg != '--':
+ if arg == '-h' or arg == '--help':
+ _usage()
+ elif arg.startswith('--title='):
+ title = arg[8:]
+ elif arg.startswith('--depth='):
+ depth = int(arg[8:])
+ else:
+ _usage("invalid option '%s'" % arg)
+ fidx += 1
+ if len(sys.argv) < fidx + 3:
+ _usage("too few arguments")
+ else:
+ if os.path.isdir(sys.argv[fidx]):
+ doxydir = sys.argv[fidx]
+ else:
+ _usage(sys.argv[fidx] + " not a directory")
+ for f in sys.argv[fidx+1:-1]:
+ _add_doxml_file(doxydir, f, sect)
+ if sys.argv[-1] == '--':
+ dbfile = sys.stdout
+ close_dbfile = False
+ else:
+ dbfile = open(sys.argv[-1], "w")
+ close_dbfile = True
+
+
+ # parse all the doxml files
+ # store the result in variable 'sect'
+ for name, doxml in _files:
+ if name[0] != '<':
+ sys.stderr.write(" DOXML %s\n" % name)
+ ParseDoxmlFile(doxml, sect)
+
+ # process 'sect' (eg. resolve typedefs, skip useless entries)
+ # merge evetrything into a sorted list ('defs')
+ defs = ProcessSections(sect)
+
+ dbroot = BuildDBTree(defs, title, depth)
+
+ # dump the dbtree
+ if dbroot.tag == 'root':
+ for section in dbroot.iterchildren():
+ dbfile.write(ET.tostring(section, pretty_print=True))
+ else:
+ dbfile.write(ET.tostring(dbroot, pretty_print=True))
+
+ if close_dbfile:
+ dbfile.close()
+
+
+def _usage(errmsg):
+ if len(errmsg) < 1:
+ err = 0
+ else:
+ err = 1
+ sys.stderr.write("Error: " + errmsg + "\n\n")
+ sys.stderr.write(__doc__)
+ sys.exit(err)
+
+def _print_sections(sect):
+ for s in sect:
+ _print_item("*** %s" % s, sect[s], 0)
+
+def _print_item(name, value, indent):
+ if value.__class__.__name__ == "list":
+ if len(name) > 0:
+ print "%s%s" % (" "*indent, name)
+ for lval in value:
+ _print_item('', lval, indent+4)
+ elif value.__class__.__name__ == "dict":
+ if len(name) < 1 and 'name' in value:
+ name = value['name']
+ if len(name) > 0:
+ print "%s%s" % (" "*indent, name)
+ for dnam, dval in value.iteritems():
+ _print_item(dnam+':', dval, indent+4)
+ else:
+ print "%s%s %s" % (" "*indent, name, value)
+
+
+####################### parse doxml to internal format #######################
+
+
+def ParseDoxmlFile(doxml_file, sect):
+ parser = ET.XMLParser(remove_comments=True, strip_cdata=False)
+ tree = ET.parse(doxml_file, parser=parser)
+ root = tree.getroot()
+ _traverse(root, sect, {})
+
+def _add_doxml_file(doxydir, path, sect):
+ name = re.sub("(\.)([hc]$)", "_8\\2.xml", os.path.basename(path))
+ doxml = os.path.join(doxydir, name)
+ if os.path.isfile(doxml):
+ _files.add((name, doxml))
+ _find_includes_in(doxydir, doxml, sect)
+ return True
+ return False
+
+def _find_includes_in(doxydir, filnam, sect):
+ tree = ET.parse(filnam)
+ root = tree.getroot()
+ for topel in root.getchildren():
+ if topel.tag == "compounddef" and topel.get("kind") == "file":
+ for el in topel.getchildren():
+ if el.tag == "includes" and len(el.text) > 0:
+ _add_doxml_file(doxydir, el.text, sect)
+ elif el.tag == "innerclass" and len(el.get("refid")) > 0:
+ refid = el.get("refid")
+ fpath = os.path.join(doxydir, refid + ".xml")
+ if os.path.isfile(fpath):
+ for snam in sect:
+ if refid.startswith(snam):
+ _files.add(("<" + el.text + ">", fpath))
+ break
+ return
+
+def _traverse(el, sect, entry):
+ if el.tag in globals():
+ entry = globals()[el.tag](el, sect, entry)
+ return entry
+
+def _traverse_children(el, sect, entry):
+ for i in el.getchildren():
+ entry = _traverse(i, sect, entry)
+ return entry
+
+def _attribute_tag(el, entry, name):
+ if el.text is not None and len(el.text) > 0:
+ entry[name] = el.text
+ return entry
+
+def _text_markup(el, sect, entry, name):
+ text = _traverse_children(el, sect, [])
+ if len(text) > 0:
+ entry[name] = text
+ return entry
+
+
+def _list_collector(el, sect, entry, name, init):
+ if name not in entry:
+ entry[name] = []
+ list_entry = _traverse_children(el, sect, {})
+ if len(list_entry) > 0:
+ if init is not None and len(init) > 0:
+ list_entry.update(init)
+ entry[name].append(list_entry)
+ return entry
+
+def _get_code(entry, path, start, end):
+ global _sources, _comment_pattern
+ if path not in _sources:
+ if not os.path.isfile(path):
+ return
+ f = open(path, "r")
+ _sources[path] = f.readlines()
+ f.close()
+ code = "".join(_sources[path][start-1:end])
+ entry['code'] = re.sub(_comment_pattern, '', code).rstrip()
+ return entry
+
+
+def compounddef(el, sect, entry):
+ t = el.get('kind')
+ if t in ['struct', 'union']:
+ sect[t].append(_traverse_children(el, sect, {}))
+ elif t == 'file':
+ entry = _traverse_children(el, sect, entry)
+ return entry
+
+def compoundname(el, sect, entry):
+ if el.getparent().get('kind') in ['struct', 'union']:
+ entry['name'] = el.text
+ return entry
+
+def sectiondef(el, sect, entry):
+ if el.get('kind') in ['enum', 'typedef', 'func', 'define'] or \
+ el.getparent().get('kind') in ['struct', 'union']:
+ entry = _traverse_children(el, sect, entry)
+ return entry
+
+
+def memberdef(el, sect, entry):
+ t = el.get('kind')
+ if t in sect:
+ sect[t].append(_traverse_children(el, sect, {}))
+ elif t == 'variable':
+ entry = _list_collector(el, sect, entry, 'variables', None)
+ return entry
+
+def location(el, sect, entry):
+ f = el.get('bodyfile')
+ s = int(el.get('bodystart', -1))
+ e = int(el.get('bodyend', -1))
+ if f is not None and s > 0 and e > 0:
+ p = el.getparent()
+ if p.tag == 'compounddef' and p.get('kind') in ['struct', 'union']:
+ entry['file'] = {'path':f, 'start':s, 'end':e}
+ entry = _get_code(entry, f, s,e)
+ return entry
+
+
+def enumvalue(el, sect, entry):
+ return _list_collector(el, sect, entry, 'enumvalues', None)
+
+def name(el, sect, entry):
+ return _attribute_tag(el, entry, 'name')
+
+def type(el, sect, entry):
+ return _attribute_tag(el, entry, 'type')
+
+def declname(el, sect, entry):
+ return _attribute_tag(el, entry, 'declname')
+
+def definition(el, sect, entry):
+ return _attribute_tag(el, entry, 'def')
+
+def argsstring(el, sect, entry):
+ return _attribute_tag(el, entry, 'args')
+
+def initializer(el, sect, entry):
+ return _attribute_tag(el, entry, 'value')
+
+def briefdescription(el, sect, entry):
+ return _text_markup(el, sect, entry, 'brief')
+
+def detaileddescription(el, sect, entry):
+ return _text_markup(el, sect, entry, 'descr')
+
+def para(el, sect, entry):
+ if entry.__class__.__name__ == 'list':
+ if el.text is not None and len(el.text.strip()) > 0:
+ entry.append({'para' : el.text.strip()})
+ else:
+ entry = _traverse_children(el, sect, entry)
+ return entry
+
+def param(el, sect, entry):
+ return _list_collector(el, sect, entry, 'param', None)
+
+def parameterlist(el, sect, entry):
+ if entry.__class__.__name__ == 'list':
+ entry.append({el.get('kind') : _traverse_children(el, sect, [])})
+ return entry
+
+def parameteritem(el, sect, entry):
+ if entry.__class__.__name__ == 'list':
+ entry.append(_traverse_children(el, sect, {}))
+ return entry
+
+def parametername(el, sect, entry):
+ if el.text is not None:
+ entry['name'] = el.text
+ return entry
+
+def parameterdescription(el, sect, entry):
+ entry['descr'] = _traverse_children(el, sect, [])
+ return entry
+
+def simplesect(el, sect, entry):
+ if entry.__class__.__name__ == 'list':
+ if el.get('kind') in ['return']:
+ entry.append(_text_markup(el, sect, {}, el.get('kind')))
+ return entry
+
+def programlisting(el, sect, entry):
+ program = _traverse_children(el, sect, '')
+ if len(program) > 0:
+ if entry.__class__.__name__ == 'list':
+ entry.append({'code' : program})
+ elif entry.__class__.__name__ == 'dict':
+ entry['code'] = program
+ return entry
+
+def codeline(el, sect, entry):
+ entry += _traverse_children(el, sect, '') + '\n'
+ return entry
+
+def highlight(el, sect, entry):
+ if el.text is not None:
+ entry += el.text
+ entry += _traverse_children(el, sect, '')
+ if el.tail is not None:
+ entry += el.tail
+ return entry
+
+def sp(el, sect, entry):
+ entry += ' '
+ if el.tail is not None:
+ entry += el.tail
+ return entry
+
+
+
+doxygen = _traverse_children
+parameternamelist = _traverse_children
+osectiondef = sectiondef
+
+########################## Build output tree ################################
+
+def ProcessSections(sect):
+ index = {'define':0, 'typedef':1, 'enum':2,
+ 'struct':3, 'union':3, 'function':4}
+ #_print_sections(sect)
+ td = {}
+ defs = []
+
+
+ for i in sect['typedef']:
+ if i['type'] not in td:
+ td[i['type']] = []
+ td[i['type']].append({'name':i['name'], 'def':i['def'], 'print':True})
+
+ for i in ['struct', 'union', 'enum']:
+ for s in sect[i]:
+ if 'name' in s and s['name'].startswith('@'):
+ continue
+ de = {'sect':i, 'index':index[i]}
+ for key, value in s.iteritems():
+ tdk = "%s %s" % (i, value)
+ if key == 'name' and tdk in td and len(td[tdk]) == 1:
+ value = td[tdk][0]['name']
+ td[tdk][0]['print'] = False
+ de[key] = value
+ defs.append(de)
+ for t,l in td.iteritems():
+ for d in l:
+ if not d['print']:
+ continue
+ df = {'type':t, 'sect':'typedef', 'index':index['typedef']}
+ for key, value in d.iteritems():
+ if key != 'print':
+ df[key] = value
+ defs.append(df)
+ for t in ['define', 'function']:
+ extra = {'sect':t, 'index':index[t]}
+ for s in sect[t]:
+ if 'name' in s and not s['name'].startswith('@'):
+ de = deepcopy(s)
+ de.update(extra)
+ defs.append(de)
+ defs.sort(_cmpfunc)
+ return defs
+
+def _cmpfunc(a, b):
+ if 'index' in a and 'index' in b:
+ if a['index'] > b['index']:
+ return 1
+ if a['index'] < b['index']:
+ return -1
+ if 'name' in a and 'name' in b:
+ if a['name'] > b['name']:
+ return 1
+ elif a['name'] < b['name']:
+ return -1
+ return 0
+
+############################# BuildDBTree ###################################
+def BuildDBTree(defs, title, depth):
+ global _escape_pattern, _escape_map
+
+ _escape_pattern = re.compile(r"([\"&<>\x89-\xff])")
+ _escape_map = {"'" : ''',
+ '"' : '"',
+ '&' : '&' ,
+ '<' : '<' ,
+ '>' : '>' }
+
+ index = -1
+ root, section_depth = _make_root(title, depth)
+
+ for d in defs:
+ if d['index'] != index:
+ index = d['index']
+ container, new_depth = _make_container(root, index, section_depth)
+ entry_builder = "build_%s_entry" % d['sect']
+ if entry_builder in globals():
+ globals()[entry_builder](container, d, new_depth)
+
+ return root
+
+
+def _make_root(title, depth):
+ if title is None:
+ section_depth = depth
+ root = ET.Element('root')
+ else:
+ root = _make_section(None, title, depth=depth)
+ section_depth = depth + 1
+ return root, section_depth
+
+def _make_container(parent, index, section_depth):
+ container_builder = ['build_define_container',
+ 'build_typedef_container',
+ 'build_enum_container',
+ 'build_struct_container',
+ 'build_function_container']
+
+ if container_builder[index] in globals():
+ return globals()[container_builder[index]](parent, section_depth)
+
+ return parent, section_depth
+
+
+def _make_section(parent, title, depth):
+ if depth is None or depth.__class__.__name__ != 'int' or depth > 4:
+ ttl = ET.Element('para')
+ ttl.text = _escape_text(title.strip())
+ section = ET.Element('para')
+ if parent is not None:
+ parent.append(ttl)
+ else:
+ if depth == 0:
+ tag = 'chapter'
+ else:
+ tag = "sect%d" % depth
+
+ section = ET.Element(tag)
+
+ ttl = ET.Element('title')
+ ttl.text = _escape_text(title.strip())
+ section.append(ttl)
+
+ if parent is not None:
+ parent.append(section)
+
+ return section
+
+
+def _make_refentry(parent, title, name, volid="3"):
+ if parent is not None:
+ parent.append(ET.Element('beginpage'))
+
+ ref_entry = ET.Element('refentry', {'id':name})
+ parent.append(ref_entry)
+
+ ref_meta = ET.Element('refmeta')
+ ref_entry.append(ref_meta)
+
+ ref_entry_title = ET.Element('refentrytitle')
+ ref_entry_title.text = title.strip()
+ ref_meta.append(ref_entry_title)
+
+ man_volnum = ET.Element('manvolnum')
+ man_volnum.text = volid
+ ref_meta.append(man_volnum)
+
+ if parent is not None:
+ parent.append(ref_entry)
+
+ return ref_entry
+
+
+def _make_refdiv(parent, divnam):
+ div = ET.Element("ref%sdiv" % divnam)
+ if parent is not None:
+ parent.append(div)
+ return div
+
+def _make_refnamediv(parent, names, brief=None):
+ if brief is not None and len(brief.strip()) > 0:
+ div = _make_refdiv(parent, 'name')
+
+ if names.__class__.__name__ == 'str':
+ names = [names]
+
+ for n in names:
+ el = ET.Element('refname')
+ el.text = n.strip()
+ div.append(el)
+
+ ref_purpose = ET.Element('refpurpose')
+ ref_purpose.text = brief.strip()
+ div.append(ref_purpose)
+
+ if parent is not None:
+ parent.append(div)
+ else:
+ div = None
+ return div
+
+def _make_refsynopsisdiv(parent, typ, name, params, info=None):
+ div = _make_refdiv(parent, 'synopsis')
+
+ if info is not None and len(info.strip()) > 0:
+ func_synopsis_info = ET.Element('funcsynopsisinfo')
+ func_synopsis_info.text = _escape_text(info.strip())
+ div.append(func_synopsis_info)
+
+ _make_funcprototype(div, typ, name, params)
+
+ if parent is not None:
+ parent.append(div)
+ return div
+
+def _make_refsection(parent, title):
+ refsect1 = ET.Element('refsect1')
+
+ ttl = ET.Element('title')
+ ttl.text = _escape_text(title.strip())
+ refsect1.append(ttl)
+
+ if parent is not None:
+ parent.append(refsect1)
+ return refsect1
+
+
+def _make_funcprototype(parent, typ, name, params):
+ def format_type(typ):
+ stripped_type = typ.strip()
+ if " " in stripped_type:
+ return stripped_type
+ return "%s " % stripped_type
+
+ func_prototype = ET.Element('funcprototype')
+
+ func_def = ET.Element('funcdef')
+ func_def.text = format_type(_escape_text(typ.strip()))
+ func_prototype.append(func_def)
+
+ function = ET.Element('function')
+ function.text = _escape_text(name)
+ func_def.append(function)
+
+ for p in params:
+ paramdef = ET.Element('paramdef')
+ paramdef.text = format_type(_escape_text(p['type'].strip()))
+ func_prototype.append(paramdef)
+
+ if 'declname' in p and len(p['declname'].strip()) > 0:
+ parameter = ET.Element('parameter')
+ parameter.text = _escape_text(p['declname'].strip())
+ paramdef.append(parameter)
+
+ if parent is not None:
+ parent.append(func_prototype)
+
+ return func_prototype
+
+def _make_varname(parent, name):
+ varname = ET.Element('varname')
+ varname.text = _escape_text(name.strip())
+ if parent is not None:
+ parent.append(varname)
+ return varname
+
+def _make_para(parent, text=None):
+ para = ET.Element('para')
+ if text is not None:
+ para.text = "%s" % text
+ if parent is not None:
+ parent.append(para)
+ return para
+
+def _make_code(parent, code):
+ screen = ET.Element('programlisting')
+ screen.text = ET.CDATA(code)
+ if parent is not None:
+ parent.append(screen)
+ return screen
+
+def _unmarked_text(elements):
+ return _escape_text(_collect_text(elements))
+
+def _collect_text(elements):
+ text = ''
+
+ if elements is not None:
+ typ = elements.__class__.__name__
+
+ if typ == 'list':
+ for entry in elements:
+ text += _collect_text(entry)
+ elif typ == 'dict':
+ for name, value in elements.iteritems():
+ if name not in ['param', 'return']:
+ text += (" %s" % value)
+ else:
+ text += (" %s" % elements)
+
+ return text.strip()
+
+def _add_marked_text(parent, elements):
+ added = False
+ for el in elements:
+ for name, value in el.iteritems():
+ if name == 'para':
+ para = _make_para(parent, value)
+ added = True
+ return added
+
+def _make_param_list(parent, descr, render='table'):
+ entries = 0
+ if render == 'table':
+ param_list, tbody = _make_list_table(['1*', '5*'])
+ for en in descr:
+ if 'param' in en:
+ for p in en['param']:
+ if len(set(p) & set(['name', 'descr'])) != 2:
+ continue
+ _add_list_table_row(tbody, p['name'], p['descr'])
+ entries += 1
+ elif render == 'varlist':
+ param_list = ET.Element('variablelist')
+ for en in descr:
+ if 'param' in en:
+ for p in en['param']:
+ if len(set(p) & set(['name', 'descr'])) != 2:
+ continue
+ name = _make_varname(None, p['name'])
+ item = _add_varlist_item(param_list, name, '')
+ _add_marked_text(item, p['descr'])
+ entries += 1
+ if entries > 0:
+ if parent is not None:
+ parent.append(param_list)
+ return param_list
+ return None
+
+def _make_variable_list(parent, variables, ratio, render='table'):
+ entries = 0
+ if render == 'table':
+ variable_list, tbody = _make_list_table(['1*', '%d*' % ratio])
+ for en in variables:
+ if 'brief' in en:
+ _add_list_table_row(tbody, en['name'], en['brief'])
+ entries += 1
+ elif render == 'varlist':
+ variable_list = ET.Element('variablelist')
+ for en in variables:
+ if 'brief' in en:
+ name = _make_varname(None, en['name'])
+ item = _add_varlist_item(variable_list, name, '')
+ _add_marked_text(item, en['brief'])
+ entries += 1
+ if entries > 0:
+ if parent is not None:
+ parent.append(variable_list)
+ return variable_list
+ return None
+
+
+def _make_list_table(colwidths):
+ table = ET.Element('table',
+ {'align' : 'left',
+ 'frame' : 'none',
+ 'colsep' : '0',
+ 'rowsep' : '0',
+ 'rowheader' : 'norowheader'})
+
+ tgroup = ET.Element('tgroup', {'cols':'%s' % len(colwidths)})
+ table.append(tgroup)
+
+ for i in colwidths:
+ tgroup.append(ET.Element('colspec', {'colwidth':i}))
+
+ tbody = ET.Element('tbody')
+ tgroup.append(tbody)
+
+ return table, tbody
+
+def _add_list_table_row(parent, name, descr):
+ row = ET.Element('row')
+ parent.append(row)
+
+ name_entry = ET.Element('entry', {'align':'left', 'valign':'top'})
+ _make_varname(name_entry, name)
+ row.append(name_entry)
+
+ desc_entry = ET.Element('entry', {'align':'left', 'valign':'top'})
+ _add_marked_text(desc_entry, descr)
+ _make_para(desc_entry, ' ')
+ row.append(desc_entry)
+
+
+def _add_varlist_item(varlist, key, brief=None, descr=None):
+ list_entry = ET.Element('varlistentry')
+ varlist.append(list_entry)
+
+ term = ET.Element('term')
+ if ET.iselement(key):
+ term.append(key)
+ else:
+ term.text = key.strip()
+ list_entry.append(term)
+
+ list_item = ET.Element('listitem')
+ if brief is not None:
+ if len(brief) > 0:
+ list_item.text = "- %s" % brief
+ else:
+ list_item.text = ":"
+
+ if descr is not None:
+ if descr.__class__.__name__ == 'list':
+ list_item.extend(descr)
+ else:
+ list_item.append(descr)
+
+ list_entry.append(list_item)
+
+ return list_item
+
+def _escape_text(text):
+ global _escape_pattern, _escape_map
+
+ def find_replacement(match):
+ pattern = match.group(1)
+ if pattern == None:
+ return ''
+ elif pattern in _escape_map:
+ return _escape_map[pattern]
+ else:
+ return ''
+
+ return re.sub(_escape_pattern, find_replacement, text)
+
+def build_define_container(parent, section_depth):
+ section = _make_section(parent, "Preprocessor definitions", section_depth)
+ parent.append(section)
+
+ if section_depth < 2:
+ table, container = _make_list_table(['1*', '2*'])
+ section.append(table)
+ else:
+ container = ET.Element('variablelist')
+ section.append(container)
+ return container, section_depth + 1
+
+def build_define_entry(parent, define, depth):
+ if len(set(['name', 'value', 'brief']) & set(define)) != 3:
+ return
+
+ name = define['name']
+
+ if depth < 3:
+ descr = define['brief']
+ if 'descr' in define:
+ descr += define['descr']
+ _add_list_table_row(parent, name, define['brief'])
+ else:
+ brief = _unmarked_text(define['brief'])
+ cdata = "#define %s %s\n" % (name, define['value'])
+ para = _make_para(None)
+ code = _make_code(para, cdata)
+
+ _add_varlist_item(parent, name, brief, para)
+
+
+def build_enum_entry(parent, enum, depth):
+ if 'name' not in enum:
+ return
+ else:
+ name = enum['name']
+
+ if depth < 2:
+ if len(set(['brief', 'enumvalues']) & set(enum)) != 2:
+ return
+ ref_entry = _make_refentry(parent, "Enumeration %s" % name, name, '7')
+
+ _make_refnamediv(ref_entry, name, _unmarked_text(enum['brief']))
+
+ elist = _make_variable_list(None, enum['enumvalues'], 3)
+ if elist is not None:
+ _make_refsection(ref_entry, 'Values').append(elist)
+ else:
+ return
+
+
+def build_struct_entry(parent, struct, depth, kind='struct'):
+ if 'name' not in struct:
+ return
+ else:
+ name = struct['name']
+
+ if depth < 2:
+ if len(set(['brief', 'code', 'variables']) & set(struct)) != 3:
+ return
+ title = "%s %s" % (kind.capitalize(), name)
+ ref_entry = _make_refentry(parent, title, name, '7')
+
+ _make_refnamediv(ref_entry, name, _unmarked_text(struct['brief']))
+
+ synop_div = _make_refdiv(ref_entry, 'synopsis')
+
+ synopsis = ET.Element('synopsis')
+ synopsis.text = ET.CDATA(struct['code'])
+ synop_div.append(synopsis)
+
+ vlist = _make_variable_list(None, struct['variables'], 5)
+ if vlist is not None:
+ _make_para(synop_div, "Where")
+ _make_para(synop_div).append(vlist)
+
+ if 'descr' in struct:
+ descr_div = _make_refsection(None, 'Description')
+ if _add_marked_text(descr_div, struct['descr']):
+ ref_entry.append(descr_div)
+ else:
+ return
+
+
+def build_union_entry(parent, union, depth):
+ build_struct_entry(parent, union, depth, 'union')
+
+def build_function_entry(parent, func, depth):
+ if 'name' not in func:
+ return
+ else:
+ name = func['name']
+
+ if depth < 2:
+ if len(set(['def', 'type', 'brief']) & set(func)) != 3:
+ return
+
+ if 'param' in func:
+ params = func['param']
+ else:
+ params = [{'type':'void', 'declname':''}]
+
+ ref_entry = _make_refentry(parent, "Function %s" % name, name, '3')
+
+ _make_refnamediv(ref_entry, name, _unmarked_text(func['brief']))
+
+ synop_div = _make_refsynopsisdiv(ref_entry, func['type'], name, params)
+
+ if 'descr' in func:
+ descr = func['descr']
+ plist = _make_param_list(None, descr)
+ if plist is not None:
+ _make_para(synop_div, 'Where')
+ _make_para(synop_div).append(plist)
+
+ descr_div = _make_refsection(None, 'Description')
+ if _add_marked_text(descr_div, descr):
+ ref_entry.append(descr_div)
+
+ code = ret = None
+ for d in descr:
+ if 'code' in d:
+ code = d['code']
+ if 'return' in d:
+ ret = d['return']
+ if ret is not None:
+ return_div = _make_refsection(ref_entry, 'Return value')
+ _add_marked_text(return_div, ret)
+ if code is not None:
+ example_div = _make_refsection(ref_entry, 'Example')
+ _make_code(example_div, d['code'])
+ else:
+ if len(set(['def', 'args', 'brief']) & set(func)) != 3:
+ return
+
+ section = _make_section(parent, "%s()" % name, depth+1)
+
+ synopsis = _make_section(section, "SYNOPSIS", depth+2)
+ _make_code(_make_para(synopsis), "%s%s" % (func['def'], func['args']))
+ _make_para(synopsis, "%s() %s" % (name, _unmarked_text(func['brief'])))
+
+ if 'descr' not in func:
+ return
+
+ valid = False
+ descr = _make_section(None, "DESCRIPTION", depth+2)
+ plist = _make_param_list(None, func['descr'])
+ if plist is not None:
+ valid = True
+ descr.append(plist)
+ if _add_marked_text(descr, func['descr']):
+ valid = True
+ if valid:
+ section.append(descr)
+
+#############################################################################
+
+if __name__ == '__main__':
+ _main()
--- /dev/null
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+ Making Doxygen dependencies
+ ===========================
+
+ Usage: doxydeps.py [OPTIONS] [depname] [doxyfile] [dir]
+
+ <depname> - is the dependency name, ie. the dependency file will
+ look like:
+ <depname>: file1.h file2.c ...
+ Depname defaults to basename of <doxyfile>.
+ <doxyfile> - is the path to the doxygen configuration file.
+ The default value is ./Doxyfile
+ <dir> - where the dependency file will be put.
+ The default value is .deps
+ if dir is '--' the dependencies will be printed on stdout
+
+ doxydeps reads the doxygen configuration file and produces a dependency
+ file with identical name + '.P' suffix under dependency directory.
+
+
+ :license: BSD.
+"""
+
+import sys
+import os
+import re
+import glob
+import commands
+import errno
+
+# TODO: get the compilers from autoconf
+c_compiler = "/usr/bin/gcc"
+
+def _main():
+ depname = None
+ doxyfile = 'doxyfile'
+ depdir = '.deps'
+
+ idx = 1
+ for arg in sys.argv[idx:]:
+ if arg == '-h' or arg == '--help':
+ _usage()
+ elif arg.startswith('-'):
+ _usage("invalid option %s" % arg)
+ else:
+ break
+ if len(sys.argv) > idx:
+ depname = sys.argv[idx]
+ idx += 1
+ if len(sys.argv) > idx:
+ doxyfile = sys.argv[idx]
+ idx += 1
+ if len(sys.argv) > idx:
+ depdir = sys.argv[idx]
+ idx += 1
+ if len(sys.argv) > idx:
+ _usage("extra arguments %s" % " ".join(sys.argv[idx:]))
+ if depname == None:
+ depname = os.path.basename(doxyfile)
+
+ if depdir == '--':
+ output = sys.stdout
+ close_output = False
+ else:
+ depfile = "%s.P" % os.path.basename(doxyfile)
+ deppath = os.path.join(depdir, depfile)
+ sys.stderr.write(" DOXYD %s\n" % depfile)
+
+ try:
+ os.makedirs(depdir)
+ except OSError as (errcode, strerror):
+ if errcode != errno.EEXIST:
+ sys.stderr.write("can't create directory '%s': %s\n" %
+ (depdir, strerror))
+ exit(errcode)
+ try:
+ output = open(deppath, "w")
+ except IOError as (errcode, strerror):
+ sys.stderr.write("failed to open '%s': %s\n" %
+ (deppath, strerror))
+ exit(errcode)
+ close_output = True
+
+ deps = DoxygenDependencies(doxyfile)
+ deps.insert(0, "%s:" % depname)
+
+ output.write(" ".join(list("%s \\\n" % d for d in deps))[:-3] + "\n")
+
+ if close_output:
+ output.close()
+
+
+def _usage(errmsg):
+ if len(errmsg) < 1:
+ err = 0
+ else:
+ err = 1
+ sys.stderr.write("Error: " + errmsg + "\n\n")
+ sys.stderr.write(__doc__)
+ sys.exit(err)
+
+
+def DoxygenDependencies(doxyfile):
+ dirs = _parse_doxyfile(doxyfile)
+ files = _find_doxygen_input_files(dirs)
+ deps = files
+ return deps
+
+def _parse_doxyfile(doxyfile):
+ home = os.path.dirname(doxyfile)
+ filt = re.compile(r"(#.*)")
+ inp = []
+ iext = []
+ irec = False
+ exa = []
+ eext = []
+ erec = False
+ try:
+ df = open(doxyfile, "r")
+ except IOError as (errno, strerror):
+ sys.stderr.write("failed to open '%s': %s" % (doxyfile, strerror))
+ exit(errno)
+
+ for line in df.readlines():
+ assign = re.sub(filt, "", line).strip().split('=')
+ if len(assign) != 2:
+ continue
+ key = assign[0].strip().upper()
+ value = assign[1].strip()
+
+ if len(value) < 1:
+ continue
+
+ if key == 'INPUT':
+ inp += value.split(' ')
+ elif key == 'FILE_PATTERNS':
+ iext += value.split(' ')
+ elif key == 'RECURSIVE':
+ if value == 'YES':
+ irec = True
+ elif key == 'EXAMPLE_PATH':
+ exa += value.split(' ')
+ elif key == 'EXAMPLE_PATTERNS':
+ iext += value.split(' ')
+ elif key == 'EXAMPLE_RECURSIVE':
+ if value == 'YES':
+ recurs = True
+
+ df.close()
+
+ dirs = []
+ if len(iext) < 1:
+ iext = ['*']
+ for i in inp:
+ if not i.startswith('/'):
+ i = os.path.join(home, i)
+ dirs.append((os.path.abspath(i), iext, irec))
+ if len(eext) < 1:
+ eext = ['*']
+ for e in exa:
+ if not e.startswith('/'):
+ e = os.path.join(home, e)
+ dirs.append((os.path.abspath(e), eext, erec))
+
+ return dirs
+
+def _find_doxygen_input_files(dirs, includes=[]):
+ allfile = set()
+ for d in dirs:
+ includes.append(os.path.dirname(d[0]))
+ for path, exts, recurse in dirs:
+ if os.path.isfile(path):
+ allfile.add(path)
+ allfile = allfile.union(_find_doxygen_dependencies(path, includes))
+ if not os.path.isdir(path):
+ continue
+ for e in exts:
+ matches = glob.glob(os.path.join(path, e))
+ allfile = allfile.union(matches)
+ for m in matches:
+ allfile = allfile.union(_find_doxygen_dependencies(m,includes))
+ if recurse:
+ for d in os.dirlist(path):
+ if os.path.isdir(d):
+ allfile.union(_find_doxygen_input_files((d,exts,recurse),
+ includes))
+ files = []
+ for f in allfile:
+ if len(f) > 0 and \
+ not f.startswith('/usr/include/') and \
+ not f.startswith('/usr/lib/'):
+ files.append(f)
+ files.sort()
+ return files
+
+def _find_doxygen_dependencies(path, includes):
+ fnam = os.path.basename(path)
+ depgen = "doxygen_%s_dependencies" % fnam[fnam.rfind('.'):][1:]
+ if depgen in globals():
+ return globals()[depgen](path, includes)
+ return set()
+
+def doxygen_c_dependencies(fnam, includes):
+ options = " ".join(list("-I%s" % f for f in includes)) + " -M"
+ cmd = "%s %s %s" % (c_compiler, options, fnam)
+ status, cdeps = commands.getstatusoutput(cmd)
+ if status == 0:
+ deps = re.sub(r" +", " ",
+ re.sub(r"\\\n", "",
+ re.sub(r"^.*\.o: ", "", cdeps)))
+ return set(deps.split(" "))
+ return set()
+
+doxygen_h_dependencies = doxygen_c_dependencies
+
+
+if __name__ == '__main__':
+ _main()
--- /dev/null
+#!/bin/sh
+# From Gerrit Code Review 2.6.1.20130902
+#
+# Part of Gerrit Code Review (http://code.google.com/p/gerrit/)
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+unset GREP_OPTIONS
+
+CHANGE_ID_AFTER="Bug|Issue"
+MSG="$1"
+
+# Check for, and add if missing, a unique Change-Id
+#
+add_ChangeId() {
+ clean_message=`sed -e '
+ /^diff --git a\/.*/{
+ s///
+ q
+ }
+ /^Signed-off-by:/d
+ /^#/d
+ ' "$MSG" | git stripspace`
+ if test -z "$clean_message"
+ then
+ return
+ fi
+
+ # Does Change-Id: already exist? if so, exit (no change).
+ if grep -i '^Change-Id:' "$MSG" >/dev/null
+ then
+ return
+ fi
+
+ id=`_gen_ChangeId`
+ T="$MSG.tmp.$$"
+ AWK=awk
+ if [ -x /usr/xpg4/bin/awk ]; then
+ # Solaris AWK is just too broken
+ AWK=/usr/xpg4/bin/awk
+ fi
+
+ # How this works:
+ # - parse the commit message as (textLine+ blankLine*)*
+ # - assume textLine+ to be a footer until proven otherwise
+ # - exception: the first block is not footer (as it is the title)
+ # - read textLine+ into a variable
+ # - then count blankLines
+ # - once the next textLine appears, print textLine+ blankLine* as these
+ # aren't footer
+ # - in END, the last textLine+ block is available for footer parsing
+ $AWK '
+ BEGIN {
+ # while we start with the assumption that textLine+
+ # is a footer, the first block is not.
+ isFooter = 0
+ footerComment = 0
+ blankLines = 0
+ }
+
+ # Skip lines starting with "#" without any spaces before it.
+ /^#/ { next }
+
+ # Skip the line starting with the diff command and everything after it,
+ # up to the end of the file, assuming it is only patch data.
+ # If more than one line before the diff was empty, strip all but one.
+ /^diff --git a/ {
+ blankLines = 0
+ while (getline) { }
+ next
+ }
+
+ # Count blank lines outside footer comments
+ /^$/ && (footerComment == 0) {
+ blankLines++
+ next
+ }
+
+ # Catch footer comment
+ /^\[[a-zA-Z0-9-]+:/ && (isFooter == 1) {
+ footerComment = 1
+ }
+
+ /]$/ && (footerComment == 1) {
+ footerComment = 2
+ }
+
+ # We have a non-blank line after blank lines. Handle this.
+ (blankLines > 0) {
+ print lines
+ for (i = 0; i < blankLines; i++) {
+ print ""
+ }
+
+ lines = ""
+ blankLines = 0
+ isFooter = 1
+ footerComment = 0
+ }
+
+ # Detect that the current block is not the footer
+ (footerComment == 0) && (!/^\[?[a-zA-Z0-9-]+:/ || /^[a-zA-Z0-9-]+:\/\//) {
+ isFooter = 0
+ }
+
+ {
+ # We need this information about the current last comment line
+ if (footerComment == 2) {
+ footerComment = 0
+ }
+ if (lines != "") {
+ lines = lines "\n";
+ }
+ lines = lines $0
+ }
+
+ # Footer handling:
+ # If the last block is considered a footer, splice in the Change-Id at the
+ # right place.
+ # Look for the right place to inject Change-Id by considering
+ # CHANGE_ID_AFTER. Keys listed in it (case insensitive) come first,
+ # then Change-Id, then everything else (eg. Signed-off-by:).
+ #
+ # Otherwise just print the last block, a new line and the Change-Id as a
+ # block of its own.
+ END {
+ unprinted = 1
+ if (isFooter == 0) {
+ print lines "\n"
+ lines = ""
+ }
+ changeIdAfter = "^(" tolower("'"$CHANGE_ID_AFTER"'") "):"
+ numlines = split(lines, footer, "\n")
+ for (line = 1; line <= numlines; line++) {
+ if (unprinted && match(tolower(footer[line]), changeIdAfter) != 1) {
+ unprinted = 0
+ print "Change-Id: I'"$id"'"
+ }
+ print footer[line]
+ }
+ if (unprinted) {
+ print "Change-Id: I'"$id"'"
+ }
+ }' "$MSG" > "$T" && mv "$T" "$MSG" || rm -f "$T"
+}
+_gen_ChangeIdInput() {
+ echo "tree `git write-tree`"
+ if parent=`git rev-parse "HEAD^0" 2>/dev/null`
+ then
+ echo "parent $parent"
+ fi
+ echo "author `git var GIT_AUTHOR_IDENT`"
+ echo "committer `git var GIT_COMMITTER_IDENT`"
+ echo
+ printf '%s' "$clean_message"
+}
+_gen_ChangeId() {
+ _gen_ChangeIdInput |
+ git hash-object -t commit --stdin
+}
+
+
+del_ChangeId() {
+ T="$MSG.tmp.$$"
+
+ cat "$MSG" | grep -v 'Change-Id: I' > "$T" && mv "$T" "$MSG" || rm -f "$T"
+}
+
+
+gittop=$(while [ ! -d .git -a $(pwd) != "/" ]; do cd ..; done; echo $(pwd))
+
+if [ -f $gittop/.rebase-branch-name ]; then
+ branch=$(cat $gittop/.rebase-branch-name)
+ echo "Taken branch name \"$branch\" from saved branch name file..."
+else
+ branch=$(git branch -l | grep '^\*' | cut -d ' ' -f 2)
+ echo "Using current branch name..."
+fi
+
+case $branch in
+ \(*|-)
+ echo "Can't figure out rebased branch name, not touching anything..."
+ ;;
+ *tizen*)
+ add_ChangeId
+ ;;
+ *)
+ del_ChangeId
+ ;;
+esac
+
+
+exit 0
--- /dev/null
+#!/bin/sh
+
+gittop=$(while [ ! -d .git -a $(pwd) != "/" ]; do cd ..; done; echo $(pwd))
+
+if [ -f $gittop/.rebase-branch-name ]; then
+ rm -f $gittop/.rebase-branch-name
+ echo "Removed saved branch name file..."
+fi
+
+exit 0
--- /dev/null
+#!/bin/bash
+#
+# This is a modified version of the stock git sample pre-commit hook.
+# In addition to the stock whitespace error checks, this one will also
+# reject any commits that try to insert TABs to *.c or *.h files.
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ascii filenames set this variable to true.
+allownonascii=$(git config hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ascii filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+ # Note that the use of brackets around a tr range is ok here, (it's
+ # even required, for portability to Solaris 10's /usr/bin/tr), since
+ # the square bracket bytes happen to fall in the designated range.
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+ echo "Error: Attempt to add a non-ascii file name."
+ echo
+ echo "This can cause problems if you want to work"
+ echo "with people on other platforms."
+ echo
+ echo "To be portable it is advisable to rename the file ..."
+ echo
+ echo "If you know what you are doing you can disable this"
+ echo "check using:"
+ echo
+ echo " git config hooks.allownonascii true"
+ echo
+ exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+git diff-index --check --cached $against --
+status=$?
+
+if [ "$status" != "0" ]; then
+ echo ""
+ echo "WARNING:"
+ echo "WARNING: Your commit would introduce whitespace errors and was"
+ echo "WARNING: hence rejected. Please fix those errors before trying"
+ echo "WARNING: to commit again."
+ echo "WARNING:"
+ exit 1
+fi
+
+# Check if any TABS have been added to .c or .h files...
+file=""
+git diff --cached $against | \
+ while read -r line; do
+ case $line in
+ diff\ --git\ a/*) # 1st diff line, dig out file name
+ file="${line##*b/}"
+ echo "Checking changes to $file..."
+ continue
+ ;;
+ esac
+ case $file in
+ *.h|*.c) ;; # we process C source code,
+ *) continue;; # and skip any other files
+ esac
+ case $line in
+ +*\ *) ;; # we flag insertions containing a TAB,
+ *) continue;; # and skip all other changes
+ esac
+
+ echo "WARNING:"
+ echo "WARNING: In $file: ($line)"
+ echo "WARNING:"
+ echo "WARNING: Your commit would introduce TABS in a *.c or *.h"
+ echo "WARNING: file and was hence rejected. We prefer not to use"
+ echo "WARNING: TABS in source code to avoid TAB-size dependent"
+ echo "WARNING: incorrect indentation to sneak in. Please fix those"
+ echo "WARNING: errors before trying to commit again."
+ echo "WARNING:"
+ exit 1
+ done
+
+# Check if this commit attempts to mix changes to autogenerated files
+# files with changes to ordinary file and give the user a gentle push
+# against the idea...
+auto="`git diff --cached $against | grep ^diff | grep -e -func-info.c`"
+plain="`git diff --cached $against | grep ^diff | grep -v -e -func-info.c`"
+if [ -n "$auto" -a -n "$plain" ]; then
+ echo "WARNING:"
+ echo "WARNING: Your commit tries to mix changes to ordinary and"
+ echo "WARNING: automatically generated files. Doing so makes it"
+ echo "WARNING: more difficult to merge your changes with changes"
+ echo "WARNING: of others working in parallel with you."
+ echo "WARNING:"
+ echo "WARNING: Please consider leaving out the following files"
+ echo "WARNING: from this commit and separating changes to them"
+ echo "WARNING: to a subsequent commit of its own:"
+ echo "WARNING:"
+ git diff --cached $against | grep ^diff | grep func-info.c | \
+ sed 's#^diff .* b/#WARNING: #g'
+ echo "WARNING:"
+ echo "WARNING: In case you really need to commit all of these"
+ echo "WARNING: changes together you can disable this check by"
+ echo "WARNING: passing the -n option to 'git commit'."
+
+ exit 1
+fi
+
+exit $?
--- /dev/null
+#!/bin/sh
+
+# This hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+
+# Dig out and save the name of the branch being rebased for commit-msg hook.
+# There the branch name is used to add gerrit Change-Id footers to branches
+# matching .*tizen.* and remove from any other branches.
+
+branch=$(git branch -l | grep '^\*' | cut -d ' ' -f 2)
+gittop=$(while [ ! -d .git -a $(pwd) != "/" ]; do cd ..; done; echo $(pwd))
+
+case $branch in
+ \(*)
+ echo "-" > $gittop/.rebase-branch-name
+ ;;
+ *)
+ echo "$branch" > $gittop/.rebase-branch-name
+ echo "Saved branch name \"$branch\" for commit-msg hook..."
+ ;;
+esac
+
+exit 0
--- /dev/null
+dnl Initiate the documentation directories
+dnl
+dnl MRP_DOCINIT([depdir],[doxyfilename],[docdir])
+dnl
+
+AC_DEFUN([MRP_DOCINIT],
+[
+ m4_ifset([$1], [depdir=$1], [depdir=.deps])
+ m4_ifset([$2], [doxyfile=$1], [doxyfile=Doxyfile])
+ m4_ifset([$3], [docdir=$2], [docdir=doc])
+
+ AC_PATH_TOOL( [MRP_FIND], find )
+ AC_PROG_SED
+ AC_PROG_MKDIR_P
+
+ AS_IF( [ test "x$MRP_FIND" = "x" -o "x$MKDIR_P" = "x" ],
+ [ AC_MSG_ERROR([essential programs are missing to init docs]) ],
+ [ found=`$MRP_FIND $docdir -name $doxyfile`
+ for f in $found ; do
+ $MKDIR_P `echo $f | $SED -e "s/$doxyfile//"`$depdir
+ done])
+])
--- /dev/null
+dnl Make automake/libtool output more friendly to humans
+dnl
+dnl Copyright (c) 2009, Damien Lespiau <damien.lespiau@gmail.com>
+dnl
+dnl Permission is hereby granted, free of charge, to any person
+dnl obtaining a copy of this software and associated documentation
+dnl files (the "Software"), to deal in the Software without
+dnl restriction, including without limitation the rights to use,
+dnl copy, modify, merge, publish, distribute, sublicense, and/or sell
+dnl copies of the Software, and to permit persons to whom the
+dnl Software is furnished to do so, subject to the following
+dnl conditions:
+dnl
+dnl The above copyright notice and this permission notice shall be
+dnl included in all copies or substantial portions of the Software.
+dnl
+dnl THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+dnl EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+dnl OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+dnl NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+dnl HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+dnl WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+dnl FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+dnl OTHER DEALINGS IN THE SOFTWARE.
+dnl
+dnl SHAVE_INIT([shavedir],[default_mode])
+dnl
+dnl shavedir: the directory where the shave scripts are, it defaults to
+dnl $(top_builddir)
+dnl default_mode: (enable|disable) default shave mode. This parameter
+dnl controls shave's behaviour when no option has been
+dnl given to configure. It defaults to disable.
+dnl
+dnl * SHAVE_INIT should be called late in your configure.(ac|in) file (just
+dnl before AC_CONFIG_FILE/AC_OUTPUT is perfect. This macro rewrites CC and
+dnl LIBTOOL, you don't want the configure tests to have these variables
+dnl re-defined.
+dnl * This macro requires GNU make's -s option.
+
+AC_DEFUN([_SHAVE_ARG_ENABLE],
+[
+ AC_ARG_ENABLE([shave],
+ AS_HELP_STRING(
+ [--enable-shave],
+ [use shave to make the build pretty [[default=$1]]]),,
+ [enable_shave=$1]
+ )
+])
+
+AC_DEFUN([SHAVE_INIT],
+[
+ dnl you can tweak the default value of enable_shave
+ m4_if([$2], [enable], [_SHAVE_ARG_ENABLE(yes)], [_SHAVE_ARG_ENABLE(no)])
+
+ if test x"$enable_shave" = xyes; then
+ dnl where can we find the shave scripts?
+ m4_if([$1],,
+ [shavedir="$ac_pwd"],
+ [shavedir="$ac_pwd/$1"])
+ AC_SUBST(shavedir)
+
+ dnl make is now quiet
+ AC_SUBST([MAKEFLAGS], [-s])
+ AC_SUBST([AM_MAKEFLAGS], ['`test -z $V && echo -s`'])
+
+ dnl we need sed
+ AC_CHECK_PROG(SED,sed,sed,false)
+
+ dnl substitute libtool
+ SHAVE_SAVED_LIBTOOL=$LIBTOOL
+ LIBTOOL="${SHELL} ${shavedir}/shave-libtool '${SHAVE_SAVED_LIBTOOL}'"
+ AC_SUBST(LIBTOOL)
+
+ dnl substitute cc/cxx
+ SHAVE_SAVED_CCAS=$CCAS
+ SHAVE_SAVED_CC=$CC
+ SHAVE_SAVED_CXX=$CXX
+ SHAVE_SAVED_FC=$FC
+ SHAVE_SAVED_F77=$F77
+ SHAVE_SAVED_OBJC=$OBJC
+ SHAVE_SAVED_MCS=$MCS
+ SHAVE_SAVED_LEX=$LEX
+ SHAVE_SAVED_YACC=$YACC
+ SHAVE_SAVED_CC_FOR_BUILD=$CC_FOR_BUILD
+ CCAS="${SHELL} ${shavedir}/shave ccas ${SHAVE_SAVED_CCAS}"
+ CC="${SHELL} ${shavedir}/shave cc ${SHAVE_SAVED_CC}"
+ CXX="${SHELL} ${shavedir}/shave cxx ${SHAVE_SAVED_CXX}"
+ FC="${SHELL} ${shavedir}/shave fc ${SHAVE_SAVED_FC}"
+ F77="${SHELL} ${shavedir}/shave f77 ${SHAVE_SAVED_F77}"
+ OBJC="${SHELL} ${shavedir}/shave objc ${SHAVE_SAVED_OBJC}"
+ MCS="${SHELL} ${shavedir}/shave mcs ${SHAVE_SAVED_MCS}"
+ LEX="${SHELL} ${shavedir}/shave lex ${SHAVE_SAVED_LEX}"
+ YACC="${SHELL} ${shavedir}/shave yacc ${SHAVE_SAVED_YACC}"
+ CC_FOR_BUILD="${SHELL} ${shavedir}/shave cc_for_build ${SHAVE_SAVED_CC_FOR_BUILD}"
+ AC_SUBST(CCAS)
+ AC_SUBST(CC)
+ AC_SUBST(CXX)
+ AC_SUBST(FC)
+ AC_SUBST(F77)
+ AC_SUBST(OBJC)
+ AC_SUBST(MCS)
+ AC_SUBST(LEX)
+ AC_SUBST(YACC)
+
+ V=@
+ else
+ V=1
+ fi
+ Q='$(V:1=)'
+ AC_SUBST(V)
+ AC_SUBST(Q)
+])
+
--- /dev/null
+# Macro to check if websockets support was enabled. This macro also
+# takes care of detecting older versions of libwebsockets (lacking
+# pkg-config support, no per-context userdata) and propagating this
+# information to config.h and the compilation process.
+#
+
+AC_DEFUN([CHECK_WEBSOCKETS],
+[
+AC_LANG_PUSH([C])
+AC_ARG_ENABLE(websockets,
+ [ --enable-websockets enable websockets support],
+ [enable_websockets=$enableval], [enable_websockets=auto])
+
+# Check if we have properly packaged libwebsockets (json-c is now mandatory
+# and already has been tested for).
+if test "$enable_websockets" != "no"; then
+ PKG_CHECK_MODULES(WEBSOCKETS, [libwebsockets],
+ [have_websockets=yes], [have_websockets=no])
+ if test "$have_websockets" = "yes"; then
+ WEBSOCKETS_CFLAGS="`pkg-config --cflags libwebsockets`"
+ # Check for a couple of recent features we need to adopt to.
+ saved_CFLAGS="$CFLAGS"
+ saved_LDFLAGS="$LDFLAGS"
+ saved_LIBS="$LIBS"
+ # Note that (at least with autoconf 2.69 and gcc 4.7.2), setting
+ # LD_AS_NEEDED to 1 breaks AC_LINK_IFELSE. That macro generates
+ # the compilation command so that the libraries are specified
+ # before the generated C source so all referenced/tested symbols
+ # from any of the libraries end up being undefined. This fools
+ # AC_LINK_IFELSE to consider the test a failure and select the
+ # else branch.
+ # rpmbuild always sets LD_AS_NEEDED to 1. To work around this save
+ # and restore LD_AS_NEEDED for the duration of the AC_LINK_IFELSE
+ # tests.
+ saved_LD_AS_NEEDED="$LD_AS_NEEDED"
+ unset LD_AS_NEEDED
+ CFLAGS="`pkg-config --cflags libwebsockets`"
+ LIBS="`pkg-config --libs libwebsockets`"
+
+ # Check for new context creation API.
+ AC_MSG_CHECKING([for WEBSOCKETS new context creation API])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[struct libwebsocket_context *ctx;
+ ctx = libwebsocket_create_context(NULL);]])],
+ [websockets_cci=yes],
+ [websockets_cci=no])
+ AC_MSG_RESULT([$websockets_cci])
+
+ # Check for new libwebsockets_get_internal_extensions.
+ AC_MSG_CHECKING([for WEBSOCKETS internal extension query API])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[struct libwebsocket_extension *ext;
+ ext = libwebsocket_get_internal_extensions();]])],
+ [websockets_query_ext=yes],
+ [websockets_query_ext=no])
+ AC_MSG_RESULT([$websockets_query_ext])
+
+ # Check for newer lws_set_log_level API.
+ # Note that we cheat heavily here: instead of rolling a proper
+ # test, we blindly assume gcc, turn on the -Werror flag (to catch
+ # calls with a mismatching function pointer) and hope that we will
+ # not get false negatives because of other warnings.
+ no_werror_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -Werror"
+ AC_MSG_CHECKING([for WEBSOCKETS updated logging API.])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>
+ static void logger(int level, const char *line) {
+ return;
+ }]],
+ [[lws_set_log_level(LLL_INFO, logger);]])],
+ [websockets_log_with_level=yes],
+ [websockets_log_with_level=no])
+ AC_MSG_RESULT([$websockets_log_with_level])
+
+ # Check whether we have libwebsocket_close_and_free_session.
+ AC_MSG_CHECKING([for WEBSOCKETS close_and_free_session API])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[libwebsocket_close_and_free_session(NULL, NULL, 0);]])],
+ [websockets_close_session=yes],
+ [websockets_close_session=no])
+ AC_MSG_RESULT([$websockets_close_session])
+
+ # Check for LWS_CALLBACK_FILTER_HTTP_CONNECTION.
+ AC_MSG_CHECKING([for WEBSOCKETS LWS_CALLBACK_FILTER_HTTP_CONNECTION])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[int foo = LWS_CALLBACK_FILTER_HTTP_CONNECTION;]])],
+ [websockets_filter_http_connection=yes],
+ [websockets_filter_http_connection=no])
+ AC_MSG_RESULT([$websockets_filter_http_connection])
+
+ # Check for LWS_CALLBACK_CHANGE_MODE_POLL_FD.
+ AC_MSG_CHECKING([for WEBSOCKETS LWS_CALLBACK_CHANGE_MODE_POLL_FD])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[int foo = LWS_CALLBACK_CHANGE_MODE_POLL_FD;]])],
+ [websockets_change_poll=yes],
+ [websockets_change_poll=no])
+ AC_MSG_RESULT([$websockets_change_poll])
+
+ # Check the signature of libwebsockets_serve_http_file to see if
+ # it takes an extra (other_headers) argument.
+ AC_MSG_CHECKING([for WEBSOCKETS libwebsockets_serve_http_file other_headers argument])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[return libwebsockets_serve_http_file(NULL, NULL, "", "", "");]])],
+ [websockets_serve_file_extraarg=yes],
+ [websockets_serve_file_extraarg=no])
+ AC_MSG_RESULT([$websockets_serve_file_extraarg])
+
+ CFLAGS="$saved_CFLAGS"
+ LDFLAGS="$saved_LDFLAGS"
+ LIBS="$saved_LIBS"
+ if test -n "$saved_LD_AS_NEEDED"; then
+ export LD_AS_NEEDED="$saved_LD_AS_NEEDED"
+ fi
+ else
+ WEBSOCKETS_CFLAGS=""
+ fi
+
+ # Check for older websockets.
+ if test "$have_websockets" = "no"; then
+ saved_LDFLAGS="$LDFLAGS"
+ saved_LIBS="$LIBS"
+ LIBS="-lwebsockets"
+ AC_MSG_CHECKING([for WEBSOCKETS without pkg-config support])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[struct libwebsocket_context *ctx;
+ ctx = libwebsocket_create_context(0, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ -1, -1, 0, NULL);]])],
+ [have_websockets=yes;old_websockets=no],
+ [have_websockets=no])
+ AC_MSG_RESULT([$have_websockets])
+ fi
+
+ # Check if we have a really old libwebsockets, still without
+ # per-context user data.
+ if test "$old_websockets" != "no"; then
+ AC_MSG_CHECKING([for really old WEBSOCKETS])
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stdlib.h>
+ #include <libwebsockets.h>]],
+ [[struct libwebsocket_context *ctx;
+ ctx = libwebsocket_create_context(0, NULL, NULL, NULL,
+ NULL, NULL,
+ -1, -1, 0);]])],
+ [have_websockets=yes;old_websockets=yes],
+ [old_websockets=no])
+ AC_MSG_RESULT([$old_websockets])
+ fi
+
+ WEBSOCKETS_LIBS="-lwebsockets"
+ if test "$old_websockets" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_OLD"
+ fi
+ if test "$websockets_cci" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_CONTEXT_INFO"
+ fi
+ if test "$websockets_query_ext" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_QUERY_EXTENSIONS"
+ fi
+ if test "$websockets_log_with_level" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_LOG_WITH_LEVEL"
+ fi
+ if test "$websockets_close_session" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_CLOSE_SESSION"
+ fi
+ if test "$websockets_filter_http_connection" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_FILTER_HTTP_CONNECTION"
+ fi
+ if test "$websockets_change_poll" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_CHANGE_MODE_POLL_FD"
+ fi
+ if test "$websockets_serve_file_extraarg" = "yes"; then
+ WEBSOCKETS_CFLAGS="$WEBSOCKETS_CFLAGS -DWEBSOCKETS_SERVE_FILE_EXTRAARG"
+ fi
+
+ LDFLAGS="$saved_LDFLAGS"
+ LIBS="$saved_LIBS"
+else
+ AC_MSG_NOTICE([libwebsockets support is disabled.])
+fi
+
+# Bail out if we lack mandatory support.
+if test "$enable_websockets" = "yes" -a "$have_websockets" = "no"; then
+ AC_MSG_ERROR([libwebsockets development libraries not found.])
+fi
+
+# Enable if found and autosupport requested.
+if test "$enable_websockets" = "auto"; then
+ enable_websockets=$have_websockets
+fi
+
+# If enabled, set up our autoconf variables accordingly.
+if test "$enable_websockets" = "yes"; then
+ AC_DEFINE([WEBSOCKETS_ENABLED], 1, [Enable websockets support ?])
+ if test "$old_websockets" = "yes"; then
+ AC_DEFINE([WEBSOCKETS_OLD], 1, [No per-context userdata ?])
+ fi
+fi
+
+# Finally substitute everything.
+AM_CONDITIONAL(WEBSOCKETS_ENABLED, [test "$enable_websockets" = "yes"])
+AM_CONDITIONAL(WEBSOCKETS_OLD, [test "$old_websockets" = "yes"])
+AC_SUBST(WEBSOCKETS_ENABLED)
+AC_SUBST(WEBSOCKETS_CFLAGS)
+AC_SUBST(WEBSOCKETS_LIBS)
+AC_SUBST(WEBSOCKETS_OLD)
+
+AC_LANG_POP
+])
--- /dev/null
+load-plugin lua config="/etc/murphy/murphy.lua"
--- /dev/null
+[Path]
+PathExists=/run/user/%U/wayland-0
+Unit=ico-homescreen.service
--- /dev/null
+with_system_controller = false
+with_amb = false
+verbose = 0
+
+m = murphy.get()
+
+-- try loading the various logging plugins
+m:try_load_plugin('systemd')
+m:try_load_plugin('dlog')
+
+-- load the console plugin
+m:try_load_plugin('console')
+
+m:try_load_plugin('console.disabled', 'webconsole', {
+ address = 'wsck:127.0.0.1:3000/murphy',
+ httpdir = '/usr/share/murphy/webconsole' });
+
+-- load the dbus plugin
+if m:plugin_exists('dbus') then
+ m:load_plugin('dbus')
+end
+
+-- load the native resource plugin
+if m:plugin_exists('resource-native') then
+ m:load_plugin('resource-native')
+ m:info("native resource plugin loaded")
+else
+ m:info("No native resource plugin found...")
+end
+
+-- load the dbus resource plugin
+m:try_load_plugin('resource-dbus', {
+ dbus_bus = "system",
+ dbus_service = "org.Murphy",
+ dbus_track = true,
+ default_zone = "driver",
+ default_class = "implicit"
+})
+
+-- load the domain control plugin
+if m:plugin_exists('domain-control') then
+ m:load_plugin('domain-control')
+else
+ m:info("No domain-control plugin found...")
+end
+
+if m:plugin_exists('glib') then
+ m:load_plugin('glib')
+else
+ m:info("No glib plugin found...")
+end
+
+if m:plugin_exists("gam-resource-manager") then
+
+function get_general_priorities(self)
+ print("*** get_general_priorities\n")
+ return { "USB Headset", "wiredHeadset", "speakers" }
+end
+
+function get_phone_priorities(self)
+ print("*** get_phone_priorities\n")
+ return { "wiredHeadset", "USB Headset" }
+end
+
+m:load_plugin('gam-resource-manager', {
+ config_dir = '/etc/murphy/gam',
+ decision_names = 'gam-wrtApplication-4',
+ max_active = 4,
+ app_mapping = {
+ ['t8j6HTRpuz.MediaPlayer'] = 'wrtApplication',
+ ['pacat'] = 'icoApplication'
+ },
+ app_default = 'icoApplication'
+})
+
+routing_sink_priority {
+ application_class = "player",
+ priority_queue = get_general_priorities
+}
+
+routing_sink_priority {
+ application_class = "game",
+ priority_queue = get_general_priorities
+}
+
+routing_sink_priority {
+ application_class = "implicit",
+ priority_queue = get_general_priorities
+}
+
+routing_sink_priority {
+ application_class = "phone",
+ priority_queue = get_phone_priorities
+}
+
+routing_sink_priority {
+ application_class = "basic",
+ priority_queue = get_phone_priorities
+}
+
+routing_sink_priority {
+ application_class = "event",
+ priority_queue = get_phone_priorities
+}
+end
+
+-- load the AMB plugin
+if m:plugin_exists('amb') then
+ m:try_load_plugin('amb')
+
+ if builtin.method.amb_initiate and
+ builtin.method.amb_update
+ then
+ with_amb = true
+ end
+else
+ m:info("No amb plugin found...")
+end
+
+-- load the ASM resource plugin
+if m:plugin_exists('resource-asm') then
+ m:try_load_plugin('resource-asm', {
+ zone = "driver",
+ share_mmplayer = "player:AVP,mandatory,exclusive,strict",
+ ignored_argv0 = "WebProcess"
+ })
+else
+ m:info("No audio session manager plugin found...")
+end
+
+if m:plugin_exists('system-controller') then
+ with_system_controller = true
+elseif m:plugin_exists('ivi-resource-manager') then
+ m:load_plugin('ivi-resource-manager')
+ with_system_controller = false
+end
+
+-- define application classes
+application_class {
+ name = "interrupt",
+ priority = 99,
+ modal = true ,
+ share = false,
+ order = "fifo"
+}
+
+application_class {
+ name = "emergency",
+ priority = 80,
+ modal = false,
+ share = false,
+ order = "fifo"
+}
+application_class {
+ name = "system",
+ priority = 52,
+ modal = false,
+ share = true,
+ order = "lifo"
+}
+application_class {
+ name = "alert",
+ priority = 51,
+ modal = false,
+ share = false,
+ order = "fifo"
+}
+
+application_class {
+ name = "navigator",
+ priority = 50,
+ modal = false,
+ share = true,
+ order = "fifo"
+}
+
+application_class {
+ name = "phone",
+ priority = 6 ,
+ modal = false,
+ share = false,
+ order = "lifo"
+}
+application_class {
+ name = "camera",
+ priority = 5,
+ modal = false,
+ share = false,
+ order = "lifo"
+}
+
+application_class { name="event" , priority=4 , modal=false, share=true , order="fifo" }
+application_class { name="game" , priority=3 , modal=false, share=false, order="lifo" }
+--# doesn't need to be created here, ivi-resource-manager creates it if loaded
+--#application_class { name="basic" , priority=2 , modal=false, share=false, order="lifo" }
+application_class { name="player" , priority=1 , modal=false, share=true , order="lifo" }
+application_class { name="implicit" , priority=0 , modal=false, share=false, order="lifo" }
+
+-- define zone attributes
+zone.attributes {
+ type = {mdb.string, "common", "rw"},
+ location = {mdb.string, "anywhere", "rw"}
+}
+
+-- define zones
+zone {
+ name = "driver",
+ attributes = {
+ type = "common",
+ location = "front-left"
+ }
+}
+
+zone {
+ name = "passanger1",
+ attributes = {
+ type = "private",
+ location = "front-right"
+ }
+}
+
+zone {
+ name = "passanger2",
+ attributes = {
+ type = "private",
+ location = "back-left"
+ }
+}
+
+zone {
+ name = "passanger3",
+ attributes = {
+ type = "private",
+ location = "back-right"
+ }
+}
+
+zone {
+ name = "passanger4",
+ attributes = {
+ type = "private",
+ location = "back-left"
+ }
+}
+
+
+-- define resource classes
+if not m:plugin_exists('ivi-resource-manager') and
+ not with_system_controller and
+ not m:plugin_exists('gam-resource-manager')
+then
+ resource.class {
+ name = "audio_playback",
+ shareable = true,
+ attributes = {
+ role = { mdb.string, "music", "rw" },
+ pid = { mdb.string, "<unknown>", "rw" },
+ policy = { mdb.string, "relaxed", "rw" },
+ source = { mdb.string, "webkit", "rw" },
+ conn_id = { mdb.unsigned, 0, "rw" },
+ name = { mdb.string, "<unknown>", "rw" },
+ }
+ }
+end
+
+if not m:plugin_exists('gam-resource-manager') then
+ resource.class {
+ name = "audio_recording",
+ shareable = true,
+ attributes = {
+ role = { mdb.string, "music" , "rw" },
+ pid = { mdb.string, "<unknown>", "rw" },
+ policy = { mdb.string, "relaxed" , "rw" },
+ name = { mdb.string, "<unknown>", "rw" },
+ }
+ }
+end
+
+resource.class {
+ name = "video_playback",
+ shareable = false,
+}
+
+resource.class {
+ name = "video_recording",
+ shareable = false
+}
+
+resource.class {
+ name = "speech_recognition",
+ shareable = true
+}
+
+resource.class {
+ name = "speech_synthesis",
+ shareable = true
+}
+
+-- PulseAudio volume context
+mdb.table {
+ name = "volume_context",
+ index = { "id" },
+ create = true,
+ columns = {
+ { "id", mdb.unsigned },
+ { "value", mdb.string, 64 },
+ }
+}
+
+-- put default volume context to the table
+mdb.table.volume_context:insert({ id = 1, value = "default" })
+
+if not m:plugin_exists('ivi-resource-manager') and
+ not with_system_controller
+then
+ resource.method.veto = {
+ function(zone, rset, grant, owners, req_set)
+ return true
+ end
+ }
+end
+
+-- test for creating selections
+mdb.select {
+ name = "audio_owner",
+ table = "audio_playback_owner",
+ columns = {"application_class"},
+ condition = "zone_name = 'driver'"
+}
+
+mdb.select {
+ name = "vehicle_speed",
+ table = "amb_vehicle_speed",
+ columns = {"value"},
+ condition = "key = 'VehicleSpeed'"
+}
+
+element.lua {
+ name = "speed2volume",
+ inputs = { speed = mdb.select.vehicle_speed, param = 9 },
+ outputs = { mdb.table { name = "speedvol",
+ index = {"zone", "device"},
+ columns = {{"zone", mdb.string, 16},
+ {"device", mdb.string, 16},
+ {"value", mdb.floating}},
+ create = true
+ }
+ },
+ oldvolume = 0.0,
+ update = function(self)
+ speed = self.inputs.speed.single_value
+ if (speed) then
+ volume = (speed - 144.0) / 7.0
+ else
+ volume = 0.0
+ end
+ diff = volume - self.oldvolume
+ if (diff*diff > self.inputs.param) then
+ print("*** element "..self.name.." update "..volume)
+ self.oldvolume = volume
+ mdb.table.speedvol:replace({zone = "driver", device = "speakers", value = volume})
+ end
+ end
+}
+
+mdb.select {
+ name = "amb_state",
+ table = "amb_state",
+ columns = { "state" },
+ condition = "id = 0"
+}
+
+-- Night mode processing chain
+
+mdb.select {
+ name = "exterior_brightness",
+ table = "amb_exterior_brightness",
+ columns = { "value" },
+ condition = "key = 'ExteriorBrightness'"
+}
+
+element.lua {
+ name = "nightmode",
+ inputs = { brightness = mdb.select.exterior_brightness },
+ oldmode = -1;
+ outputs = {
+ mdb.table {
+ name = "amb_nightmode",
+ index = { "id" },
+ create = true,
+ columns = {
+ { "id", mdb.unsigned },
+ { "night_mode", mdb.unsigned }
+ }
+ }
+ },
+ update = function(self)
+ -- This is a trivial function to calculate night mode. Later, we will
+ -- need a better threshold value and hysteresis to prevent oscillation.
+
+ brightness = self.inputs.brightness.single_value
+
+ if not brightness then
+ return
+ end
+
+ print("*** element "..self.name.." update brightness: "..brightness)
+
+ if brightness > 300 then
+ mode = 0
+ else
+ mode = 1
+ end
+
+ print("*** resulting mode: ".. mode)
+
+ if not (mode == self.oldmode) then
+ mdb.table.amb_nightmode:replace({ id = 0, night_mode = mode })
+ end
+
+ self.oldmode = mode
+ end
+}
+
+mdb.select {
+ name = "select_night_mode",
+ table = "amb_nightmode",
+ columns = { "night_mode" },
+ condition = "id = 0"
+}
+
+if with_amb then
+ sink.lua {
+ name = "night_mode",
+ inputs = { NightMode = mdb.select.select_night_mode,
+ amb_state = mdb.select.amb_state },
+ property = "NightMode",
+ type = "b",
+ initiate = builtin.method.amb_initiate,
+ update = builtin.method.amb_update
+ }
+end
+
+-- Night mode general handlers
+
+if with_system_controller then
+ sink.lua {
+ name = "nightmode_homescreen",
+ inputs = { owner = mdb.select.select_night_mode },
+ initiate = function(self)
+ -- data = mdb.select.select_night_mode.single_value
+ return true
+ end,
+ update = function(self)
+ send_night_mode_to(homescreen)
+ end
+ }
+end
+
+-- Driving mode processing chain
+
+element.lua {
+ name = "drivingmode",
+ inputs = { speed = mdb.select.vehicle_speed },
+ oldmode = -1;
+ outputs = {
+ mdb.table {
+ name = "amb_drivingmode",
+ index = { "id" },
+ create = true,
+ columns = {
+ { "id", mdb.unsigned },
+ { "driving_mode", mdb.unsigned }
+ }
+ }
+ },
+ update = function(self)
+
+ speed = self.inputs.speed.single_value
+
+ if not speed then
+ return
+ end
+
+ if speed == 0 then
+ mode = 0
+ else
+ mode = 1
+ end
+
+ if not (mode == self.oldmode) then
+ mdb.table.amb_drivingmode:replace({ id = 0, driving_mode = mode })
+ end
+
+ self.oldmode = mode
+ end
+}
+
+mdb.select {
+ name = "select_driving_mode",
+ table = "amb_drivingmode",
+ columns = { "driving_mode" },
+ condition = "id = 0"
+}
+
+if with_amb then
+ sink.lua {
+ name = "driving_mode",
+ inputs = { DrivingMode = mdb.select.select_driving_mode,
+ amb_state = mdb.select.amb_state },
+ property = "DrivingMode",
+ type = "u",
+ initiate = builtin.method.amb_initiate,
+ update = builtin.method.amb_update
+ }
+end
+
+-- turn signals (left, right)
+
+mdb.select {
+ name = "winker",
+ table = "amb_turn_signal",
+ columns = { "value" },
+ condition = "key = 'TurnSignal'"
+}
+
+-- define three categories
+
+mdb.select {
+ name = "undefined_applications",
+ table = "aul_applications",
+ columns = { "appid" },
+ condition = "category = '<undefined>'"
+}
+
+mdb.select {
+ name = "basic_applications",
+ table = "aul_applications",
+ columns = { "appid" },
+ condition = "category = 'basic'"
+}
+
+mdb.select {
+ name = "entertainment_applications",
+ table = "aul_applications",
+ columns = { "appid" },
+ condition = "category = 'entertainment'"
+}
+
+function ft(t)
+ -- filter the object garbage out of the tables
+ ret = {}
+
+ for k,v in pairs(t) do
+ if k ~= "userdata" and k ~= "new" then
+ ret[k] = v
+ end
+ end
+
+ return ret
+end
+
+function getApplication(appid)
+ local conf = nil
+
+ -- find the correct local application definition
+
+ for k,v in pairs(ft(application)) do
+ if appid == v.appid then
+ conf = v
+ break
+ end
+ end
+
+ return conf
+end
+
+function regulateApplications(t, regulation)
+ for k,v in pairs(ft(t)) do
+
+ whitelisted = false
+
+ -- our local application config, which takes precedence
+ local conf = getApplication(v.appid)
+
+ if conf then
+ if conf.resource_class ~= "player" then
+ whitelisted = true
+ end
+
+ if conf.requisites and conf.requisites.screen then
+ if conf.requisites.screen.driving then
+ whitelisted = true
+ end
+ end
+ end
+
+ if whitelisted then
+ -- override, don't disable
+ resmgr:disable_screen_by_appid("*", "*", v.appid, false, false)
+ else
+ resmgr:disable_screen_by_appid("*", "*", v.appid, regulation == 1, false)
+ end
+ end
+ resource.method.recalc("driver")
+end
+
+-- regulation (on), use "select_driving_mode"
+
+sink.lua {
+ name = "driving_regulation",
+ inputs = { owner = mdb.select.select_driving_mode },
+ initiate = function(self)
+ -- local data = mdb.select.select_driving_mode.single_value
+ return true
+ end,
+ update = function(self)
+ local data = mdb.select.select_driving_mode.single_value
+
+ if verbose > 1 then
+ print("Driving mode updated: " .. tostring(data))
+ end
+
+ if not sc then
+ return true
+ end
+
+ -- tell homescreen that driving mode was updated
+ send_driving_mode_to(homescreen)
+
+ regulateApplications(ft(mdb.select.entertainment_applications), data)
+ regulateApplications(ft(mdb.select.undefined_applications), data)
+
+ return true
+ end
+}
+--[[
+sink.lua {
+ name = "regulated_app_change",
+ inputs = { undef = mdb.select.undefined_applications,
+ entertainment = mdb.select.entertainment_applications },
+ initiate = function(self)
+ return true
+ end,
+ update = function(self)
+ local data = mdb.select.select_driving_mode.single_value
+
+ if not sc then
+ return
+ end
+
+ if verbose > 1 then
+ print("regulated application list was changed")
+ end
+
+ regulateApplications(ft(mdb.select.entertainment_applications), data)
+ regulateApplications(ft(mdb.select.undefined_applications), data)
+
+ return true
+ end
+}
+--]]
+
+-- shift position (parking, reverse, other)
+
+mdb.select {
+ name = "gear_position",
+ table = "amb_gear_position",
+ columns = { "value" },
+ condition = "key = 'GearPosition'"
+}
+
+-- cameras (back, front, left, right)
+
+element.lua {
+ name = "camera_state",
+ inputs = { winker = mdb.select.winker, gear = mdb.select.gear_position },
+ oldmode = -1;
+ outputs = {
+ mdb.table {
+ name = "target_camera_state",
+ index = { "id" },
+ create = true,
+ columns = {
+ { "id", mdb.unsigned },
+ { "front_camera", mdb.unsigned },
+ { "back_camera", mdb.unsigned },
+ { "right_camera", mdb.unsigned },
+ { "left_camera", mdb.unsigned }
+ }
+ }
+ },
+ update = function(self)
+
+ front_camera = 0
+ back_camera = 0
+ right_camera = 0
+ left_camera = 0
+
+ if self.inputs.gear == 128 then
+ back_camera = 1
+ elseif self.inputs.winker == 1 then
+ right_camera = 1
+ elseif self.inputs.winker == 2 then
+ left_camera = 1
+ end
+
+ mdb.table.target_camera_state:replace({ id = 0, front_camera = front_camera, back_camera = back_camera, right_camera = right_camera, left_camera = left_camera })
+
+ end
+}
+
+-- system controller test setup
+
+if not with_system_controller then
+ -- ok, we should have 'audio_playback' defined by now
+ m:try_load_plugin('telephony')
+ return
+end
+
+m:load_plugin('system-controller')
+
+onscreen_counter = 0
+
+window_manager_operation_names = {
+ [1] = "create",
+ [2] = "destroy"
+}
+
+function window_manager_operation_name(oper)
+ local name = window_manager_operation_names[oper]
+ if name then return name end
+ return "<unknown " .. tostring(oper) .. ">"
+end
+
+window_operation_names = {
+ [1] = "create",
+ [2] = "destroy",
+ [3] = "name_change",
+ [4] = "visible",
+ [5] = "configure",
+ [6] = "active",
+ [7] = "map",
+ [8] = "hint"
+}
+
+function window_operation_name(oper)
+ local name = window_operation_names[oper]
+ if name then return name end
+ return "<unknown " .. tostring(oper) .. ">"
+end
+
+layer_operation_names = {
+ [1] = "create",
+ [2] = "destroy",
+ [3] = "visible"
+}
+
+function layer_operation_name(oper)
+ local name = layer_operation_names[oper]
+ if name then return name end
+ return "<unknown " .. tostring(oper) .. ">"
+end
+
+input_manager_operation_names = {
+ [1] = "create",
+ [2] = "destroy",
+ [3] = "ready"
+}
+
+function input_manager_operation_name(oper)
+ local name = input_manager_operation_names[oper]
+ if name then return name end
+ return "<unknown " .. tostring(oper) .. ">"
+end
+
+input_operation_names = {
+ [1] = "create",
+ [2] = "destroy",
+ [3] = "update"
+}
+
+function input_operation_name(oper)
+ local name = input_operation_names[oper]
+ if name then return name end
+ return "<unknown " .. tostring(oper) .. ">"
+end
+
+code_operation_names = {
+ [1] = "create",
+ [2] = "destroy",
+ [3] = "state_change"
+}
+
+function code_operation_name(oper)
+ local name = code_operation_names[oper]
+ if name then return name end
+ return "<unknown " .. tostring(oper) .. ">"
+end
+
+command_names = {
+ [0x00001] = "send_appid",
+ [0x10001] = "create",
+ [0x10002] = "destroy",
+ [0x10003] = "show",
+ [0x10004] = "hide",
+ [0x10005] = "move",
+ [0x10006] = "animation",
+ [0x10007] = "change_active",
+ [0x10008] = "change_layer",
+ [0x10009] = "change_attr",
+ [0x10010] = "name",
+ [0x10020] = "map_thumb",
+ [0x10021] = "unmap_thumb",
+ [0x10022] = "map_get",
+ [0x10030] = "show layer",
+ [0x10031] = "hide_layer",
+ [0x10032] = "change_layer_attr",
+ [0x20001] = "add_input",
+ [0x20002] = "del_input",
+ [0x30001] = "change_user",
+ [0x30002] = "get_userlist",
+ [0x30003] = "get_lastinfo",
+ [0x30004] = "set_lastinfo",
+ [0x40001] = "acquire_res",
+ [0x40002] = "release_res",
+ [0x40003] = "deprive_res",
+ [0x40004] = "waiting_res",
+ [0x40005] = "revert_res",
+ [0x40006] = "window_id_res",
+ [0x40011] = "create_res",
+ [0x40012] = "destroy_res",
+ [0x50001] = "set_region",
+ [0x50002] = "unset_region",
+ [0x60001] = "change_state"
+}
+
+function command_name(command)
+ local name = command_names[command]
+ if name then return name end
+ return "<unknown " .. tostring(command) .. ">"
+end
+
+input_layer = {
+ [101] = true, -- input
+ [102] = true, -- touch
+ [103] = true -- cursor
+}
+
+-- some day this should be merged with wmgr.layers
+ico_layer_type = {
+ [1] = 0x1000, -- background
+ [2] = 0x2000, -- application
+ [3] = 0x2000, -- homescreen
+ [4] = 0x2000, -- interrupt application
+ [5] = 0x2000, -- onscreen application
+ [6] = 0xc000, -- startup
+ [7] = 0x3000, -- fullscreen
+ [101] = 0x4000, -- input
+ [102] = 0xa000, -- touch
+ [103] = 0xb000 -- cursor
+}
+
+resmgr = resource_manager {
+ screen_event_handler = function(self, ev)
+ local event = ev.event
+ local surface = ev.surface
+
+ if event == "init" then
+ if verbose > 0 then
+ print("*** init screen resource allocation -- disable all 'player'")
+ end
+ resmgr:disable_audio_by_appid("*", "player", "*", true, false)
+ elseif event == "preallocate" then
+ if verbose > 0 then
+ print("*** preallocate screen resource "..
+ "for '" .. ev.appid .. "' -- enable 'player', if any")
+ end
+ resmgr:disable_audio_by_appid("*", "player", ev.appid, false, false)
+ elseif event == "grant" then
+ if verbose > 0 then
+ print("*** make visible surface "..surface)
+ end
+ local a = animation({})
+ local r = m:JSON({surface = surface,
+ visible = 1,
+ raise = 1})
+ if ev.appid == onscreen then
+ onscreen_counter = onscreen_counter + 1
+ wmgr:layer_request(m:JSON({layer = 5, visible = 1}))
+ end
+
+ wmgr:window_request(r,a,0)
+ elseif event == "revoke" then
+ if verbose > 0 then
+ print("*** hide surface "..surface)
+ end
+ local a = animation({})
+ local r = m:JSON({surface = ev.surface,
+ visible = 0})
+ if ev.appid == onscreen then
+ onscreen_counter = onscreen_counter - 1
+ if onscreen_counter <= 0 then
+ onscreen_counter = 0
+ wmgr:layer_request(m:JSON({layer = 5, visible = 0}))
+ end
+ end
+ wmgr:window_request(r,a,0)
+
+ elseif event == "create" then
+
+ if verbose > 0 then
+ print("*** screen resource event: " ..
+ tostring(ev))
+ end
+
+ local regulation = mdb.select.select_driving_mode.single_value
+
+ if regulation == 1 then
+
+ local blacklisted = false
+
+ -- applications which have their category set to "entertainment"
+ -- or "undefined" are blacklisted, meaning they should be regulated
+
+ for i,v in pairs(ft(mdb.select.undefined_applications)) do
+ if v.appid == ev.appid then
+ if verbose > 0 then
+ print(ev.appid .. " was blacklisted (undefined)")
+ end
+ blacklisted = true
+ break
+ end
+ end
+
+ if not blacklisted then
+ for i,v in pairs(ft(mdb.select.entertainment_applications)) do
+ if v.appid == ev.appid then
+ if verbose > 0 then
+ print(ev.appid .. " was blacklisted (entertainment)")
+ end
+ blacklisted = true
+ break
+ end
+ end
+ end
+
+ -- our local application config, which takes precedence
+ local conf = getApplication(ev.appid)
+
+ if not conf then
+ blacklisted = true
+ else
+ if conf.resource_class == "player" then
+ blacklisted = true
+ end
+
+ -- check the exceptions
+ if conf.requisites and conf.requisites.screen then
+ if conf.requisites.screen.driving then
+ blacklisted = false
+ end
+ end
+ end
+
+ -- disable only non-whitelisted applications
+ if blacklisted then
+ if verbose > 0 then
+ print("disabling screen for " .. ev.appid)
+ end
+ resmgr:disable_screen_by_appid("*", "*", ev.appid, true, true)
+ end
+ end
+
+ elseif event == "destroy" then
+ if verbose > 0 then
+ print("*** screen resource event: " ..
+ tostring(ev))
+ end
+ else
+ if verbose > 0 then
+ print("*** screen resource event: " ..
+ tostring(ev))
+ end
+ end
+ end,
+ audio_event_handler = function(self, ev)
+ local event = ev.event
+ local appid = ev.appid
+ local audioid = ev.audioid
+
+ if event == "grant" then
+ if verbose > 0 then
+ print("*** grant audio to "..appid..
+ " ("..audioid..") in '" ..
+ ev.zone .. "' zone")
+ end
+ elseif event == "revoke" then
+ if verbose > 0 then
+ print("*** revoke audio from "..appid..
+ " ("..audioid..") in '" ..
+ ev.zone .. "' zone")
+ end
+ else
+ if verbose > 0 then
+ print("*** audio resource event: " ..
+ tostring(ev))
+ end
+ end
+ end
+}
+
+resclnt = resource_client {}
+
+wmgr = window_manager {
+ geometry = function(self, w,h, v)
+ if type(v) == "function" then
+ return v(w,h)
+ end
+ return v
+ end,
+
+ application = function(self, appid)
+ if appid then
+ local app = application_lookup(appid)
+ if not app then
+ app = application_lookup("default")
+ end
+ return app
+ end
+ return { privileges = {screen="none", audio="none"} }
+ end,
+
+ output_order = { 1, 0 },
+
+ outputs = { { name = "Mid",
+ id = 1,
+ zone = "driver",
+ areas = { Full = {
+ id = 20,
+ pos_x = 0,
+ pos_y = 0,
+ width = function(w,h) return w end,
+ height = function(w,h) return h end
+ },
+ Left = {
+ id = 21,
+ pos_x = 0,
+ pos_y = 0,
+ width = 320,
+ height = function(w,h) return h end
+ },
+ Right = {
+ id = 22,
+ pos_x = function(w,h) return w-320 end,
+ pos_y = 0,
+ width = 320,
+ height = function(w,h) return h end
+ }
+ }
+ },
+ { name = "Center",
+ id = 4,
+ zone = "driver",
+ areas = { Status = {
+ id = 0,
+ pos_x = 0,
+ pos_y = 0,
+ width = function(w,h) return w end,
+ height = 64
+ },
+ Full = {
+ id = 1,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w end,
+ height = function(w,h) return h-64-128 end
+ },
+ Upper = {
+ id = 2,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ Lower = {
+ id = 3,
+ pos_x = 0,
+ pos_y = function(w,h) return (h-64-128)/2+64 end,
+ width = function(w,h) return w end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ UpperLeft = {
+ id = 4,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w/2 end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ UpperRight = {
+ id = 5,
+ pos_x = function(w,h) return w/2 end,
+ pos_y = 64,
+ width = function(w,h) return w/2 end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ LowerLeft = {
+ id = 6,
+ pos_x = 0,
+ pos_y = function(w,h) return (h-64-128/2)+64 end,
+ width = function(w,h) return w/2 end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ LowerRight = {
+ id = 7,
+ pos_x = function(w,h) return w/2 end,
+ pos_y = function(w,h) return (h-64-128/2)+64 end,
+ width = function(w,h) return w/2 end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ SysApp = {
+ id = 8,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w end,
+ height = function(w,h) return h-64-128 end
+ },
+ ["SysApp.Left"] = {
+ id = 9,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w/2-181 end,
+ height = function(w,h) return h-64-128 end
+ },
+ ["SysApp.Right"] = {
+ id = 10,
+ pos_x = function(w,h) return w/2+181 end,
+ pos_y = 64,
+ width = function(w,h) return w/2-181 end,
+ height = function(w,h) return h-64-128 end
+ },
+ MobileFull = {
+ id = 11,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w end,
+ height = function(w,h) return h-64-128 end
+ },
+ MobileUpper = {
+ id = 12,
+ pos_x = 0,
+ pos_y = 64,
+ width = function(w,h) return w end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ MobileLower = {
+ id = 13,
+ pos_x = 0,
+ pos_y = function(w,h) return (h-64-128)/2+64 end,
+ width = function(w,h) return w end,
+ height = function(w,h) return (h-64-128)/2 end
+ },
+ Control = {
+ id = 14,
+ pos_x = 0,
+ pos_y = function(w,h) return h-128 end,
+ width = function(w,h) return w end,
+ height = 128
+ },
+ }
+ }
+ },
+ -- id name type output
+ layers = { { 0, "BackGround" , 1, "Center" },
+ { 1, "Application" , 2, "Center" },
+ { 2, "HomeScreen" , 3, "Center" },
+ { 3, "ControlBar" , 3, "Center" },
+ { 4, "InterruptApp" , 4, "Center" },
+ { 5, "OnScreen" , 5, "Center" },
+ { 6, "Touch" , 102, "Center" },
+ { 7, "Cursor" , 103, "Center" }
+ },
+
+
+ manager_update = function(self, oper)
+ if verbose > 0 then
+ print("### <== WINDOW MANAGER UPDATE:" ..
+ window_manager_operation_name(oper))
+ end
+ if oper == 1 then
+ local wumask = window_mask { --raise = true,
+ visible = true,
+ active = true }
+ local wrmask = window_mask { raise = true,
+ active = true,
+ layer = true }
+ local lumask = layer_mask { visible = true }
+ local lrmask = layer_mask { visible = true }
+ local req = m:JSON({
+ passthrough_window_update = wumask:tointeger(),
+ passthrough_window_request = wrmask:tointeger(),
+ passthrough_layer_update = lumask:tointeger(),
+ passthrough_layer_request = lrmask:tointeger()
+ })
+ self:manager_request(req)
+ end
+ end,
+
+ window_update = function(self, oper, win, mask)
+ if verbose > 0 then
+ print("### <== WINDOW UPDATE oper:" ..
+ window_operation_name(oper) ..
+ " mask: " .. tostring(mask))
+ if verbose > 1 then
+ print(win)
+ end
+ end
+
+ local arg = m:JSON({ surface = win.surface,
+ winname = win.name
+ })
+ local command = 0
+
+ if oper == 1 then -- create
+ local layertype = win.layertype
+ if layertype and input_layer[layertype] then
+ if verbose > 0 then
+ print("ignoring input panel creation")
+ end
+ return
+ end
+ command = 0x10001
+ elseif oper == 2 then -- destroy
+ command = 0x10002
+ elseif oper == 3 then -- namechange
+ command = 0x10010
+ elseif oper == 4 or oper == 5 then -- visible/configure
+ command = 0x10009
+ arg.zone = win.area
+ arg.node = win.node
+ if win.layertype then
+ arg.layertype = win.layertype
+ end
+ arg.layer = win.layer
+ arg.pos_x = win.pos_x
+ arg.pos_y = win.pos_y
+ arg.width = win.width
+ arg.height = win.height
+ arg.raise = win.raise
+ arg.visible = win.visible
+ if win.active == 0 then
+ arg.active = 0
+ else
+ arg.active = 1
+ end
+ elseif oper == 6 then -- active
+ if win.active == 0 then
+ if verbose > 0 then
+ print("ignoring inactive event")
+ end
+ return
+ end
+ command = 0x10007
+ elseif oper == 7 then -- map
+ local map = win.map
+ if not map then
+ return
+ end
+ if win.mapped == 0 then
+ command = 0x10021
+ else
+ command = 0x10020
+ end
+ arg.attr = map.type
+ --arg.name = map.target
+ arg.width = map.width
+ arg.height = map.height
+ arg.stride = map.stride
+ arg.format = map.format
+ else
+ if verbose > 0 then
+ print("### nothing to do")
+ end
+ return
+ end
+
+ local msg = m:JSON({ command = command,
+ appid = win.appid,
+ pid = win.pid,
+ arg = arg
+ })
+ if verbose > 0 then
+ print("### <== sending " ..
+ command_name(msg.command) ..
+ " window message to '" .. homescreen .. "'")
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ sc:send_message(homescreen, msg)
+
+ if oper == 1 then -- create
+ local i = input_layer[win.layertype]
+ local p = self:application(win.appid)
+ local s = p.privileges.screen
+
+ if s == "system" then
+ local a = animation({})
+ local r = m:JSON({surface = win.surface,
+ visible = 0,
+ raise = 1})
+ self:window_request(r,a,0)
+ else
+ if i then
+ if verbose > 0 then
+ print("do not make resource for " ..
+ "input window")
+ end
+ else
+ resclnt:resource_set_create("screen",
+ "driver",
+ win.appid,
+ win.surface)
+ special_screen_sets[win.surface] = true
+ end
+ end
+
+ if onscreen and win.appid == onscreen then
+ local resmsg = m:JSON({
+ command = 0x40006, -- window_id_res
+ appid = win.appid,
+ pid = win.pid,
+ res = m:JSON({
+ window = m:JSON({
+ ECU = "",
+ display = "",
+ layer = "",
+ layout = "",
+ area = "",
+ dispatchApp = "",
+ role = win.name,
+ resourceId = win.surface
+ })
+ })
+ })
+ if verbose > 0 then
+ print("### <== sending " ..
+ command_name(resmsg.command) ..
+ " message to '" .. onscreen .. "'")
+ if verbose > 1 then
+ print(resmsg)
+ end
+ end
+ sc:send_message(onscreen, resmsg);
+ end
+ elseif oper == 2 then -- destroy
+ resclnt:resource_set_destroy("screen", win.surface)
+ special_screen_sets[win.surface] = nil
+ elseif oper == 6 then -- active
+ if win.active then
+ local i = input_layer[win.layertype]
+ local p = self:application(win.appid)
+ local s = p.privileges.screen
+ local surface = win.surface
+ if not i and s ~= "system" then
+ resclnt:resource_set_acquire("screen",surface)
+ resmgr:window_raise(win.appid, surface, 1)
+ end
+ end
+ end
+ end,
+
+ layer_update = function(self, oper, layer, mask)
+ if verbose > 0 then
+ print("### LAYER UPDATE:" ..
+ layer_operation_name(oper) ..
+ " mask: " .. tostring(mask))
+ if verbose > 1 then
+ print(layer)
+ end
+ end
+ if oper == 3 then -- visible
+ local command = 0x10008 -- change_layer
+ local msg = m:JSON({
+ command = command,
+ appid = "",
+ arg = m:JSON({layer = layer.id,
+ visible = layer.visible
+ })
+ })
+ if verbose > 0 then
+ print("### <== sending "..command_name(command)..
+ " layer message")
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ sc:send_message(homescreen, msg)
+ else
+ if verbose > 0 then
+ print("### nothing to do")
+ end
+ end
+ end,
+
+ output_update = function(self, oper, out, mask)
+ local idx = out.index
+ local defidx = self.output_order[idx+1]
+ if verbose > 0 then
+ print("### OUTPUT UPDATE:" .. oper ..
+ " mask: "..tostring(mask))
+ end
+ if not defidx then
+ return
+ end
+ print(out)
+ local outdef = self.outputs[defidx+1]
+ if (oper == 1) then -- create
+ if outdef then
+ self:output_request(m:JSON({index = idx,
+ id = outdef.id,
+ name = outdef.name
+ }))
+ end
+ elseif (oper == 5) then -- done
+ local ads = outdef.areas
+ local on = outdef.name
+ if ads then
+ for name,ad in pairs(ads) do
+ local can = wmgr:canonical_name(on.."."..name)
+ local a = m:JSON({name = name,
+ output = out.index})
+ for fld,val in pairs(ad) do
+ a[fld] = self:geometry(out.width,
+ out.height,
+ val)
+ end
+ self:area_create(a)
+ resmgr:area_create(area[can], outdef.zone)
+ end
+ end
+ end
+ end
+}
+
+
+imgr = input_manager {
+ inputs = {{ name = "G27 Racing Wheel",
+ id = 0,
+ switch = { [2] = {appid="org.tizen.ico.app-soundsample" },
+ [3] = {appid="org.tizen.ico.homescreen", keycode=1},
+ [4] = {appid="org.tizen.ico.app-soundsample" },
+ [5] = {appid="org.tizen.ico.homescreen", keycode=2}
+ }}
+ },
+
+ manager_update = function(self, oper)
+ if verbose > 0 then
+ print("### <== INPUT MANAGER UPDATE:" ..
+ input_manager_operation_name(oper))
+ end
+ end,
+
+ input_update = function(self, oper, inp, mask)
+ if verbose > 0 then
+ print("### INPUT UPDATE:" ..
+ input_operation_name(oper) ..
+ " mask: " .. tostring(mask))
+ if verbose > 1 then
+ print(inp)
+ end
+ end
+ end,
+ code_update = function(self, oper, code, mask)
+ if verbose > 0 then
+ print("### CODE UPDATE: mask: " .. tostring(mask))
+ if verbose > 1 then
+ print(code)
+ end
+ end
+ local msg = m:JSON({ command = 1,
+ appid = "org.tizen.ico.homescreen",
+ arg = m:JSON({ device = code.device,
+ time = code.time,
+ input = code.input,
+ code = code.id,
+ state = code.state
+ })
+ })
+ if verbose > 0 then
+ print("### <== sending " ..
+ command_name(msg.command) ..
+ " input message")
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ sc:send_message(homescreen, msg)
+ end
+}
+
+sc = m:get_system_controller()
+
+-- resource sets
+sets = {}
+
+-- special screen resource sets
+-- TODO: just rewrite screen resource handling to use regular resource API
+
+special_screen_sets = {}
+
+-- user manager
+um = m:UserManager()
+
+connected = false
+homescreen = ""
+onscreen = ""
+
+cids = {}
+
+-- these shoud be before wmgr:connect() is called
+if verbose > 0 then
+ print("====== creating applications ======")
+end
+application {
+ appid = "default",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ resource_class = "player",
+ screen_priority = 0
+}
+
+application {
+ appid = "weston",
+ area = "Center.Full",
+ privileges = { screen = "system", audio = "none" },
+ resource_class = "implicit",
+ screen_priority = 30
+}
+
+application {
+ appid = "org.tizen.ico.homescreen",
+ area = "Center.Full",
+ windows = { {'ico_hs_controlbarwindow', 'Center.Control'} },
+ privileges = { screen = "system", audio = "system" },
+ resource_class = "player",
+ screen_priority = 20
+}
+
+application {
+ appid = "org.tizen.ico.statusbar",
+ area = "Center.Status",
+ privileges = { screen = "system", audio = "none" },
+ resource_class = "player",
+ screen_priority = 20
+}
+
+application {
+ appid = "org.tizen.ico.onscreen",
+ area = "Center.Full",
+ privileges = { screen = "system", audio = "system" },
+ resource_class = "player",
+ screen_priority = 20
+}
+
+application {
+ appid = "org.tizen.ico.login",
+ area = "Center.Full",
+ privileges = { screen = "system", audio = "system" },
+ resource_class = "player",
+ screen_priority = 20
+}
+
+application {
+ appid = "org.tizen.ico.camera_left",
+ area = "Center.SysApp.Left",
+ privileges = { screen = "system", audio = "none" },
+ requisites = { screen = "blinker_left", audio = "none" },
+ resource_class = "player",
+ screen_priority = 30
+}
+
+application {
+ appid = "org.tizen.ico.camera_right",
+ area = "Center.SysApp.Right",
+ privileges = { screen = "system", audio = "none" },
+ requisites = { screen = "blinker_right", audio = "none" },
+ resource_class = "player",
+ screen_priority = 30
+}
+
+application {
+ appid = "net.zmap.navi",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ resource_class = "navigator",
+ screen_priority = 30
+}
+
+application {
+ appid = "GV3ySIINq7.GhostCluster",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ resource_class = "system",
+ requisites = { screen = "driving", audio = "none" },
+ screen_priority = 30
+}
+
+application {
+ appid = "MediaPlayer",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ requisites = { screen = "driving", audio = "none" },
+ resource_class = "player",
+ screen_priority = 0
+}
+
+application {
+ appid = "MyMediaPlayer",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ requisites = { screen = "driving", audio = "none" },
+ resource_class = "player",
+ screen_priority = 0
+}
+
+application {
+ appid = "MeterWidget",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ requisites = { screen = "driving", audio = "none" },
+ resource_class = "player",
+ screen_priority = 0
+}
+
+application {
+ appid = "org.tizen.ico.app-soundsample",
+ area = "Center.Full",
+ privileges = { screen = "none", audio = "none" },
+ -- uncomment the next line to make the app exempt from regulation
+ -- requisites = { screen = "driving", audio = "none" },
+ resource_class = "player",
+ screen_priority = 0
+}
+
+
+if sc then
+ sc.client_handler = function (self, cid, msg)
+ local command = msg.command
+ local appid = msg.appid
+ if verbose > 0 then
+ print('### ==> client handler:')
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+
+ -- known commands: 1 for SEND_APPID, synthetic command 0xFFFF for
+ -- disconnection
+
+ if command == 0xFFFF then
+ if verbose > 1 then
+ print('client ' .. cid .. ' disconnected')
+ end
+ if msg.appid == homescreen then
+ homescreen = ""
+ for i,v in pairs(special_screen_sets) do
+ resclnt:resource_set_destroy("screen", i)
+ special_screen_sets[i] = nil
+ end
+ end
+ return
+ end
+
+ -- handle the connection to weston
+
+ if appid then
+ if appid == "org.tizen.ico.homescreen" then
+ print('Setting homescreen='..appid)
+ homescreen = appid
+ if command and command == 1 then
+ send_driving_mode_to(homescreen)
+ send_night_mode_to(homescreen)
+ end
+ elseif appid == "org.tizen.ico.onscreen" then
+ onscreen = appid
+ if command and command == 1 then
+ send_driving_mode_to(onscreen)
+ send_night_mode_to(onscreen)
+ end
+ end
+
+ if not connected and appid == "org.tizen.ico.homescreen" then
+ print('Trying to connect to weston...')
+ connected = wmgr:connect()
+ end
+ cids[cid] = appid
+ end
+ end
+
+ sc.generic_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('### ==> generic handler:')
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ end
+
+ sc.window_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('### ==> received ' ..
+ command_name(msg.command) .. ' message from ' .. cids[cid])
+ if verbose > 1 then
+ print(tostring(msg))
+ end
+ end
+
+ local a = animation({})
+ local nores = false
+ if msg.command == 0x10003 then -- ico SHOW command
+ local raise_mask = 0x01000000
+ local lower_mask = 0x02000000
+ local nores_mask = 0x40000000
+ local time_mask = 0x00ffffff
+
+ local surface = msg.arg.surface
+ local system_surface = false
+ local appdb = wmgr:application(msg.appid)
+ system_surface = appdb.privileges.screen == "system"
+
+ msg.arg.visible = 1
+
+ if msg.arg then
+ local time = 200
+ if msg.arg.anim_time then
+ local t = msg.arg.anim_time
+ -- the actual time for the animation
+ time = m:AND(t, time_mask)
+ -- flag for ignoring resource control
+ nores = m:AND(t, nores_mask)
+ if m:AND(t, raise_mask) then
+ msg.arg.raise = 1
+ elseif m:AND(t, lower_mask) then
+ msg.arg.raise = 0
+ end
+ end
+ if msg.arg.anim_name then
+ a.show = { msg.arg.anim_name, time }
+ print('time: ' .. tostring(time))
+ end
+ end
+
+ -- all show messages from system surfaces are forced
+ if not nores and system_surface then
+ nores = true
+ if not msg.arg.raise then
+ msg.arg.raise = 1
+ end
+ end
+
+ if verbose > 2 then
+ print('### ==> SHOW')
+ print(tostring(msg.arg) .. ", system_surface=" .. tostring(system_surface))
+ end
+
+ if nores then
+ wmgr:window_request(msg.arg, a, 0)
+
+ -- only non-system surfaces have resource sets
+ if not system_surface then
+ resclnt:resource_set_acquire("screen", surface)
+ end
+ else
+ resclnt:resource_set_acquire("screen", surface)
+ resmgr:window_raise(msg.appid, surface, 1)
+ end
+ elseif msg.command == 0x10004 then -- ico HIDE command
+ local raise_mask = 0x01000000
+ local lower_mask = 0x02000000
+ local nores_mask = 0x40000000
+ local time_mask = 0x00ffffff
+ msg.arg.visible = 0
+ if msg.arg then
+ local time = 200
+ if msg.arg.anim_time then
+ local t = msg.arg.anim_time
+ -- the actual time for the animation
+ time = m:AND(t, time_mask)
+ -- flag for ignoring resource control
+ nores = m:AND(t, nores_mask)
+ end
+ if msg.arg.anim_name then
+ a.hide = { msg.arg.anim_name, time }
+ print('hide animation time: ' .. tostring(a.hide[2]))
+ end
+ end
+ if not nores then
+ local p = wmgr:application(msg.appid)
+ local s = p.privileges.screen
+ if s == "system" then
+ nores = true
+ msg.arg.raise = 0
+ end
+ end
+ if verbose > 2 then
+ print('### ==> HIDE REQUEST')
+ print(tostring(msg.arg))
+ end
+ if nores then
+ wmgr:window_request(msg.arg, a, 0)
+ else
+ resmgr:window_raise(msg.appid, msg.arg.surface, -1)
+ end
+ elseif msg.command == 0x10005 then -- ico MOVE
+ if verbose > 2 then
+ print('### ==> MOVE REQUEST')
+ print(tostring(msg.arg))
+ end
+ if msg.arg.zone then
+ msg.arg.area = msg.arg.zone
+ end
+ wmgr:window_request(msg.arg, a, 0)
+ -- TODO: handle if area changed
+ elseif msg.command == 0x10007 then -- ico CHANGE_ACTIVE
+ if not msg.arg.active then
+ msg.arg.active = 3 -- pointer + keyboard
+ end
+ if verbose > 2 then
+ print('### ==> CHANGE_ACTIVE REQUEST')
+ print(tostring(msg.arg))
+ end
+ wmgr:window_request(msg.arg, a, 0)
+ elseif msg.command == 0x10008 then -- ico CHANGE_LAYER
+ if verbose > 2 then
+ print('### ==> CHANGE_LAYER REQUEST')
+ print(tostring(msg.arg))
+ end
+ --[[
+ if msg.arg.layer ~= 4 or msg.arg.layer ~= 5 then
+ print("do not change layer for other than cursor or touch")
+ return
+ end
+ --]]
+ wmgr:window_request(msg.arg, a, 0)
+ elseif msg.command == 0x10020 then -- ico MAP_THUMB
+ local framerate = msg.arg.framerate
+ local animname = msg.arg.anim_name
+ if animname then
+ a.map = { animname, 1 }
+ if not framerate or framerate < 0 then
+ framerate = 5
+ end
+ msg.arg.mapped = 1
+ if verbose > 2 then
+ print('### ==> MAP_THUMB REQUEST')
+ print(msg.arg)
+ print('framerate: '..framerate)
+ end
+ wmgr:window_request(msg.arg, a, framerate)
+ end
+ elseif msg.command == 0x10021 then -- ico UNMAP_THUMB
+ msg.arg.mapped = 0
+ if verbose > 2 then
+ print('### ==> UNMAP_THUMB REQUEST')
+ print(msg.arg)
+ end
+ wmgr:window_request(msg.arg, a, 0)
+--[[
+ elseif msg.command == 0x10013 then -- ico MAP_BUFFER command
+ local shmname = msg.arg.anim_name
+ local bufsize = msg.arg.width
+ local bufnum = msg.arg.height
+ if shmname and bufsize and bufnum then
+ if verbose > 2 then
+ print('### ==> MAP_BUFFER REQUEST')
+ print("shmaname='" .. shmname ..
+ "' bufsize='" .. bufsize ..
+ " bufnum=" .. bufnum)
+ end
+ wmgr:buffer_request(shmname, bufsize, bufnum)
+ end
+--]]
+ elseif msg.command == 0x10030 then -- ico SHOW_LAYER command
+ msg.arg.visible = 1
+ if verbose > 2 then
+ print('### ==> SHOW_LAYER REQUEST')
+ print(msg.arg)
+ end
+ wmgr:layer_request(msg.arg)
+ elseif msg.command == 0x10031 then -- ico HIDE_LAYER command
+ msg.arg.visible = 0
+ if verbose > 2 then
+ print('### ==> HIDE_LAYER REQUEST')
+ print(msg.arg)
+ end
+ wmgr:layer_request(msg.arg)
+ end
+ end
+
+ sc.input_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('### ==> input handler: ' .. command_name(msg.command))
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ if msg.command == 0x20001 then -- add_input
+ msg.arg.appid = msg.appid
+ if verbose > 2 then
+ print('### ==> ADD_INPUT REQUEST')
+ print(tostring(msg.arg))
+ end
+ imgr:input_request(msg.arg)
+ elseif msg.command == 0x20002 then -- del_input
+ msg.arg.appid = ''
+ if verbose > 2 then
+ print('### ==> DEL_INPUT REQUEST')
+ print(tostring(msg.arg))
+ end
+ imgr:input_request(msg.arg)
+ -- elseif msg.command == 0x20003 then -- send_input
+ end
+ end
+
+ sc.user_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('### ==> user handler: ' .. command_name(msg.command))
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+
+ if not um then
+ print("User Manager not initialized")
+ return
+ end
+
+ if msg.command == 0x30001 then -- change_user
+ print("command CHANGE_USER")
+ if not msg.arg then
+ print("invalid message")
+ return
+ end
+
+ username = msg.arg.user
+ passwd = msg.arg.pass
+
+ if not username then
+ username = ""
+ end
+
+ if not passwd then
+ passwd = ""
+ end
+
+ success = um:changeUser(username, passwd)
+
+ if not success then
+ reply = m.JSON({
+ appid = msg.appid,
+ command = cmd
+ })
+ if sc:send_message(msg.appid, reply) then
+ print('*** sent authentication failed message')
+ else
+ print('*** failed to send authentication failed message')
+ end
+ end
+
+ elseif msg.command == 0x30002 then -- get_userlist
+ print("command GET_USERLIST")
+ if not msg.appid then
+ print("invalid message")
+ return
+ end
+
+ users, currentUser = um:getUserList()
+
+ if not users then
+ print("failed to get user list")
+ return
+ end
+
+ nUsers = 0
+
+ for i,v in pairs(users) do
+ nUsers = nUsers + 1
+ end
+
+ if not currentUser then
+ currentUser = ""
+ end
+
+ if verbose > 1 then
+ print("current user: " .. currentUser)
+ print("user list:")
+ for i,v in pairs(users) do
+ print(v)
+ end
+ end
+
+ reply = m.JSON({
+ appid = msg.appid,
+ command = cmd,
+ arg = m.JSON({
+ user_num = nUsers,
+ user_list = users,
+ user_login = currentUser
+ })
+ })
+
+ if verbose > 1 then
+ print("### <== GetUserList reply: " .. tostring(reply))
+ end
+
+ if sc:send_message(msg.appid, reply) then
+ print('*** reply OK')
+ else
+ print('*** reply FAILED')
+ end
+
+ elseif msg.command == 0x30003 then -- get_lastinfo
+ print("command GET_LASTINFO")
+ if not msg.appid then
+ print("invalid message")
+ return
+ end
+
+ lastInfo = um:getLastInfo(msg.appid)
+
+ if not lastInfo then
+ print("failed to get last info for app" .. msg.appid)
+ return
+ end
+
+ reply = m.JSON({
+ appid = msg.appid,
+ command = cmd,
+ arg = m.JSON({
+ lastinfo = lastinfo
+ })
+ })
+
+ if sc:send_message(msg.appid, reply) then
+ print('*** reply OK')
+ else
+ print('*** reply FAILED')
+ end
+
+ elseif msg.command == 0x30004 then -- set_lastinfo
+ print("command SET_LASTINFO")
+ if not msg.arg or not msg.appid then
+ print("invalid message")
+ return
+ end
+
+ lastInfo = um:setLastInfo(msg.appid, msg.arg.lastinfo)
+ end
+ end
+
+ sc.resource_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('### ==> resource handler: ' .. command_name(msg.command))
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+
+ getKey = function (msg)
+ -- Field rset.key is an id for distinguishing between rsets. All
+ -- resource types appear to contain a different key. Just pick one
+ -- based on what we have on the message.
+
+ key = nil
+
+ if msg and msg.res then
+ if msg.res.sound then
+ key = msg.res.sound.id
+ elseif msg.res.input then
+ key = msg.res.input.name
+ elseif msg.res.window then
+ -- alarm! this appars to be non-unique?
+ key = msg.res.window.resourceId
+ end
+ end
+
+ return key
+ end
+
+ createResourceSet = function (ctl, client, msg)
+ cb = function(rset)
+
+ -- m:info("*** resource_cb: client = '" .. msg.appid .. "'")
+
+ -- type is either basic (0) or interrupt (1)
+ requestType = 0
+ if msg.res.type then
+ requestType = msg.res.type
+ end
+
+ if rset.data.filter_first then
+ rset.data.filter_first = false
+ return
+ end
+
+ if rset.acquired then
+ cmd = 0x00040001 -- acquire
+
+ if msg.appid == onscreen then
+ -- notifications are valid only for a number of seconds
+ rset.timer = m:Timer({
+ interval = 5000,
+ oneshot = true,
+ callback = function (t)
+ m:info("notification timer expired")
+
+ if rset.data then
+ reply.res.window = rset.data.window
+ end
+
+ rset:release()
+ rset.timer = nil
+
+ -- Send a "RELEASE" message to client.
+ -- This triggers the resource deletion
+ -- cycle in OnScreen.
+
+ reply = m.JSON({
+ appid = msg.appid,
+ command = cmd,
+ res = {
+ type = requestType
+ }
+ })
+
+ sc:send_message(client, reply)
+ end
+ })
+ end
+ else
+ cmd = 0x00040002 -- release
+ if rset.timer then
+ rset.timer.callback = nil
+ rset.timer = nil
+ end
+ end
+
+ reply = m.JSON({
+ appid = msg.appid,
+ command = cmd,
+ res = {
+ type = requestType
+ }
+ })
+
+ if rset.data.sound then
+ reply.res.sound = rset.data.sound
+ end
+
+ if rset.data.window then
+ reply.res.window = rset.data.window
+ end
+
+ if rset.data.input then
+ reply.res.input = rset.data.input
+ end
+
+ if rset.acquired then
+ m:info("resource cb: 'acquire' reply to client " .. client)
+ else
+ m:info("resource cb: 'release' reply to client " .. client)
+ end
+
+ if not sc:send_message(client, reply) then
+ m:info('*** reply FAILED')
+ end
+ end
+
+ local rset = m:ResourceSet({
+ application_class = "player",
+ zone = "driver", -- msg.zone ("full")
+ callback = cb
+ })
+
+ rset.data = {
+ cid = cid,
+ ctl = ctl,
+ filter_first = true
+ }
+
+ if msg.res.sound then
+ rset:addResource({
+ resource_name = "audio_playback"
+ })
+ rset.resources.audio_playback.attributes.pid = tostring(msg.pid)
+ rset.resources.audio_playback.attributes.appid = msg.appid
+ print("sound name: " .. msg.res.sound.name)
+ print("sound zone:" .. msg.res.sound.zone)
+ print("sound adjust: " .. tostring(msg.res.sound.adjust))
+
+ rset.data.sound = msg.res.sound
+ end
+
+ if msg.res.input then
+ rset:addResource({
+ resource_name = "input"
+ })
+ rset.resources.input.attributes.pid = tostring(msg.pid)
+ rset.resources.input.attributes.appid = msg.appid
+ print("input name: " .. msg.res.input.name)
+ print("input event:" .. tostring(msg.res.input.event))
+
+ rset.data.input = msg.res.input
+ end
+
+ if msg.res.window then
+ rset:addResource({
+ resource_name = "screen",
+ shared = true
+ })
+ rset.resources.screen.attributes.pid = tostring(msg.pid)
+ rset.resources.screen.attributes.appid = msg.appid
+ rset.resources.screen.attributes.surface = msg.res.window.resourceId
+ complete_area = msg.res.window.display .. '.' .. msg.res.window.area
+ rset.resources.screen.attributes.area = complete_area
+ if msg.appid == onscreen then
+ rset.resources.screen.attributes.classpri = 1
+ else
+ rset.resources.screen.attributes.classpri = 0
+ end
+
+ rset.data.window = msg.res.window
+ end
+
+ rset.key = getKey(msg)
+
+ return rset
+ end
+
+ -- parse the message
+
+ -- fields common to all messages:
+ -- msg.command
+ -- msg.appid
+ -- msg.pid
+
+ if msg.command == 0x40011 then -- create_res
+ print("command CREATE_RES")
+ key = getKey(msg)
+
+ if key then
+ if not sets.cid then
+ sets.cid = {}
+ end
+ sets.cid[key] = createResourceSet(self, cid, msg)
+ end
+
+ elseif msg.command == 0x40012 then -- destroy_res
+ print("command DESTROY_RES")
+ key = getKey(msg)
+
+ if key then
+ if sets.cid and sets.cid[key] then
+ sets.cid[key]:release()
+ sets.cid[key].timer = nil
+ sets.cid[key] = nil -- garbage collecting
+ end
+ end
+
+ elseif msg.command == 0x40001 then -- acquire_res
+ print("command ACQUIRE_RES")
+ key = getKey(msg)
+
+ if key then
+ if not sets.cid then
+ sets.cid = {}
+ end
+ if not sets.cid[key] then
+ sets.cid[key] = createResourceSet(self, cid, msg)
+ end
+ print("acquiring the resource set")
+ sets.cid[key]:acquire()
+ end
+
+ elseif msg.command == 0x40002 then -- release_res
+ print("command RELEASE_RES")
+
+ key = getKey(msg)
+
+ if key then
+ if sets.cid and sets.cid[key] then
+ sets.cid[key]:release()
+
+ if msg.appid == onscreen then
+ -- in case of OnScreen, this actually means that the
+ -- resource set is never used again; let gc do its job
+ sets.cid[key].timer = nil
+ sets.cid[key] = nil -- garbage collecting
+ end
+ end
+ end
+
+ elseif msg.command == 0x40003 then -- deprive_res
+ print("command DEPRIVE_RES")
+
+ elseif msg.command == 0x40004 then -- waiting_res
+ print("command WAITING_RES")
+
+ elseif msg.command == 0x40005 then -- revert_res
+ print("command REVERT_RES")
+ end
+ end
+
+ sc.inputdev_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('*** inputdev handler: ' .. command_name(msg.command))
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ end
+
+ sc.notify_handler = function (self, cid, msg)
+ if verbose > 0 then
+ print('### notify handler: ' .. command_name(msg.command))
+ if verbose > 1 then
+ print(msg)
+ end
+ end
+ end
+end
+
+function send_driving_mode_to(client)
+ if client == "" then
+ return
+ end
+
+ local driving_mode = mdb.select.select_driving_mode.single_value
+
+ if not driving_mode then driving_mode = 0 end
+
+ local reply = m:JSON({ command = 0x60001,
+ arg = m:JSON({ stateid = 1,
+ state = driving_mode
+ })
+ })
+
+ if verbose > 0 then
+ print("### <== sending " .. command_name(reply.command) .. " message")
+ if verbose > 1 then
+ print(reply)
+ end
+ end
+
+ sc:send_message(client, reply)
+end
+
+function send_night_mode_to(client)
+ if client == "" then
+ return
+ end
+
+ local night_mode = mdb.select.select_night_mode.single_value
+
+ if not night_mode then night_mode = 0 end
+
+ local reply = m:JSON({ command = 0x60001,
+ arg = m:JSON({ stateid = 2,
+ state = night_mode
+ })
+ })
+
+ if verbose > 0 then
+ print("### <== sending " .. command_name(reply.command) .. " message")
+ if verbose > 1 then
+ print(reply)
+ end
+ end
+
+ sc:send_message(client, reply)
+end
+
+-- we should have 'audio_playback' defined by now
+m:try_load_plugin('telephony')
--- /dev/null
+# By default we build with distro-default compilation flags which
+# enables optimizations. If you want to build with full debugging
+# ie. with optimization turned off and full debug info (-O0 -g3)
+# pass '--with debug' to rpmbuild on the command line. Similary
+# you can chose to compile with/without pulse, ecore, glib, qt,
+# dbus, and telephony support. --without squashpkg will prevent
+# squashing the -core and -plugins-base packages into the base
+# murphy package.
+
+%{!?_with_debug:%{!?_without_debug:%define _without_debug 0}}
+%{!?_with_lua:%{!?_without_lua:%define _with_lua 1}}
+%{!?_with_pulse:%{!?_without_pulse:%define _with_pulse 1}}
+%{!?_with_ecore:%{!?_without_ecore:%define _with_ecore 1}}
+%{!?_with_glib:%{!?_without_glib:%define _with_glib 1}}
+%{!?_with_qt:%{!?_without_qt:%define _without_qt 1}}
+%{!?_with_dbus:%{!?_without_dbus:%define _with_dbus 1}}
+%{!?_with_telephony:%{!?_without_telephony:%define _with_telephony 1}}
+%{!?_with_audiosession:%{!?_without_audiosession:%define _with_audiosession 1}}
+%{!?_with_websockets:%{!?_without_websockets:%define _with_websockets 1}}
+%{!?_with_smack:%{!?_without_smack:%define _with_smack 1}}
+%{!?_with_squashpkg:%{!?_without_squashpkg:%define _with_squashpkg 1}}
+
+# TODO: take care of /lib vs /lib64...
+%define systemddir /lib/systemd
+
+Summary: Murphy policy framework
+Name: murphy
+Version: @VERSION@
+Release: 1
+License: BSD-3-Clause
+Group: System/Service
+URL: http://01.org/murphy/
+Source0: %{name}-%{version}.tar.gz
+@DECLARE_PATCHES@
+BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%endif
+
+Requires(post): /bin/systemctl
+Requires(postun): /bin/systemctl
+
+BuildRequires: flex
+BuildRequires: bison
+BuildRequires: pkgconfig(lua)
+BuildRequires: pkgconfig(libsystemd-daemon)
+BuildRequires: pkgconfig(libsystemd-journal)
+
+BuildRequires: pkgconfig(wayland-client)
+BuildRequires: pkgconfig(ivi-extension-protocol)
+BuildRequires: ico-uxf-weston-plugin-devel
+
+%if %{?_with_pulse:1}%{!?_with_pulse:0}
+BuildRequires: pkgconfig(libpulse)
+%endif
+%if %{?_with_ecore:1}%{!?_with_ecore:0}
+BuildRequires: pkgconfig(ecore)
+BuildRequires: mesa-libEGL
+BuildRequires: mesa-libGLESv2
+%endif
+%if %{?_with_glib:1}%{!?_with_glib:0}
+BuildRequires: pkgconfig(glib-2.0)
+%endif
+%if %{?_with_qt:1}%{!?_with_qt:0}
+BuildRequires: pkgconfig(QtCore)
+%endif
+%if %{?_with_dbus:1}%{!?_with_dbus:0}
+BuildRequires: pkgconfig(dbus-1)
+%endif
+%if %{?_with_telephony:1}%{!?_with_telephony:0}
+BuildRequires: pkgconfig(ofono)
+%endif
+%if %{?_with_audiosession:1}%{!?_with_audiosession:0}
+BuildRequires: pkgconfig(audio-session-mgr)
+%endif
+%if %{?_with_websockets:1}%{!?_with_websockets:0}
+BuildRequires: libwebsockets-devel
+%endif
+BuildRequires: pkgconfig(json)
+
+%if %{?_with_smack:1}%{!?_with_smack:0}
+BuildRequires: pkgconfig(libsmack)
+%endif
+
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+%package core
+Summary: Murphy core runtime libraries
+Group: System/Libraries
+
+%package plugins-base
+Summary: The basic set of Murphy plugins
+Group: System/Service
+Requires: %{name} = %{version}
+Requires: %{name}-core = %{version}
+%endif
+
+%package devel
+Summary: The header files and libraries needed for Murphy development
+Group: System/Libraries
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+Requires: libjson-devel
+
+%package doc
+Summary: Documentation for Murphy
+Group: SDK/Documentation
+
+%if %{?_with_pulse:1}%{!?_with_pulse:0}
+%package pulse
+Summary: Murphy PulseAudio mainloop integration
+Group: System/Libraries
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+
+%package pulse-devel
+Summary: Murphy PulseAudio mainloop integration development files
+Group: System/Libraries
+Requires: %{name}-pulse = %{version}
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+%endif
+
+%if %{?_with_ecore:1}%{!?_with_ecore:0}
+%package ecore
+Summary: Murphy EFL/ecore mainloop integration
+Group: System/Libraries
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+
+%package ecore-devel
+Summary: Murphy EFL/ecore mainloop integration development files
+Group: System/Libraries
+Requires: %{name}-ecore = %{version}
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+%endif
+
+%if %{?_with_glib:1}%{!?_with_glib:0}
+%package glib
+Summary: Murphy glib mainloop integration
+Group: System/Libraries
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+
+%package glib-devel
+Summary: Murphy glib mainloop integration development files
+Group: System/Libraries
+Requires: %{name}-glib = %{version}
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+%endif
+
+%if %{?_with_qt:1}%{!?_with_qt:0}
+%package qt
+Summary: Murphy Qt mainloop integration
+Group: System/Libraries
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+
+%package qt-devel
+Summary: Murphy Qt mainloop integration development files
+Group: System/Libraries
+Requires: %{name}-qt = %{version}
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+%endif
+
+%package tests
+Summary: Various test binaries for Murphy
+Group: System/Testing
+Requires: %{name} = %{version}
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+Requires: %{name}-core = %{version}
+%else
+Requires: %{name} = %{version}
+%endif
+
+%description
+This package contains the basic daemon.
+
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+%description core
+This package contains the core runtime libraries.
+
+%description plugins-base
+This package contains a basic set of plugins.
+%endif
+
+%description devel
+This package contains header files and libraries necessary for development.
+
+%description doc
+This package contains documentation.
+
+%if %{?_with_pulse:1}%{!?_with_pulse:0}
+%description pulse
+This package contains the Murphy PulseAudio mainloop integration runtime files.
+
+%description pulse-devel
+This package contains the Murphy PulseAudio mainloop integration development
+files.
+%endif
+
+%if %{?_with_ecore:1}%{!?_with_ecore:0}
+%description ecore
+This package contains the Murphy EFL/ecore mainloop integration runtime files.
+
+%description ecore-devel
+This package contains the Murphy EFL/ecore mainloop integration development
+files.
+%endif
+
+%if %{?_with_glib:1}%{!?_with_glib:0}
+%description glib
+This package contains the Murphy glib mainloop integration runtime files.
+
+%description glib-devel
+This package contains the Murphy glib mainloop integration development
+files.
+%endif
+
+%if %{?_with_qt:1}%{!?_with_qt:0}
+%description qt
+This package contains the Murphy Qt mainloop integration runtime files.
+
+%description qt-devel
+This package contains the Murphy Qt mainloop integration development
+files.
+%endif
+
+%description tests
+This package contains various test binaries for Murphy.
+
+%prep
+%setup -q
+@APPLY_PATCHES@
+
+%build
+%if %{?_with_debug:1}%{!?_with_debug:0}
+export CFLAGS="-O0 -g3"
+V="V=1"
+%endif
+
+CONFIG_OPTIONS=""
+DYNAMIC_PLUGINS="domain-control"
+
+%if %{?_with_pulse:1}%{!?_with_pulse:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-pulse"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-pulse"
+%endif
+
+%if %{?_with_ecore:1}%{!?_with_ecore:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-ecore"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-ecore"
+%endif
+
+%if %{?_with_glib:1}%{!?_with_glib:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-glib"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-glib"
+%endif
+
+%if %{?_with_qt:1}%{!?_with_qt:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-qt"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-qt"
+%endif
+
+%if %{?_with_dbus:1}%{!?_with_dbus:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-dbus"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-dbus"
+%endif
+
+%if %{?_with_telephony:1}%{!?_with_telephony:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-telephony"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-telephony"
+%endif
+
+%if %{?_with_audiosession:1}%{!?_with_audiosession:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-resource-asm"
+DYNAMIC_PLUGINS="$DYNAMIC_PLUGINS,resource-asm"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-resource-asm"
+%endif
+
+%if %{?_with_websockets:1}%{!?_with_websockets:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-websockets"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-websockets"
+%endif
+
+%if %{?_with_smack:1}%{!?_with_smack:0}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-smack"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-smack"
+%endif
+
+NUM_CPUS="`cat /proc/cpuinfo | tr -s '\t' ' ' | \
+ grep '^processor *:' | wc -l`"
+[ -z "$NUM_CPUS" ] && NUM_CPUS=1
+
+./bootstrap && \
+ %configure $CONFIG_OPTIONS --with-dynamic-plugins=$DYNAMIC_PLUGINS && \
+ make clean && \
+ make -j$(($NUM_CPUS + 1)) $V
+
+%install
+rm -rf $RPM_BUILD_ROOT
+%make_install
+
+# Make sure we have a plugin dir even if all the basic plugins
+# are configured to be built in.
+mkdir -p $RPM_BUILD_ROOT%{_libdir}/murphy/plugins
+
+# Get rid of any *.la files installed by libtool.
+rm -f $RPM_BUILD_ROOT%{_libdir}/*.la
+
+# Clean up also the murphy DB installation.
+rm -f $RPM_BUILD_ROOT%{_libdir}/murphy/*.la
+
+# Generate list of linkedin plugins (depends on the configuration).
+outdir="`pwd`"
+pushd $RPM_BUILD_ROOT >& /dev/null && \
+ find ./%{_libdir}/murphy/plugins -name libmurphy-plugin-*.so* | \
+ sed 's#^./*#/#g' > $outdir/filelist.plugins-base && \
+popd >& /dev/null
+
+# Generate list of header files, filtering ones that go to subpackages.
+outdir="`pwd`"
+pushd $RPM_BUILD_ROOT >& /dev/null && \
+ find ./%{_includedir}/murphy | \
+ egrep -v '((pulse)|(ecore)|(glib)|(qt))-glue' | \
+ sed 's#^./*#/#g' > $outdir/filelist.devel-includes && \
+popd >& /dev/null
+
+# Replace the default sample/test config files with the packaging ones.
+cp packaging.in/murphy-lua.conf $RPM_BUILD_ROOT%{_sysconfdir}/murphy/murphy.conf
+cp packaging.in/murphy.lua $RPM_BUILD_ROOT%{_sysconfdir}/murphy/murphy.lua
+
+# Copy plugin configuration files in place.
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/murphy/plugins/amb
+cp packaging.in/amb-config.lua \
+ $RPM_BUILD_ROOT%{_sysconfdir}/murphy/plugins/amb/config.lua
+
+# Copy the systemd service file in place.
+mkdir -p $RPM_BUILD_ROOT%{systemddir}/system
+cp packaging.in/murphyd.service $RPM_BUILD_ROOT%{systemddir}/system
+
+%if %{?_with_dbus:1}%{!?_with_dbus:0}
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/dbus-1/system.d
+cp packaging.in/org.Murphy.conf $RPM_BUILD_ROOT%{_sysconfdir}/dbus-1/system.d/org.Murphy.conf
+%endif
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%post
+/bin/systemctl enable murphyd.service
+
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+%post core
+%endif
+ldconfig
+
+%postun
+if [ "$1" = "0" ]; then
+ /bin/systemctl disable murphyd.service
+fi
+
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+%postun core
+%endif
+ldconfig
+
+%if %{?_with_glib:1}%{!?_with_glib:0}
+%post glib
+ldconfig
+
+%postun glib
+ldconfig
+%endif
+
+%if %{?_with_pulse:1}%{!?_with_pulse:0}
+%post pulse
+ldconfig
+
+%postun pulse
+ldconfig
+%endif
+
+%if %{?_with_ecore:1}%{!?_with_ecore:0}
+%post ecore
+ldconfig
+
+%postun ecore
+ldconfig
+%endif
+
+%if %{?_with_qt:1}%{!?_with_qt:0}
+%post qt
+lfconfig
+
+%postun qt
+ldconfig
+%endif
+
+%if %{?_with_squashpkg:1}%{!?_with_squashpkg:0}
+%files -f filelist.plugins-base
+%else
+%files
+%endif
+%defattr(-,root,root,-)
+%{_bindir}/murphyd
+%config %{_sysconfdir}/murphy
+%{systemddir}/system/murphyd.service
+%if %{?_with_audiosession:1}%{!?_with_audiosession:0}
+%{_sbindir}/asm-bridge
+%endif
+%if %{?_with_dbus:1}%{!?_with_dbus:0}
+%{_sysconfdir}/dbus-1/system.d
+%config %{_sysconfdir}/dbus-1/system.d/org.Murphy.conf
+%endif
+%if %{?_with_websockets:1}%{!?_with_websockets:0}
+%{_datadir}/murphy
+%endif
+
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+%files core
+%defattr(-,root,root,-)
+%endif
+%{_libdir}/libmurphy-common.so.*
+%{_libdir}/libmurphy-core.so.*
+%{_libdir}/libmurphy-resolver.so.*
+%{_libdir}/libmurphy-resource.so.*
+%{_libdir}/libmurphy-resource-backend.so.*
+%if %{?_with_lua:1}%{!?_with_lua:0}
+%{_libdir}/libmurphy-lua-utils.so.*
+%{_libdir}/libmurphy-lua-decision.so.*
+%endif
+%{_libdir}/libmurphy-domain-controller.so.*
+%{_libdir}/murphy/*.so.*
+%{_libdir}/libbreedline*.so.*
+%if %{?_with_dbus:1}%{!?_with_dbus:0}
+%{_libdir}/libmurphy-dbus.so.*
+%endif
+
+%if %{?_with_squashpkg:0}%{!?_with_squashpkg:1}
+%files plugins-base -f filelist.plugins-base
+%defattr(-,root,root,-)
+%endif
+%{_libdir}/murphy/plugins
+
+%files devel -f filelist.devel-includes
+%defattr(-,root,root,-)
+# %{_includedir}/murphy/config.h
+# %{_includedir}/murphy/common.h
+# #%{_includedir}/murphy/core.h
+# %{_includedir}/murphy/common
+# %{_includedir}/murphy/core
+# %{_includedir}/murphy/resolver
+# %{_includedir}/murphy/resource
+# # hmmm... should handle disabled plugins properly.
+# %{_includedir}/murphy/domain-control
+# %{_includedir}/murphy/plugins
+%{_includedir}/murphy-db
+%{_libdir}/libmurphy-common.so
+%{_libdir}/libmurphy-core.so
+%{_libdir}/libmurphy-resolver.so
+%{_libdir}/libmurphy-resource.so
+%{_libdir}/libmurphy-resource-backend.so
+%if %{?_with_lua:1}%{!?_with_lua:0}
+%{_libdir}/libmurphy-lua-utils.so
+%{_libdir}/libmurphy-lua-decision.so
+%endif
+%{_libdir}/libmurphy-domain-controller.so
+%{_libdir}/murphy/*.so
+%{_libdir}/pkgconfig/murphy-common.pc
+%{_libdir}/pkgconfig/murphy-core.pc
+%{_libdir}/pkgconfig/murphy-resolver.pc
+#%{_libdir}/pkgconfig/murphy-resource.pc
+%if %{?_with_lua:1}%{!?_with_lua:0}
+%{_libdir}/pkgconfig/murphy-lua-utils.pc
+%{_libdir}/pkgconfig/murphy-lua-decision.pc
+%endif
+%{_libdir}/pkgconfig/murphy-domain-controller.pc
+%{_libdir}/pkgconfig/murphy-db.pc
+%{_libdir}/pkgconfig/murphy-resource.pc
+%{_includedir}/breedline
+%{_libdir}/libbreedline*.so
+%{_libdir}/pkgconfig/breedline*.pc
+%if %{?_with_dbus:1}%{!?_with_dbus:0}
+#%{_includedir}/murphy/dbus
+%{_libdir}/libmurphy-dbus.so
+%{_libdir}/pkgconfig/murphy-dbus.pc
+%endif
+
+%files doc
+%defattr(-,root,root,-)
+%doc %{_docdir}/../murphy/AUTHORS
+%doc %{_docdir}/../murphy/CODING-STYLE
+%license %{_docdir}/../murphy/COPYING
+%doc %{_docdir}/../murphy/ChangeLog
+%doc %{_docdir}/../murphy/INSTALL
+%doc %{_docdir}/../murphy/NEWS
+%doc %{_docdir}/../murphy/README
+
+%if %{?_with_pulse:1}%{!?_with_pulse:0}
+%files pulse
+%defattr(-,root,root,-)
+%{_libdir}/libmurphy-pulse.so.*
+
+%files pulse-devel
+%defattr(-,root,root,-)
+%{_includedir}/murphy/common/pulse-glue.h
+%{_libdir}/libmurphy-pulse.so
+%{_libdir}/pkgconfig/murphy-pulse.pc
+%endif
+
+%if %{?_with_ecore:1}%{!?_with_ecore:0}
+%files ecore
+%defattr(-,root,root,-)
+%{_libdir}/libmurphy-ecore.so.*
+
+%files ecore-devel
+%defattr(-,root,root,-)
+%{_includedir}/murphy/common/ecore-glue.h
+%{_libdir}/libmurphy-ecore.so
+%{_libdir}/pkgconfig/murphy-ecore.pc
+%endif
+
+%if %{?_with_glib:1}%{!?_with_glib:0}
+%files glib
+%defattr(-,root,root,-)
+%{_libdir}/libmurphy-glib.so.*
+
+%files glib-devel
+%defattr(-,root,root,-)
+%{_includedir}/murphy/common/glib-glue.h
+%{_libdir}/libmurphy-glib.so
+%{_libdir}/pkgconfig/murphy-glib.pc
+%endif
+
+%if %{?_with_qt:1}%{!?_with_qt:0}
+%files qt
+%defattr(-,root,root,-)
+%{_libdir}/libmurphy-qt.so.*
+
+%files qt-devel
+%defattr(-,root,root,-)
+%{_includedir}/murphy/common/qt-glue.h
+%{_libdir}/libmurphy-qt.so
+%{_libdir}/pkgconfig/murphy-qt.pc
+%endif
+
+%files tests
+%defattr(-,root,root,-)
+%{_bindir}/resource-client
+%{_bindir}/resource-api-test
+%{_bindir}/resource-api-fuzz
+%{_bindir}/test-domain-controller
+%{_bindir}/murphy-console
+
+%changelog
+* Tue Nov 27 2012 Krisztian Litkey <krisztian.litkey@intel.com> -
+- Initial build for 2.0alpha.
--- /dev/null
+d /tmp/murphy 0755 app app - -
+d /var/run/murphy/processes 0755 app app - -
--- /dev/null
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: skeleton
+# Required-Start: $remote_fs $syslog
+# Required-Stop: $remote_fs $syslog
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Example initscript
+# Description: This file should be used to construct scripts to be
+# placed in /etc/init.d.
+### END INIT INFO
+
+# Author: Foo Bar <foo@bar.org>
+#
+
+# Do NOT "set -e"
+
+# PATH should only include /usr/* if it runs after the mountnfs.sh script
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="Murphy Daemon"
+NAME=murphyd
+DAEMON=/usr/bin/$NAME
+DAEMON_ARGS="-t dlog -vvv"
+SCRIPTNAME=/etc/init.d/$NAME
+
+# Exit if the package is not installed
+[ -x "$DAEMON" ] || exit 0
+
+# Read configuration variable file if it is present
+[ -r /etc/default/$NAME ] && . /etc/default/$NAME
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB log_* functions.
+# Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
+. /lib/lsb/init-functions
+
+#
+# Function that starts the daemon/service
+#
+do_start()
+{
+ mkdir -p /tmp/murphy
+ mkdir -p /var/run/murphy/processes
+ # Return
+ # 0 if daemon has been started
+ # 1 if daemon was already running
+ # 2 if daemon could not be started
+ start-stop-daemon --start --quiet --exec $DAEMON --test > /dev/null \
+ || return 1
+ start-stop-daemon --start --quiet --exec $DAEMON -- \
+ $DAEMON_ARGS \
+ || return 2
+}
+
+#
+# Function that stops the daemon/service
+#
+do_stop()
+{
+ # Return
+ # 0 if daemon has been stopped
+ # 1 if daemon was already stopped
+ # 2 if daemon could not be stopped
+ # other if a failure occurred
+ start-stop-daemon --stop -s 9 --quiet --oknodo --exec $DAEMON
+ RETVAL="$?"
+
+ # clean up
+ rm -f /var/run/murphy/processes/*
+
+ [ "$RETVAL" = 2 ] && return 2
+ # Wait for children to finish too if this is a daemon that forks
+ # and if the daemon is only ever run from this initscript.
+ # If the above conditions are not satisfied then add some other code
+ # that waits for the process to drop all resources that could be
+ # needed by services started subsequently. A last resort is to
+ # sleep for some time.
+ start-stop-daemon --stop --quiet --oknodo --exec $DAEMON
+ [ "$?" = 2 ] && return 2
+ return "$RETVAL"
+}
+
+#
+# Function that sends a SIGHUP to the daemon/service
+#
+do_reload() {
+ #
+ # If the daemon can reload its configuration without
+ # restarting (for example, when it is sent a SIGHUP),
+ # then implement that here.
+ #
+ start-stop-daemon --stop --signal 1 --quiet --name $NAME
+ return 0
+}
+
+case "$1" in
+ start)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
+ do_start
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ stop)
+ [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+ 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+ esac
+ ;;
+ status)
+ status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
+ ;;
+ #reload|force-reload)
+ #
+ # If do_reload() is not implemented then leave this commented out
+ # and leave 'force-reload' as an alias for 'restart'.
+ #
+ #log_daemon_msg "Reloading $DESC" "$NAME"
+ #do_reload
+ #log_end_msg $?
+ #;;
+ restart|force-reload)
+ #
+ # If the "reload" option is implemented then remove the
+ # 'force-reload' alias
+ #
+ log_daemon_msg "Restarting $DESC" "$NAME"
+ do_stop
+ case "$?" in
+ 0|1)
+ do_start
+ case "$?" in
+ 0) log_end_msg 0 ;;
+ 1) log_end_msg 1 ;; # Old process is still running
+ *) log_end_msg 1 ;; # Failed to start
+ esac
+ ;;
+ *)
+ # Failed to stop
+ log_end_msg 1
+ ;;
+ esac
+ ;;
+ *)
+ #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
+ echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
+ exit 3
+ ;;
+esac
+
+:
--- /dev/null
+[Unit]
+Description=Murphy Resource Policy Daemon
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/murphyd -t systemd -vvv -f
+KillSignal=SIGTERM
+
+[Install]
+WantedBy=default.target
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<busconfig>
+ <policy group="@TZ_SYS_USER_GROUP@">
+ <allow own="org.Murphy"/>
+ <allow receive_sender="org.Murphy"/>
+ <allow send_destination="org.Murphy"/>
+ </policy>
+ <policy context="default">
+ <allow receive_sender="org.Murphy"/>
+ <allow send_destination="org.Murphy"/>
+ </policy>
+</busconfig>
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_" />
+ </request>
+</manifest>
--- /dev/null
+# These are on by default, unless explicitly disabled.
+%bcond_without lua
+#%bcond_without pulse
+#%bcond_without ecore
+%bcond_without glib
+%bcond_without dbus
+#%bcond_without telephony
+#%bcond_without audiosession
+#%bcond_without websockets
+#%bcond_without smack
+#%bcond_without sysmon
+
+# These are off by default, unless explicitly enabled.
+#
+# Notes:
+# By default we build with distro-default compilation flags which
+# enables optimizations. If you want to build with full debugging
+# ie. with optimization turned off and full debug info (-O0 -g3)
+# pass '--with debug' to rpmbuild on the command line. Similary
+# you can chose to compile with/without pulse, ecore, glib, qt,
+# dbus, and telephony support.
+#
+# qt is the macro for controlling Qt4 support, which is not supported
+# in Tizen any more. qt5 is the corrsponding macro for controlling
+# Qt5 support.
+#
+#%bcond_with icosyscon
+#%bcond_with qt
+#%bcond_with debug
+
+
+Summary: Resource policy framework
+Name: murphy
+Version: 0.0.74
+Release: 0
+License: BSD-3-Clause
+Group: System/Service
+URL: http://01.org/murphy/
+Source0: %{name}-%{version}.tar.gz
+Source1001: %{name}.manifest
+
+Requires(post): /bin/systemctl
+#Requires(post): libcap-tools
+Requires(postun): /bin/systemctl
+
+BuildRequires: flex
+BuildRequires: bison
+BuildRequires: pkgconfig(lua)
+BuildRequires: pkgconfig(libsystemd-daemon)
+BuildRequires: pkgconfig(libsystemd-journal)
+#BuildRequires: pkgconfig(libcap)
+#BuildRequires: pkgconfig(libtzplatform-config)
+#%if %{with pulse}
+#BuildRequires: pkgconfig(libpulse)
+#%endif
+#%if %{with ecore}
+#BuildRequires: pkgconfig(ecore)
+#BuildRequires: mesa-libEGL
+#BuildRequires: mesa-libGLESv2
+#%endif
+%if %{with glib}
+BuildRequires: pkgconfig(glib-2.0)
+%endif
+#%if %{with qt}
+#BuildRequires: pkgconfig(QtCore)
+#%endif
+%if %{with dbus}
+BuildRequires: pkgconfig(dbus-1)
+%endif
+#%if %{with telephony}
+#BuildRequires: pkgconfig(ofono)
+#%endif
+#%if %{with audiosession}
+#BuildRequires: pkgconfig(audio-session-mgr)
+#BuildRequires: pkgconfig(aul)
+#%endif
+#%if %{with websockets}
+#BuildRequires: libwebsockets-devel
+#%endif
+BuildRequires: pkgconfig(json)
+#%if %{with smack}
+#BuildRequires: pkgconfig(libsmack)
+#%endif
+#%if %{with icosyscon}
+#BuildRequires: ico-uxf-weston-plugin-devel
+#BuildRequires: weston-ivi-shell-devel
+#BuildRequires: genivi-shell-devel
+#BuildRequires: pkgconfig(ail)
+#BuildRequires: pkgconfig(aul)
+#BuildRequires: libxml2-devel
+#%endif
+
+%description
+This package contains the basic Murphy daemon.
+
+%package devel
+Summary: The header files and libraries needed for Murphy development
+Group: System/Libraries
+Requires: %{name} = %{version}-%{release}
+Requires: libjson-devel
+
+%description devel
+This package contains header files and libraries necessary for development.
+
+%package doc
+Summary: Documentation for Murphy
+Group: SDK/Documentation
+
+%description doc
+This package contains documentation.
+
+#%if %{with pulse}
+#%package pulse
+#Summary: Murphy PulseAudio mainloop integration
+#Group: System/Libraries
+#Requires: %{name} = %{version}-%{release}
+
+#%description pulse
+#This package contains the Murphy PulseAudio mainloop integration runtime files.
+
+#%package pulse-devel
+#Summary: Murphy PulseAudio mainloop integration development files
+#Group: System/Libraries
+#Requires: %{name}-pulse = %{version}-%{release}
+#Requires: %{name} = %{version}-%{release}
+
+#%description pulse-devel
+#This package contains the Murphy PulseAudio mainloop integration development
+#files.
+#%endif
+
+#%if %{with ecore}
+#%package ecore
+#Summary: Murphy EFL/ecore mainloop integration
+#Group: System/Libraries
+#Requires: %{name} = %{version}-%{release}
+
+#%description ecore
+#This package contains the Murphy EFL/ecore mainloop integration runtime files.
+
+#%package ecore-devel
+#Summary: Murphy EFL/ecore mainloop integration development files
+#Group: System/Libraries
+#Requires: %{name}-ecore = %{version}-%{release}
+#Requires: %{name} = %{version}-%{release}
+
+#%description ecore-devel
+#This package contains the Murphy EFL/ecore mainloop integration development
+#files.
+#%endif
+
+%if %{with glib}
+%package glib
+Summary: Murphy glib mainloop integration
+Group: System/Libraries
+Requires: %{name} = %{version}-%{release}
+
+%description glib
+This package contains the Murphy glib mainloop integration runtime files.
+
+%package glib-devel
+Summary: Murphy glib mainloop integration development files
+Group: System/Libraries
+Requires: %{name}-glib = %{version}-%{release}
+Requires: %{name} = %{version}-%{release}
+
+%description glib-devel
+This package contains the Murphy glib mainloop integration development
+files.
+%endif
+
+#%if %{with qt}
+#%package qt
+#Summary: Murphy Qt mainloop integration
+#Group: System/Libraries
+#Requires: %{name} = %{version}-%{release}
+
+#%description qt
+#This package contains the Murphy Qt mainloop integration runtime files.
+
+#%package qt-devel
+#Summary: Murphy Qt mainloop integration development files
+#Group: System/Libraries
+#Requires: %{name}-qt = %{version}-%{release}
+#Requires: %{name} = %{version}-%{release}
+#
+#%description qt-devel
+#This package contains the Murphy Qt mainloop integration development
+#files.
+#%endif
+
+#%package gam
+#Summary: Murphy support for Genivi Audio Manager
+#Group: System/Libraries
+#Requires: %{name} = %{version}-%{release}
+
+#%description gam
+#This package contains the Murphy plugins for necessary for supporting
+#Genivi Audio Manager.
+
+#%package gam-devel
+#Summary: Murphy support for Genivi Audio Manager development files
+#Group: System/Libraries
+#Requires: %{name}-gam = %{version}-%{release}
+
+#%description gam-devel
+#This package contains development files for Murphy Genivi Audio Manager
+#plugins.
+
+%package tests
+Summary: Various test binaries for Murphy
+Group: System/Testing
+Requires: %{name} = %{version}-%{release}
+Requires: %{name} = %{version}-%{release}
+
+%description tests
+This package contains various test binaries for Murphy.
+
+#%if %{with icosyscon}
+#%package system-controller
+#Summary: Murphy IVI System Controller plugin
+#Group: System/Service
+#Requires: ico-uxf-homescreen
+#Conflicts: murphy-ivi-resource-manager
+#Provides: system-controller
+#Conflicts: ico-uxf-homescreen-system-controller
+
+#%description system-controller
+#This package contains the Murphy IVI resource manager plugin.
+#%endif
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+#%if %{with icosyscon}
+#echo "Build with icosyscon"
+#%else
+#echo "Build without icosyscon"
+#%endif
+
+%build
+%if %{with debug}
+export CFLAGS="-O0 -g3"
+V="V=1"
+%endif
+
+CONFIG_OPTIONS=""
+DYNAMIC_PLUGINS="domain-control,system-controller"
+
+#%if %{with pulse}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-pulse"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-pulse"
+#%endif
+
+#%if %{with ecore}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-ecore"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-ecore"
+#%endif
+
+%if %{with glib}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-glib"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-glib"
+%endif
+
+#%if %{with qt}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-qt"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-qt"
+#%endif
+
+%if %{with dbus}
+CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-libdbus"
+%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-libdbus"
+%endif
+
+#%if %{with telephony}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-gpl --enable-telephony"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-telephony"
+#%endif
+
+#%if %{with audiosession}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-resource-asm"
+#DYNAMIC_PLUGINS="$DYNAMIC_PLUGINS,resource-asm"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-resource-asm"
+#%endif
+
+#%if %{with websockets}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-websockets"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-websockets"
+#%endif
+
+#%if %{with smack}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-smack"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-smack"
+#%endif
+
+#%if %{with icosyscon}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-system-controller"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-system-controller"
+#%endif
+
+#%if %{with sysmon}
+#CONFIG_OPTIONS="$CONFIG_OPTIONS --enable-system-monitor"
+#%else
+CONFIG_OPTIONS="$CONFIG_OPTIONS --disable-system-monitor"
+#%endif
+
+./bootstrap
+%configure $CONFIG_OPTIONS --with-dynamic-plugins=$DYNAMIC_PLUGINS
+%__make clean
+%__make %{?_smp_mflags} $V
+
+%install
+rm -rf %{buildroot}
+%make_install
+
+# Make sure we have a plugin dir even if all the basic plugins
+# are configured to be built in.
+mkdir -p %{buildroot}%{_libdir}/murphy/plugins
+
+# Get rid of any *.la files installed by libtool.
+rm -f %{buildroot}%{_libdir}/*.la
+
+# Clean up also the murphy DB installation.
+rm -f %{buildroot}%{_libdir}/murphy/*.la
+
+# Generate list of linkedin plugins (depends on the configuration).
+outdir="`pwd`"
+pushd %{buildroot}
+find ./%{_libdir} -name libmurphy-plugin-*.so* | \
+sed 's#^./*#/#g' > $outdir/filelist.plugins-base
+popd
+echo "Found the following linked-in plugin files:"
+cat $outdir/filelist.plugins-base | sed 's/^/ /g'
+
+# Generate list of header files, filtering ones that go to subpackages.
+outdir="`pwd`"
+pushd %{buildroot}
+find ./%{_includedir}/murphy | \
+grep -E -v '((pulse)|(ecore)|(glib)|(qt))-glue' | \
+sed 's#^./*#/#g' > $outdir/filelist.devel-includes
+popd
+
+# Replace the default sample/test config files with the packaging ones.
+rm -f %{buildroot}%{_sysconfdir}/murphy/*
+cp packaging.in/murphy-lua.conf %{buildroot}%{_sysconfdir}/murphy/murphy.conf
+cp packaging.in/murphy.lua %{buildroot}%{_sysconfdir}/murphy/murphy.lua
+
+# Copy plugin configuration files in place.
+#mkdir -p %{buildroot}%{_sysconfdir}/murphy/plugins/amb
+#cp packaging.in/amb-config.lua \
+#%{buildroot}%{_sysconfdir}/murphy/plugins/amb/config.lua
+
+# Copy tmpfiles.d config file in place
+mkdir -p %{buildroot}%{_tmpfilesdir}
+cp packaging.in/murphyd.conf %{buildroot}%{_tmpfilesdir}
+
+# Copy the systemd files in place.
+#mkdir -p %%{buildroot}%%{_unitdir}
+mkdir -p %{buildroot}%{_unitdir_user}
+cp packaging.in/murphyd.service %{buildroot}%{_unitdir_user}
+
+%if %{with dbus}
+mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d
+sed "s/@TZ_SYS_USER_GROUP@/%{TZ_SYS_USER_GROUP}/g" \
+ packaging.in/org.Murphy.conf.in > packaging.in/org.Murphy.conf
+cp packaging.in/org.Murphy.conf \
+ %{buildroot}%{_sysconfdir}/dbus-1/system.d/org.Murphy.conf
+%endif
+
+# copy (experimental) GAM resource backend configuration files
+#mkdir -p %{buildroot}%{_sysconfdir}/murphy/gam
+#cp packaging.in/gam-*.names packaging.in/gam-*.tree \
+# %{buildroot}%{_sysconfdir}/murphy/gam
+
+%clean
+rm -rf %{buildroot}
+
+%post
+/bin/systemctl --user enable --global murphyd.service
+setcap 'cap_net_admin=+ep' %{_bindir}/murphyd
+ldconfig
+
+%postun
+if [ "$1" = "0" ]; then
+systemctl --user disable --global murphyd.service
+fi
+ldconfig
+
+%if %{with glib}
+%post glib
+ldconfig
+
+%postun glib
+ldconfig
+%endif
+
+#%if %{with pulse}
+#%post pulse
+#ldconfig
+
+#%postun pulse
+#ldconfig
+#%endif
+
+#%if %{with ecore}
+#%post ecore
+#ldconfig
+
+#%postun ecore
+#ldconfig
+#%endif
+
+#%if %{with qt}
+#%post qt
+#ldconfig
+
+#%postun qt
+#ldconfig
+#%endif
+
+#%post gam
+#ldconfig
+
+#%postun gam
+#ldconfig
+
+%files -f filelist.plugins-base
+%defattr(-,root,root,-)
+%manifest murphy.manifest
+%{_bindir}/murphyd
+%config %{_sysconfdir}/murphy
+%{_unitdir_user}/murphyd.service
+%{_tmpfilesdir}/murphyd.conf
+#%if %{with audiosession}
+#%{_sbindir}/asm-bridge
+#%endif
+%if %{with dbus}
+%{_sysconfdir}/dbus-1/system.d
+%config %{_sysconfdir}/dbus-1/system.d/org.Murphy.conf
+%endif
+#%if %{with websockets}
+#%{_datadir}/murphy
+#%endif
+
+%{_libdir}/libmurphy-common.so.*
+%{_libdir}/libmurphy-core.so.*
+%{_libdir}/libmurphy-resolver.so.*
+%{_libdir}/libmurphy-resource.so.*
+%{_libdir}/libmurphy-resource-backend.so.*
+%if %{with lua}
+%{_libdir}/libmurphy-lua-utils.so.*
+%{_libdir}/libmurphy-lua-decision.so.*
+%endif
+%{_libdir}/libmurphy-domain-controller.so.*
+%{_libdir}/murphy/*.so.*
+%{_libdir}/libbreedline*.so.*
+%if %{with dbus}
+%{_libdir}/libmurphy-libdbus.so.*
+%{_libdir}/libmurphy-dbus-libdbus.so.*
+%endif
+#%if %{with sysmon}
+#%{_libdir}/libmurphy-libdbus.so.*
+#%endif
+
+%{_libdir}/murphy/plugins/plugin-domain-control.so
+#%{_libdir}/murphy/plugins/plugin-resource-asm.so
+%{_libdir}/murphy/plugins/plugin-resource-native.so
+
+%files devel -f filelist.devel-includes
+%defattr(-,root,root,-)
+%{_includedir}/murphy-db
+%{_libdir}/libmurphy-common.so
+%{_libdir}/libmurphy-core.so
+%{_libdir}/libmurphy-resolver.so
+%{_libdir}/libmurphy-resource.so
+%{_libdir}/libmurphy-resource-backend.so
+%if %{with lua}
+%{_libdir}/libmurphy-lua-utils.so
+%{_libdir}/libmurphy-lua-decision.so
+%endif
+%{_libdir}/libmurphy-domain-controller.so
+%{_libdir}/murphy/*.so
+%{_libdir}/pkgconfig/murphy-common.pc
+%{_libdir}/pkgconfig/murphy-core.pc
+%{_libdir}/pkgconfig/murphy-resolver.pc
+%if %{with lua}
+%{_libdir}/pkgconfig/murphy-lua-utils.pc
+%{_libdir}/pkgconfig/murphy-lua-decision.pc
+%endif
+%{_libdir}/pkgconfig/murphy-domain-controller.pc
+%{_libdir}/pkgconfig/murphy-db.pc
+%{_libdir}/pkgconfig/murphy-resource.pc
+%{_includedir}/breedline
+%{_libdir}/libbreedline*.so
+%{_libdir}/pkgconfig/breedline*.pc
+%if %{with dbus}
+%{_libdir}/libmurphy-libdbus.so
+%{_libdir}/libmurphy-dbus-libdbus.so
+%{_libdir}/pkgconfig/murphy-libdbus.pc
+%{_libdir}/pkgconfig/murphy-dbus-libdbus.pc
+%endif
+
+%files doc
+%defattr(-,root,root,-)
+%doc %{_datadir}/doc/murphy/AUTHORS
+%doc %{_datadir}/doc/murphy/CODING-STYLE
+%doc %{_datadir}/doc/murphy/ChangeLog
+%doc %{_datadir}/doc/murphy/NEWS
+%doc %{_datadir}/doc/murphy/README
+%license COPYING LICENSE-BSD
+
+#%if %{with pulse}
+#%files pulse
+#%defattr(-,root,root,-)
+#%{_libdir}/libmurphy-pulse.so.*
+#%manifest murphy.manifest
+
+#%files pulse-devel
+#%defattr(-,root,root,-)
+#%{_includedir}/murphy/common/pulse-glue.h
+#%{_libdir}/libmurphy-pulse.so
+#%{_libdir}/pkgconfig/murphy-pulse.pc
+#%endif
+
+#%if %{with ecore}
+#%files ecore
+#%defattr(-,root,root,-)
+#%{_libdir}/libmurphy-ecore.so.*
+#%manifest murphy.manifest
+
+#%files ecore-devel
+#%defattr(-,root,root,-)
+#%{_includedir}/murphy/common/ecore-glue.h
+#%{_libdir}/libmurphy-ecore.so
+#%{_libdir}/pkgconfig/murphy-ecore.pc
+#%endif
+
+%if %{with glib}
+%files glib
+%defattr(-,root,root,-)
+%{_libdir}/libmurphy-glib.so.*
+%manifest murphy.manifest
+
+%files glib-devel
+%defattr(-,root,root,-)
+%{_includedir}/murphy/common/glib-glue.h
+%{_libdir}/libmurphy-glib.so
+%{_libdir}/pkgconfig/murphy-glib.pc
+%endif
+
+#%if %{with qt}
+#%files qt
+#%defattr(-,root,root,-)
+#%{_libdir}/libmurphy-qt.so.*
+#%manifest murphy.manifest
+
+#%files qt-devel
+#%defattr(-,root,root,-)
+#%{_includedir}/murphy/common/qt-glue.h
+#%{_libdir}/libmurphy-qt.so
+#%{_libdir}/pkgconfig/murphy-qt.pc
+#%endif
+
+#%files gam
+#%defattr(-,root,root,-)
+#%{_libdir}/libmurphy-decision-tree.so.*
+#%{_libdir}/libmurphy-decision-tree.so.0.0.0
+#%{_libdir}/murphy/plugins/plugin-gam-resource-manager.so
+
+#%files gam-devel
+#%defattr(-,root,root,-)
+#%{_bindir}/decision-test
+#%{_bindir}/pattern-generator
+#%{_libdir}/libmurphy-decision-tree.so
+
+%files tests
+%defattr(-,root,root,-)
+%{_bindir}/resource-client
+%{_bindir}/resource-api-test
+%{_bindir}/resource-api-fuzz
+%{_bindir}/resource-context-create
+%{_bindir}/test-domain-controller
+%{_bindir}/murphy-console
+%manifest murphy.manifest
+
+#%if %{with icosyscon}
+#%files system-controller
+#%defattr(-,root,root,-)
+#%{_libdir}/murphy/plugins/plugin-system-controller.so
+#%manifest murphy.manifest
+#%endif
--- /dev/null
+*** rpmbuild/SPECS/lua.spec.orig 2014-03-09 23:16:11.119234628 +0200
+--- rpmbuild/SPECS/lua.spec 2014-03-09 23:16:20.243251349 +0200
+***************
+*** 1,3 ****
+--- 1,6 ----
++ %global lua_version 5.1.4
++ %global _prefix /usr/local/lua-%{lua_version}
++
+ Name: lua
+ Version: 5.1.4
+ Release: 12%{?dist}
+***************
+*** 63,68 ****
+--- 65,72 ----
+ make %{?_smp_mflags} LIBS="-lm -ldl" luac_LDADD="liblua.la -lm -ldl"
+ # also remove readline from lua.pc
+ sed -i 's/-lreadline -lncurses //g' etc/lua.pc
++ # add path to %_libdir to lua.pc
++ sed -i 's#^Libs: #Libs: -L${libdir} #g' etc/lua.pc
+
+
+ %install
+***************
+*** 72,81 ****
+--- 76,102 ----
+ mkdir -p $RPM_BUILD_ROOT%{_libdir}/lua/5.1
+ mkdir -p $RPM_BUILD_ROOT%{_datadir}/lua/5.1
+
++ # construct a config file to help the dynamic linker find us
++ mkdir -p $RPM_BUILD_ROOT/etc/ld.so.conf.d
++ echo "%{_libdir}" > $RPM_BUILD_ROOT/etc/ld.so.conf.d/lua-%{lua_version}.conf
++
++ # add a version-specific symlink for pkgconfig to find us
++ case %{_libdir} in
++ *lib64*) lib=lib64;;
++ *) lib=lib
++ esac
++ mkdir -p $RPM_BUILD_ROOT/usr/$lib/pkgconfig
++ ln -sf %_libdir/pkgconfig/lua.pc \
++ $RPM_BUILD_ROOT/usr/$lib/pkgconfig/lua-%{lua_version}.pc
+
+ %clean
+ rm -rf $RPM_BUILD_ROOT
+
++ %post
++ ldconfig
++
++ %postun
++ ldconfig
+
+ %files
+ %defattr(-,root,root,-)
+***************
+*** 87,93 ****
+ %dir %{_libdir}/lua/5.1
+ %dir %{_datadir}/lua
+ %dir %{_datadir}/lua/5.1
+!
+
+ %files devel
+ %defattr(-,root,root,-)
+--- 108,114 ----
+ %dir %{_libdir}/lua/5.1
+ %dir %{_datadir}/lua
+ %dir %{_datadir}/lua/5.1
+! /etc/ld.so.conf.d/lua-%{lua_version}.conf
+
+ %files devel
+ %defattr(-,root,root,-)
+***************
+*** 95,100 ****
+--- 116,122 ----
+ %{_includedir}/l*.hpp
+ %{_libdir}/liblua.so
+ %{_libdir}/pkgconfig/*.pc
++ /usr/lib*/pkgconfig/lua-*.pc
+
+ %files static
+ %defattr(-,root,root,-)
--- /dev/null
+*** rpmbuild/SPECS/lua.spec.orig 2013-10-23 19:30:44.000000000 +0300
+--- rpmbuild/SPECS/lua.spec 2014-03-09 23:28:23.064213927 +0200
+***************
+*** 1,4 ****
+--- 1,6 ----
+ %global major_version 5.2
++ %global lua_version 5.2.2
++ %global _prefix /usr/local/lua-%{lua_version}
+
+ Name: lua
+ Version: %{major_version}.2
+***************
+*** 65,70 ****
+--- 67,74 ----
+ # hack so that only /usr/bin/lua gets linked with readline as it is the
+ # only one which needs this and otherwise we get License troubles
+ make %{?_smp_mflags} LIBS="-lm -ldl" luac_LDADD="liblua.la -lm -ldl"
++ # add path to %_libdir to lua.pc
++ sed -i 's#^Libs: #Libs: -L${libdir} #g' src/lua.pc
+
+
+ %install
+***************
+*** 73,78 ****
+--- 77,102 ----
+ mkdir -p $RPM_BUILD_ROOT%{_libdir}/lua/%{major_version}
+ mkdir -p $RPM_BUILD_ROOT%{_datadir}/lua/%{major_version}
+
++ # construct a config file to help the dynamic linker find us
++ mkdir -p $RPM_BUILD_ROOT/etc/ld.so.conf.d
++ echo "%{_libdir}" > $RPM_BUILD_ROOT/etc/ld.so.conf.d/lua-%{lua_version}.conf
++
++ # add a version-specific symlink for pkgconfig to find us
++ case %{_libdir} in
++ *lib64*) lib=lib64;;
++ *) lib=lib
++ esac
++ mkdir -p $RPM_BUILD_ROOT/usr/$lib/pkgconfig
++ ln -sf %_libdir/pkgconfig/lua.pc \
++ $RPM_BUILD_ROOT/usr/$lib/pkgconfig/lua-%{lua_version}.pc
++
++
++ %post
++ ldconfig
++
++ %postun
++ ldconfig
++
+
+ %files
+ %doc README doc/*.html doc/*.css doc/*.gif doc/*.png
+***************
+*** 90,95 ****
+--- 114,121 ----
+ %{_includedir}/l*.hpp
+ %{_libdir}/liblua.so
+ %{_libdir}/pkgconfig/*.pc
++ /usr/lib*/pkgconfig/lua-*.pc
++ /etc/ld.so.conf.d/lua-%{lua_version}.conf
+
+ %files static
+ %{_libdir}/*.a
--- /dev/null
+#!/bin/bash
+
+# Replace TABs with sequences of 8 spaces in all given files.
+replace_tabs() {
+ local _file _tmp
+
+ for _file in $*; do
+ _tmp=$_file.tabs
+ cp $_file $_tmp && \
+ cat $_tmp | \
+ sed 's/\t/ /g' > $_file && \
+ rm -f $_tmp
+ done
+}
+
+
+# Replaces lines containing only spaces with empty lines in all given files.
+strip_empty_lines() {
+ local _file _tmp
+
+ for _file in $*; do
+ _tmp=$_file.spaces
+ cp $_file $_tmp && \
+ cat $_tmp | \
+ sed 's/^ [ ]*$//g' > $_file && \
+ rm -f $_tmp
+ done
+}
+
+
+# Strip trailing white space from all the given files.
+strip_trailing_ws() {
+ local _file _tmp
+
+ for _file in $*; do
+ _tmp=$_file.spaces
+ cp $_file $_tmp && \
+ cat $_tmp | \
+ sed 's/ *$//g' > $_file && \
+ rm -f $_tmp
+ done
+}
+
+
+# Clean up TABS and empty lines in all given or found files.
+if [ -n "$*" ]; then
+ replace_tabs $* && \
+ strip_empty_lines $* && \
+ strip_trailing_ws $*
+else
+ files=$(find . -name \*.h -o -name \*.c)
+ replace_tabs $files && \
+ strip_empty_lines $files && \
+ strip_trailing_ws $files
+fi
--- /dev/null
+SUBDIRS = murphy-db . \
+ common/tests breedline/tests core/tests \
+ core/lua-decision/tests resolver/tests \
+ daemon/tests plugins/tests
+
+AM_CFLAGS = $(WARNING_CFLAGS) $(AM_CPPFLAGS) \
+ -DSYSCONFDIR=\"@SYSCONFDIR@\" -DLIBDIR=\"@LIBDIR@\"
+MURPHY_CFLAGS =
+pkgconfigdir = ${libdir}/pkgconfig
+
+bin_PROGRAMS =
+lib_LTLIBRARIES =
+pkgconfig_DATA =
+EXTRA_DIST =
+
+QUIET_GEN = $(Q:@=@echo ' GEN '$@;)
+
+LEXCOMPILE = $(LEX) $(LFLAGS) $(AM_LFLAGS)
+YACCCOMPILE = $(YACC) $(YFLAGS) $(AM_YFLAGS)
+
+AM_CPPFLAGS = -I$(top_builddir) -I$(top_builddir)/src \
+ -I$(top_builddir)/src/murphy-db/include
+BUILT_SOURCES =
+
+###################################
+# murphy common library
+#
+
+lib_LTLIBRARIES += libmurphy-common.la
+EXTRA_DIST += common/murphy-common.pc
+pkgconfig_DATA += common/murphy-common.pc
+
+
+libmurphy_commonh_ladir = \
+ $(includedir)/murphy
+
+libmurphy_commonh_la_HEADERS = \
+ common.h \
+ config.h
+
+libmurphy_common_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_common_la_HEADERS = \
+ common/macros.h \
+ common/list.h \
+ common/log.h \
+ common/debug.h \
+ common/debug-info.h \
+ common/env.h \
+ common/mm.h \
+ common/hashtbl.h \
+ common/process.h \
+ common/mainloop.h \
+ common/utils.h \
+ common/file-utils.h \
+ common/socket-utils.h \
+ common/msg.h \
+ common/refcnt.h \
+ common/fragbuf.h \
+ common/json.h \
+ common/transport.h \
+ common/tlv.h \
+ common/native-types.h \
+ common/mask.h
+
+libmurphy_common_la_REGULAR_SOURCES = \
+ common/log.c \
+ common/debug.c \
+ common/env.c \
+ common/mm.c \
+ common/hashtbl.c \
+ common/mainloop.c \
+ common/utils.c \
+ common/file-utils.c \
+ common/socket-utils.c \
+ common/process.c \
+ common/msg.c \
+ common/fragbuf.c \
+ common/json.c \
+ common/transport.c \
+ common/stream-transport.c \
+ common/internal-transport.c \
+ common/dgram-transport.c \
+ common/tlv.c \
+ common/native-types.c
+
+libmurphy_common_la_SOURCES = \
+ $(libmurphy_common_la_REGULAR_SOURCES)
+
+libmurphy_common_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(JSON_CFLAGS)
+
+libmurphy_common_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.common \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_common_la_LIBADD = \
+ $(JSON_LIBS) \
+ -lrt
+
+libmurphy_common_la_DEPENDENCIES = \
+ linker-script.common \
+ $(filter %.la, $(libmurphy_common_la_LIBADD))
+
+libcommonincludedir = $(includedir)/murphy/common
+libcommoninclude_HEADERS = $(libmurphy_common_la_HEADERS)
+
+if WEBSOCKETS_ENABLED
+libmurphy_common_la_HEADERS += \
+ common/websocklib.h \
+ common/websocket.h
+
+libmurphy_common_la_REGULAR_SOURCES += \
+ common/websocklib.c \
+ common/websocket.c \
+ common/wsck-transport.c
+
+libmurphy_common_la_CFLAGS += \
+ $(WEBSOCKETS_CFLAGS)
+
+libmurphy_common_la_LIBADD += \
+ $(WEBSOCKETS_LIBS)
+
+EXTRA_DIST += $(webconsole_DATA);
+webconsoledir = $(datadir)/murphy/webconsole
+webconsole_DATA = \
+ plugins/console/console.js \
+ plugins/console/console.html
+endif
+
+
+# linker script generation
+linker-script.common: $(libmurphy_common_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_common_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.common
+
+###################################
+# murphy lua utilities
+#
+
+lib_LTLIBRARIES += libmurphy-lua-utils.la
+EXTRA_DIST += core/lua-utils/murphy-lua-utils.pc
+pkgconfig_DATA += core/lua-utils/murphy-lua-utils.pc
+
+libmurphy_lua_utils_ladir = \
+ $(includedir)/murphy/core/lua-utils
+
+libmurphy_lua_utils_la_HEADERS = \
+ core/lua-utils/lua-utils.h \
+ core/lua-utils/strarray.h \
+ core/lua-utils/funcbridge.h \
+ core/lua-utils/object.h \
+ core/lua-utils/error.h \
+ core/lua-utils/include.h
+
+libmurphy_lua_utils_la_REGULAR_SOURCES = \
+ core/lua-utils/lua-utils.c \
+ core/lua-utils/strarray.c \
+ core/lua-utils/funcbridge.c \
+ core/lua-utils/object.c \
+ core/lua-utils/error.c \
+ core/lua-utils/include.c
+
+libmurphy_lua_utils_la_SOURCES = \
+ $(libmurphy_lua_utils_la_REGULAR_SOURCES)
+
+libmurphy_lua_utils_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LUA_CFLAGS)
+
+libmurphy_lua_utils_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.lua-utils \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_lua_utils_la_LIBADD = \
+ libmurphy-common.la \
+ $(LUA_LIBS)
+
+libmurphy_lua_utils_la_DEPENDENCIES = \
+ linker-script.lua-utils \
+ $(filter %.la, $(libmurphy_lua_utils_la_LIBADD))
+
+# lua-utils linker script generation
+linker-script.lua-utils: $(libmurphy_lua_utils_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_lua_utils_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.lua-utils
+
+###################################
+# murphy lua decision network
+#
+
+lib_LTLIBRARIES += libmurphy-lua-decision.la
+EXTRA_DIST += core/lua-decision/murphy-lua-decision.pc
+pkgconfig_DATA += core/lua-decision/murphy-lua-decision.pc
+
+libmurphy_lua_decision_ladir = \
+ $(includedir)/murphy/core/lua-decision
+
+libmurphy_lua_decision_la_HEADERS = \
+ core/lua-decision/mdb.h \
+ core/lua-decision/element.h
+
+libmurphy_lua_decision_la_REGULAR_SOURCES = \
+ core/lua-decision/mdb.c \
+ core/lua-decision/element.c
+
+libmurphy_lua_decision_la_SOURCES = \
+ $(libmurphy_lua_decision_la_REGULAR_SOURCES)
+
+libmurphy_lua_decision_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LUA_CFLAGS)
+
+libmurphy_lua_decision_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.lua-decision \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_lua_decision_la_LIBADD = \
+ libmurphy-common.la \
+ libmurphy-lua-utils.la \
+ murphy-db/mql/libmql.la \
+ murphy-db/mqi/libmqi.la \
+ murphy-db/mdb/libmdb.la \
+ $(LUA_LIBS)
+
+libmurphy_lua_decision_la_DEPENDENCIES = \
+ linker-script.lua-decision \
+ $(filter %.la, $(libmurphy_lua_decision_la_LIBADD))
+
+# lua-decision linker script generation
+linker-script.lua-decision: $(libmurphy_lua_decision_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_lua_decision_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.lua-decision
+
+###################################
+# murphy core library
+#
+
+lib_LTLIBRARIES += libmurphy-core.la
+EXTRA_DIST += core/murphy-core.pc
+pkgconfig_DATA += core/murphy-core.pc
+
+LUA_BINDINGS_SOURCES = \
+ core/lua-bindings/lua-murphy.c \
+ core/lua-bindings/lua-lua.c \
+ core/lua-bindings/lua-plugin.c \
+ core/lua-bindings/lua-log.c \
+ core/lua-bindings/lua-console.c \
+ core/lua-bindings/lua-bitwise.c \
+ core/lua-bindings/lua-json.c \
+ core/lua-bindings/lua-timer.c \
+ core/lua-bindings/lua-event.c \
+ core/lua-bindings/lua-deferred.c \
+ core/lua-bindings/lua-sighandler.c \
+ core/lua-bindings/lua-transport.c \
+ core/lua-bindings/lua-env.c
+
+libmurphy_core_lua_bindings_ladir = \
+ $(includedir)/murphy/core/lua-bindings
+
+libmurphy_core_lua_bindings_la_HEADERS = \
+ core/lua-bindings/murphy.h \
+ core/lua-bindings/lua-json.h
+
+libmurphy_coreh_ladir = \
+ $(includedir)/murphy
+
+libmurphy_coreh_la_HEADERS = \
+ core.h
+
+libmurphy_core_ladir = \
+ $(includedir)/murphy/core
+
+libmurphy_core_la_HEADERS = \
+ core/context.h \
+ core/plugin.h \
+ core/console-command.h \
+ core/console.h \
+ core/scripting.h \
+ core/method.h \
+ core/auth.h \
+ core/domain.h \
+ core/domain-types.h
+
+libmurphy_core_la_REGULAR_SOURCES = \
+ core/context.c \
+ core/plugin.c \
+ core/console.c \
+ core/scripting.c \
+ core/method.c \
+ core/auth.c \
+ core/auth-deny.c \
+ core/domain.c \
+ $(LUA_BINDINGS_SOURCES)
+
+if SMACK_ENABLED
+libmurphy_core_la_REGULAR_SOURCES += \
+ core/auth-smack.c
+endif
+
+libmurphy_core_la_SOURCES = \
+ $(libmurphy_core_la_REGULAR_SOURCES)
+
+libmurphy_core_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LUA_CFLAGS) \
+ $(JSON_CFLAGS) \
+ $(SMACK_CFLAGS)
+
+libmurphy_core_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.core \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_core_la_LIBADD = \
+ libmurphy-common.la \
+ libmurphy-lua-decision.la \
+ libmurphy-lua-utils.la \
+ murphy-db/mql/libmql.la \
+ murphy-db/mqi/libmqi.la \
+ murphy-db/mdb/libmdb.la \
+ $(LUA_LIBS) \
+ $(SMACK_LIBS) \
+ -ldl
+
+libmurphy_core_la_DEPENDENCIES = \
+ linker-script.core \
+ $(filter %.la, $(libmurphy_core_la_LIBADD))
+
+# core linker script generation
+linker-script.core: $(libmurphy_core_la_HEADERS) \
+ $(libmurphy_core_lua_bindings_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_core_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.core
+
+###################################
+# murphy dbus library
+#
+# Right now we have three variants of the murphy D-Bus library. The
+# original with leaky libdbus abstraction, a revised one that plugs
+# the most gaping abstraction holes, and one which uses systemd-bus,
+# the licensing-wise much easier/liberal D-Bus implementation from
+# systemd.
+#
+# All original code uses/used to use the libdbus-based one with leaky
+# abstraction. This would make it very difficult to move away from
+# the license-troubled libdbus even if/when a viable alternative (eg.
+# from the kdbus effort) became available. To get rid of this problem,
+# we need to get rid of the direct dependency on libdbus. Once that is
+# done, we can (hopefully) switch easily and transparently between
+# D-Bus libraries, if needed by touching only one component, the
+# murphy-dbus library.
+#
+# To recap these are the different library variants:
+#
+# 1) murphy-libdbus
+# This is the original murphy-dbus library. It uses libdbus as the
+# underlying D-Bus client library implementation. It does not provide
+# a proper abstraction on top of libdbus. Messages, message building,
+# and parsing used DBusMessage.
+#
+# 2) murphy-dbus-libdbus
+# This is a modified version of murphy-libdbus which provides an
+# additional abstraction for messages, message building and parsing.
+#
+# 3) murphy-dbus-systemdbus
+# This library provides an identical API to murphy-dbus-libdbus but
+# it uses the systemd dbus library implementation (systemd-bus) as
+# the underlying D-Bus client library implementation.
+#
+# So all current code still using libdbus for message building/parsing
+# needs to get rid of it and changed to use murphy-dbus-libdbus.
+#
+
+# original libdbus-based library
+murphy_libdbus_headers = \
+ common/libdbus.h
+
+if LIBDBUS_ENABLED
+lib_LTLIBRARIES += libmurphy-libdbus.la
+EXTRA_DIST += common/murphy-libdbus.pc
+pkgconfig_DATA += common/murphy-libdbus.pc
+
+libmurphy_libdbus_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_libdbus_la_HEADERS = $(murphy_libdbus_headers)
+
+libmurphy_libdbus_la_REGULAR_SOURCES = \
+ common/libdbus.c \
+ common/libdbus-glue.c
+
+libmurphy_libdbus_la_SOURCES = \
+ $(libmurphy_libdbus_la_REGULAR_SOURCES)
+
+libmurphy_libdbus_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LIBDBUS_CFLAGS)
+
+libmurphy_libdbus_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.libdbus \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_libdbus_la_LIBADD = \
+ libmurphy-common.la \
+ $(LIBDBUS_LIBS) \
+ -lrt
+
+libmurphy_libdbus_la_DEPENDENCIES = \
+ linker-script.libdbus \
+ $(filter %.la, $(libmurphy_libdbus_la_LIBADD))
+
+libmurphy_libdbusdir = $(includedir)/murphy/dbus
+libmurphy_libdbus_HEADERS = $(libmurphy_libdbus_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.libdbus: $(murphy_libdbus_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_libdbus_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.libdbus
+
+# libdbus-based library with full abstraction
+murphy_dbus_libdbus_headers = \
+ common/dbus-libdbus.h \
+ common/dbus-error.h
+
+if LIBDBUS_ENABLED
+lib_LTLIBRARIES += libmurphy-dbus-libdbus.la
+EXTRA_DIST += common/murphy-dbus-libdbus.pc
+pkgconfig_DATA += common/murphy-dbus-libdbus.pc
+
+libmurphy_dbus_libdbus_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_dbus_libdbus_la_HEADERS = $(murphy_dbus_libdbus_headers)
+
+libmurphy_dbus_libdbus_la_REGULAR_SOURCES = \
+ common/dbus-libdbus.c \
+ common/dbus-libdbus-glue.c \
+ common/dbus-libdbus-transport.c
+
+libmurphy_dbus_libdbus_la_SOURCES = \
+ $(libmurphy_dbus_libdbus_la_REGULAR_SOURCES)
+
+libmurphy_dbus_libdbus_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LIBDBUS_CFLAGS)
+
+libmurphy_dbus_libdbus_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.dbus-libdbus \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_dbus_libdbus_la_LIBADD = \
+ libmurphy-common.la \
+ -lrt $(LIBDBUS_LIBS)
+
+libmurphy_dbus_libdbus_la_DEPENDENCIES = \
+ linker-script.dbus-libdbus \
+ $(filter %.la, $(libmurphy_dbus_libdbus_la_LIBADD))
+
+libmurphy_dbus_libdbusdir = $(includedir)/murphy/dbus
+libmurphy_dbus_libdbus_HEADERS = $(libmurphy_dbus_libdbus_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.dbus-libdbus: $(murphy_dbus_libdbus_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_dbus_libdbus_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.dbus-libdbus
+
+# systemd-bus based library
+murphy_dbus_sdbus_headers = \
+ common/dbus-sdbus.h \
+ common/dbus-error.h
+
+if SDBUS_ENABLED
+lib_LTLIBRARIES += libmurphy-dbus-sdbus.la
+EXTRA_DIST += common/murphy-dbus-sdbus.pc
+pkgconfig_DATA += common/murphy-dbus-sdbus.pc
+
+libmurphy_dbus_sdbus_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_dbus_sdbus_la_HEADERS = $(murphy_dbus_sdbus_headers)
+
+libmurphy_dbus_sdbus_la_REGULAR_SOURCES = \
+ common/dbus-sdbus.c \
+ common/dbus-sdbus-glue.c \
+ common/dbus-sdbus-transport.c
+
+libmurphy_dbus_sdbus_la_SOURCES = \
+ $(libmurphy_dbus_sdbus_la_REGULAR_SOURCES)
+
+libmurphy_dbus_sdbus_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(SDBUS_CFLAGS)
+
+libmurphy_dbus_sdbus_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.dbus-sdbus \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_dbus_sdbus_la_LIBADD = \
+ -lrt $(SDBUS_LIBS)
+
+libmurphy_dbus_sdbus_la_DEPENDENCIES = \
+ linker-script.dbus-sdbus \
+ $(filter %.la, $(libmurphy_dbus_sdbus_la_LIBADD))
+
+libmurphy_dbus_sdbusdir = $(includedir)/murphy/dbus
+libmurphy_dbus_sdbus_HEADERS = $(libmurphy_dbus_sdbus_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.dbus-sdbus: $(murphy_dbus_sdbus_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_dbus_sdbus_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.sd-bus
+
+###################################
+# murphy pulse glue library
+#
+
+murphy_pulse_headers = \
+ common/pulse-glue.h \
+ common/pulse-subloop.h
+
+if PULSE_ENABLED
+lib_LTLIBRARIES += libmurphy-pulse.la
+EXTRA_DIST += common/murphy-pulse.pc
+pkgconfig_DATA += common/murphy-pulse.pc
+
+libmurphy_pulse_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_pulse_la_HEADERS = $(murphy_pulse_headers)
+
+libmurphy_pulse_la_SOURCES = \
+ common/pulse-glue.c \
+ common/pulse-subloop.c
+
+libmurphy_pulse_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(PULSE_CFLAGS)
+
+libmurphy_pulse_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.pulse \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_pulse_la_LIBADD = \
+ libmurphy-common.la \
+ $(PULSE_LIBS)
+
+libmurphy_pulse_la_DEPENDENCIES = \
+ linker-script.pulse \
+ $(filter %.la, $(libmurphy_pulse_la_LIBADD))
+
+libpulseincludedir = $(includedir)/murphy/pulse
+libpulseinclude_HEADERS = $(libmurphy_pulse_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.pulse: $(murphy_pulse_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_pulse_la_CFLAGS)" \
+ -p '^mrp_|^_mrp|^pa_' -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.pulse
+
+###################################
+# murphy ecore glue library
+#
+
+murphy_ecore_headers = \
+ common/ecore-glue.h
+
+if ECORE_ENABLED
+lib_LTLIBRARIES += libmurphy-ecore.la
+EXTRA_DIST += common/murphy-ecore.pc
+pkgconfig_DATA += common/murphy-ecore.pc
+
+libmurphy_ecore_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_ecore_la_HEADERS = $(murphy_ecore_headers)
+
+libmurphy_ecore_la_SOURCES = \
+ common/ecore-glue.c
+
+libmurphy_ecore_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(ECORE_CFLAGS)
+
+libmurphy_ecore_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.ecore \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_ecore_la_LIBADD = \
+ libmurphy-common.la \
+ $(ECORE_LIBS)
+
+libmurphy_ecore_la_DEPENDENCIES = \
+ linker-script.ecore \
+ $(filter %.la, $(libmurphy_ecore_la_LIBADD))
+
+libecoreincludedir = $(includedir)/murphy/ecore
+libecoreinclude_HEADERS = $(libmurphy_ecore_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.ecore: $(murphy_ecore_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_ecore_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.ecore
+
+###################################
+# murphy glib glue library
+#
+
+murphy_glib_headers = \
+ common/glib-glue.h
+
+if GLIB_ENABLED
+lib_LTLIBRARIES += libmurphy-glib.la
+EXTRA_DIST += common/murphy-glib.pc
+pkgconfig_DATA += common/murphy-glib.pc
+
+libmurphy_glib_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_glib_la_HEADERS = $(murphy_glib_headers)
+
+libmurphy_glib_la_SOURCES = \
+ common/glib-glue.c
+
+libmurphy_glib_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(GLIB_CFLAGS)
+
+libmurphy_glib_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.glib \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_glib_la_LIBADD = \
+ libmurphy-common.la \
+ $(GLIB_LIBS)
+
+libmurphy_glib_la_DEPENDENCIES = \
+ linker-script.glib \
+ $(filter %.la, $(libmurphy_glib_la_LIBADD))
+
+libglibincludedir = $(includedir)/murphy/glib
+libglibinclude_HEADERS = $(libmurphy_glib_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.glib: $(murphy_glib_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_glib_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.glib
+
+###################################
+# murphy qt glue library
+#
+
+murphy_qt_headers = \
+ common/qt-glue.h
+
+if QT_ENABLED
+lib_LTLIBRARIES += libmurphy-qt.la
+EXTRA_DIST += common/murphy-qt.pc
+pkgconfig_DATA += common/murphy-qt.pc
+
+BUILT_SOURCES += common/qt-glue-priv.moc.h
+
+libmurphy_qt_ladir = \
+ $(includedir)/murphy/common
+
+libmurphy_qt_la_HEADERS = $(murphy_qt_headers)
+
+libmurphy_qt_la_SOURCES = \
+ common/qt-glue.cpp
+
+libmurphy_qt_la_CPPFLAGS = \
+ $(AM_CFLAGS) \
+ $(QTCORE_CFLAGS)
+
+libmurphy_qt_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.qt \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_qt_la_LIBADD = $(QTCORE_LIBS)
+
+libmurphy_qt_la_DEPENDENCIES = \
+ linker-script.qt \
+ $(filter %.la, $(libmurphy_qt_la_LIBADD))
+
+libqtincludedir = $(includedir)/murphy/qt
+libqtinclude_HEADERS = $(libmurphy_qt_la_HEADERS)
+
+# run moc to generate Qt meta objects
+common/qt-glue-priv.moc.h: common/qt-glue-priv.h
+ $(QUIET_GEN)$(QT_MOC) -DQT_ENABLED=1 $< -o$@
+endif
+
+# linker script generation
+linker-script.qt: $(murphy_qt_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_qt_la_CPPFLAGS) -DQT_ENABLED=1" \
+ -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.qt
+
+###################################
+# murphy resolver library
+#
+
+lib_LTLIBRARIES += libmurphy-resolver.la
+EXTRA_DIST += resolver/murphy-resolver.pc
+pkgconfig_DATA += resolver/murphy-resolver.pc
+
+BUILT_SOURCES += resolver/scanner.c resolver/parser.c
+
+libmurphy_resolver_ladir = \
+ $(includedir)/murphy/resolver
+
+libmurphy_resolver_la_HEADERS = \
+ resolver/resolver.h
+
+SIMPLE_SCRIPT_SOURCES = \
+ resolver/scripting/simple/simple-script.c \
+ resolver/scripting/simple/simple-scanner.c \
+ resolver/scripting/simple/simple-parser.c \
+ resolver/scripting/simple/call.c \
+ resolver/scripting/simple/builtins.c
+
+libmurphy_resolver_la_REGULAR_SOURCES = \
+ resolver/resolver.c \
+ resolver/parser.c \
+ resolver/scanner.c \
+ resolver/target.c \
+ resolver/target-sorter.c \
+ resolver/fact.c \
+ resolver/events.c \
+ resolver/console.c \
+ $(SIMPLE_SCRIPT_SOURCES)
+
+libmurphy_resolver_la_SOURCES = \
+ $(libmurphy_resolver_la_REGULAR_SOURCES)
+
+libmurphy_resolver_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(JSON_CFLAGS)
+
+libmurphy_resolver_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.resolver \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_resolver_la_LIBADD = \
+ libmurphy-core.la \
+ libmurphy-common.la \
+ murphy-db/mql/libmql.la \
+ murphy-db/mqi/libmqi.la \
+ murphy-db/mdb/libmdb.la
+
+libmurphy_resolver_la_DEPENDENCIES = \
+ linker-script.resolver \
+ $(filter %.la, $(libmurphy_resolver_la_LIBADD))
+
+# resolver lexical analyser and parser generation
+RESOLVER_PARSER_PREFIX = yy_res_
+
+resolver/scanner.c: resolver/scanner.l resolver/parser.c
+ $(LEXCOMPILE) -P $(RESOLVER_PARSER_PREFIX) $<
+ mv lex.$(RESOLVER_PARSER_PREFIX).c $@
+
+resolver/parser.h resolver/parser.c: resolver/parser.y
+ $(YACCCOMPILE) -p $(RESOLVER_PARSER_PREFIX) $< -o resolver/parser.c
+
+# resolver linker script generation
+linker-script.resolver: $(libmurphy_resolver_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_resolver_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.resolver
+
+# simple interpreter lexical analyser and parser generation
+SIMPLE_PARSER_PREFIX = yy_smpl_
+BUILT_SOURCES += resolver/scripting/simple/simple-scanner.c \
+ resolver/scripting/simple/simple-parser.c
+
+resolver/scripting/simple/simple-scanner.c: \
+ resolver/scripting/simple/simple-scanner.l \
+ resolver/scripting/simple/simple-parser.c
+ $(LEXCOMPILE) -P $(SIMPLE_PARSER_PREFIX) $<
+ mv lex.$(SIMPLE_PARSER_PREFIX).c $@
+
+# simple interpreter parser generation
+resolver/scripting/simple/simple-parser.h \
+resolver/scripting/simple/simple-parser.c: \
+ resolver/scripting/simple/simple-parser.y
+ $(YACCCOMPILE) -p $(SIMPLE_PARSER_PREFIX) $< -o \
+ resolver/scripting/simple/simple-parser.c
+
+###################################
+# murphy backend resource library
+#
+
+if BUILD_RESOURCES
+RESOURCE_LIBRARY = libmurphy-resource-backend.la
+else
+RESOURCE_LIBRARY =
+endif
+
+if BUILD_RESOURCES
+lib_LTLIBRARIES += $(RESOURCE_LIBRARY)
+
+libmurphy_resource_backend_ladir = \
+ $(includedir)/murphy/resource
+
+libmurphy_resource_backend_la_HEADERS = \
+ resource/data-types.h \
+ resource/common-api.h \
+ resource/client-api.h \
+ resource/manager-api.h \
+ resource/config-api.h \
+ resource/protocol.h
+
+libmurphy_resource_backend_la_REGULAR_SOURCES = \
+ resource/attribute.c \
+ resource/resource.c \
+ resource/resource-set.c \
+ resource/application-class.c \
+ resource/resource-owner.c \
+ resource/resource-client.c \
+ resource/zone.c \
+ resource/config-lua.c \
+ resource/resource-lua.c \
+ resource/lua-resource.c
+
+libmurphy_resource_backend_la_SOURCES = \
+ $(libmurphy_resource_backend_la_REGULAR_SOURCES)
+
+libmurphy_resource_backend_la_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(LUA_CFLAGS)
+
+libmurphy_resource_backend_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.resource_backend \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_resource_backend_la_LIBADD = \
+ libmurphy-core.la \
+ libmurphy-common.la \
+ libmurphy-lua-utils.la \
+ murphy-db/mql/libmql.la \
+ murphy-db/mqi/libmqi.la \
+ murphy-db/mdb/libmdb.la \
+ $(LUA_LIBS)
+
+libmurphy_resource_backend_la_DEPENDENCIES = \
+ linker-script.resource_backend \
+ $(filter %.la, $(libmurphy_resource_backend_la_LIBADD))
+endif
+
+# resource linker script generation
+linker-script.resource_backend: $(libmurphy_resource_backend_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_resource_backend_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.resource_backend
+
+##########################
+# readline replacement
+#
+lib_LTLIBRARIES += libbreedline.la
+EXTRA_DIST += breedline/breedline.pc
+pkgconfig_DATA += breedline/breedline.pc
+
+libbreedline_ladir = \
+ $(includedir)/breedline
+
+libbreedline_la_HEADERS = \
+ breedline/breedline.h \
+ breedline/macros.h
+
+libbreedline_la_SOURCES = \
+ breedline/breedline.c
+
+libbreedline_la_CFLAGS = \
+ $(AM_CFLAGS)
+
+libbreedline_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.breedline \
+ -version-info @MURPHY_VERSION_INFO@
+
+libbreedline_la_LIBADD =
+
+libbreedline_la_DEPENDENCIES = \
+ linker-script.breedline \
+ $(filter %.la, $(libbreedline_la_LIBADD))
+
+libbreedlineincludedir = $(includedir)/breedline
+libbreedlineinclude_HEADERS = $(libbreedline_la_HEADERS)
+
+# linker script generation
+linker-script.breedline: $(libbreedline_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libbreedline_la_CFLAGS)" -p '^brl_' -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.breedline
+
+##########################
+# breedline murphy glue library
+#
+lib_LTLIBRARIES += libbreedline-murphy.la
+EXTRA_DIST += breedline/breedline-murphy.pc
+pkgconfig_DATA += breedline/breedline-murphy.pc
+
+libbreedline_murphy_ladir = $(includedir)/breedline
+
+libbreedline_murphy_la_HEADERS = breedline/breedline-murphy.h
+libbreedline_murphy_la_SOURCES = breedline/breedline-murphy.c
+libbreedline_murphy_la_CFLAGS = $(AM_CFLAGS)
+
+libbreedline_murphy_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.breedline-murphy \
+ -version-info @MURPHY_VERSION_INFO@
+
+libbreedline_murphy_la_LIBADD = \
+ libmurphy-common.la \
+ libbreedline.la
+
+libbreedline_murphy_la_DEPENDENCIES = \
+ linker-script.breedline-murphy \
+ $(filter %.la,$(libbreedline_murphy_la_LIBADD))
+
+libbreedline_murphyincludedir = $(includedir)/breedline
+libbreedline_murphyinclude_HEADERS = $(libbreedline_murphy_la_HEADERS)
+
+# linker script generation
+linker-script.breedline-murphy: $(libbreedline_murphy_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libbreedline_murphy_la_CFLAGS)" -p '^brl_' -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.breedline-murphy
+
+##########################
+# breedline glib glue library
+#
+
+breedline_glib_headers = \
+ breedline/breedline-glib.h
+
+if GLIB_ENABLED
+lib_LTLIBRARIES += libbreedline-glib.la
+EXTRA_DIST += breedline/breedline-glib.pc
+pkgconfig_DATA += breedline/breedline-glib.pc
+
+libbreedline_glib_ladir = $(includedir)/breedline
+
+libbreedline_glib_la_HEADERS = $(breedline_glib_headers)
+libbreedline_glib_la_SOURCES = breedline/breedline-glib.c
+libbreedline_glib_la_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS)
+
+libbreedline_glib_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.breedline-glib \
+ -version-info @MURPHY_VERSION_INFO@
+
+libbreedline_glib_la_LIBADD = \
+ libbreedline.la \
+ $(GLIB_LIBS)
+
+libbreedline_glib_la_DEPENDENCIES = \
+ linker-script.breedline-glib \
+ $(filter %.la, $(libbreedline_glib_la_LIBADD))
+
+libbreedline_glibincludedir = $(includedir)/breedline
+libbreedline_glibinclude_HEADERS = $(libbreedline_glib_la_HEADERS)
+endif
+
+# linker script generation
+linker-script.breedline-glib: $(breedline_glib_headers)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libbreedline_glib_la_CFLAGS)" -p '^brl_' \
+ -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.breedline-glib
+
+###################################
+# murphy resource external library
+
+# TODO: put this under conditional compilation
+
+lib_LTLIBRARIES += libmurphy-resource.la
+EXTRA_DIST += plugins/resource-native/libmurphy-resource/murphy-resource.pc
+pkgconfig_DATA += plugins/resource-native/libmurphy-resource/murphy-resource.pc
+
+libmurphy_resource_ladir = \
+ $(includedir)/murphy/plugins/resource-native/libmurphy-resource
+
+libmurphy_resource_la_HEADERS = \
+ plugins/resource-native/libmurphy-resource/resource-api.h \
+ plugins/resource-native/libmurphy-resource/resource-private.h
+
+libmurphy_resource_la_SOURCES = \
+ plugins/resource-native/libmurphy-resource/resource.c \
+ plugins/resource-native/libmurphy-resource/attribute.c \
+ plugins/resource-native/libmurphy-resource/rset.c \
+ plugins/resource-native/libmurphy-resource/string_array.c \
+ plugins/resource-native/libmurphy-resource/message.c \
+ plugins/resource-native/libmurphy-resource/resource-log.c
+
+libmurphy_resource_la_CFLAGS = \
+ $(AM_CFLAGS)
+
+libmurphy_resource_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.resource \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_resource_la_LIBADD = \
+ libmurphy-common.la
+
+libmurphy_resource_la_DEPENDENCIES = \
+ libmurphy-common.la \
+ linker-script.resource \
+ $(filter %.la, $(libmurphy_resource_la_LIBADD))
+
+# linker script generation
+linker-script.resource: $(libmurphy_resource_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_resource_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.resource
+
+# resource-api-test
+bin_PROGRAMS += resource-api-test
+
+resource_api_test_SOURCES = plugins/resource-native/libmurphy-resource/api_test.c
+resource_api_test_CFLAGS = $(AM_CFLAGS)
+resource_api_test_LDADD = libmurphy-common.la libmurphy-resource.la
+
+# resource-api-fuzz
+bin_PROGRAMS += resource-api-fuzz
+
+resource_api_fuzz_SOURCES = plugins/resource-native/libmurphy-resource/resource-fuzz.c
+resource_api_fuzz_CFLAGS = $(AM_CFLAGS)
+resource_api_fuzz_LDADD = libmurphy-common.la libmurphy-resource.la
+
+# context-create
+bin_PROGRAMS += resource-context-create
+
+resource_context_create_SOURCES = plugins/resource-native/libmurphy-resource/context-create.c
+resource_context_create_CFLAGS = $(AM_CFLAGS)
+resource_context_create_LDADD = libmurphy-common.la libmurphy-resource.la
+
+###################################
+# murphy plugins
+#
+
+BUILTIN_PLUGINS =
+BUILTIN_CFLAGS = -D__MURPHY_BUILTIN_PLUGIN__ $(AM_CFLAGS)
+BUILTIN_LIBS =
+
+LINKEDIN_PLUGINS =
+
+plugin_LTLIBRARIES =
+plugindir = $(libdir)/murphy/plugins
+
+
+# test plugin
+TEST_PLUGIN_SOURCES = plugins/plugin-test.c
+TEST_PLUGIN_CFLAGS =
+TEST_PLUGIN_LIBS =
+
+if !DISABLED_PLUGIN_TEST
+if BUILTIN_PLUGIN_TEST
+BUILTIN_PLUGINS += $(TEST_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(TEST_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(TEST_PLUGIN_LIBS)
+else
+plugin_test_la_SOURCES = $(TEST_PLUGIN_SOURCES)
+plugin_test_la_CFLAGS = $(TEST_PLUGIN_CFLAGS) $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_test_la_LDFLAGS = -module -avoid-version
+plugin_test_la_LIBADD = $(TEST_PLUGIN_LIBS)
+
+plugin_LTLIBRARIES += plugin-test.la
+endif
+endif
+
+# dbus plugin
+if LIBDBUS_ENABLED
+DBUS_PLUGIN_SOURCES = plugins/plugin-dbus.c
+DBUS_PLUGIN_CFLAGS = $(LIBDBUS_CFLAGS)
+DBUS_PLUGIN_LIBS = libmurphy-dbus-libdbus.la $(LIBDBUS_LIBS)
+
+if !DISABLED_PLUGIN_DBUS
+if BUILTIN_PLUGIN_DBUS
+BUILTIN_PLUGINS += $(DBUS_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(DBUS_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(DBUS_PLUGIN_LIBS)
+else
+plugin_dbus_la_SOURCES = $(DBUS_PLUGIN_SOURCES)
+plugin_dbus_la_CFLAGS = $(DBUS_PLUGIN_CFLAGS) $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_dbus_la_LDFLAGS = -module -avoid-version
+plugin_dbus_la_LIBADD = $(DBUS_PLUGIN_LIBS)
+
+plugin_LTLIBRARIES += plugin-dbus.la
+endif
+endif
+endif
+
+if GLIB_ENABLED
+# glib plugin
+GLIB_PLUGIN_SOURCES = plugins/plugin-glib.c
+GLIB_PLUGIN_CFLAGS = $(GLIB_CFLAGS)
+GLIB_PLUGIN_LIBS = $(GLIB_LIBS)
+
+if !DISABLED_PLUGIN_GLIB
+if BUILTIN_PLUGIN_GLIB
+BUILTIN_PLUGINS += $(GLIB_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(GLIB_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(GLIB_PLUGIN_LIBS)
+else
+plugin_glib_la_SOURCES = $(GLIB_PLUGIN_SOURCES)
+plugin_glib_la_CFLAGS = $(GLIB_PLUGIN_CFLAGS) $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_glib_la_LDFLAGS = -module -avoid-version
+plugin_glib_la_LIBADD = $(GLIB_PLUGIN_LIBS)
+
+plugin_LTLIBRARIES += plugin-glib.la
+endif
+endif
+endif
+
+# resource-dbus plugin
+if BUILD_RESOURCES
+if LIBDBUS_ENABLED
+RESOURCE_DBUS_PLUGIN_SOURCES = plugins/plugin-resource-dbus.c
+RESOURCE_DBUS_PLUGIN_CFLAGS = $(LIBDBUS_CFLAGS)
+RESOURCE_DBUS_PLUGIN_LIBS = \
+ libmurphy-dbus-libdbus.la \
+ libmurphy-core.la \
+ libmurphy-common.la \
+ $(RESOURCE_LIBRARY)
+
+if !DISABLED_PLUGIN_RESOURCE_DBUS
+if BUILTIN_PLUGIN_RESOURCE_DBUS
+BUILTIN_PLUGINS += $(RESOURCE_DBUS_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(RESOURCE_DBUS_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(RESOURCE_DBUS_PLUGIN_LIBS)
+else
+plugin_resource_dbus_la_SOURCES = $(RESOURCE_DBUS_PLUGIN_SOURCES)
+plugin_resource_dbus_la_CFLAGS = $(RESOURCE_DBUS_PLUGIN_CFLAGS) $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_resource_dbus_la_LDFLAGS = -module -avoid-version
+plugin_resource_dbus_la_LIBADD = $(RESOURCE_DBUS_PLUGIN_LIBS)
+plugin_LTLIBRARIES += plugin-resource-dbus.la
+endif
+endif
+endif
+endif
+
+# resource-wrt plugin
+if BUILD_RESOURCES
+if WEBSOCKETS_ENABLED
+RESOURCE_WRT_PLUGIN_SOURCES = plugins/resource-wrt/plugin-resource-wrt.c
+RESOURCE_WRT_PLUGIN_CFLAGS = $(WEBSOCKETS_CFLAGS)
+RESOURCE_WRT_PLUGIN_LIBS =
+
+if !DISABLED_PLUGIN_RESOURCE_WRT
+if BUILTIN_PLUGIN_RESOURCE_WRT
+BUILTIN_PLUGINS += $(RESOURCE_WRT_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(RESOURCE_WRT_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(RESOURCE_WRT_PLUGIN_LIBS)
+else
+plugin_resource_wrt_la_SOURCES = $(RESOURCE_WRT_PLUGIN_SOURCES)
+plugin_resource_wrt_la_CFLAGS = $(RESOURCE_WRT_PLUGIN_CFLAGS) \
+ $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_resource_wrt_la_LDFLAGS = -module -avoid-version
+plugin_resource_wrt_la_LIBADD = $(RESOURCE_WRT_PLUGIN_LIBS)
+plugin_LTLIBRARIES += plugin-resource-wrt.la
+
+EXTRA_DIST += $(resource_wrt_DATA);
+resource_wrtdir = $(datadir)/murphy/resource-wrt
+resource_wrt_DATA = \
+ plugins/resource-wrt/resource-api.js \
+ plugins/resource-wrt/resource-test.html
+
+endif
+endif
+endif
+endif
+
+# console plugin
+CONSOLE_PLUGIN_REGULAR_SOURCES = plugins/console/plugin-console.c
+CONSOLE_PLUGIN_SOURCES = $(CONSOLE_PLUGIN_REGULAR_SOURCES)
+CONSOLE_PLUGIN_CFLAGS =
+CONSOLE_PLUGIN_LIBS =
+
+if CONSOLE_ENABLED
+if !DISABLED_PLUGIN_CONSOLE
+if BUILTIN_PLUGIN_CONSOLE
+BUILTIN_PLUGINS += $(CONSOLE_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(CONSOLE_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(CONSOLE_PLUGIN_LIBS)
+else
+plugin_console_la_SOURCES = $(CONSOLE_PLUGIN_SOURCES)
+plugin_console_la_CFLAGS = $(CONSOLE_PLUGIN_CFLAGS) \
+ $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_console_la_LDFLAGS = -module -avoid-version
+plugin_console_la_LIBADD = $(CONSOLE_PLUGIN_LIBS)
+
+plugin_LTLIBRARIES += plugin-console.la
+endif
+endif
+endif
+
+# native resource plugin
+if BUILD_RESOURCES
+PLUGIN_RESOURCE_NATIVE_REGULAR_SOURCES = \
+ plugins/resource-native/plugin-resource-native.c
+PLUGIN_RESOURCE_NATIVE_SOURCES = \
+ $(PLUGIN_RESOURCE_NATIVE_REGULAR_SOURCES)
+PLUGIN_RESOURCE_NATIVE_CFLAGS = \
+ $(LUA_CFLAGS)
+
+PLUGIN_RESOURCE_NATIVE_LIBS = \
+ libmurphy-common.la \
+ $(RESOURCE_LIBRARY) \
+ $(LUA_LIBS)
+
+plugin_resource_native_la_SOURCES = $(PLUGIN_RESOURCE_NATIVE_SOURCES)
+plugin_resource_native_la_CFLAGS = $(PLUGIN_RESOURCE_NATIVE_CFLAGS) \
+ $(MURPHY_CFLAGS) $(AM_CFLAGS) \
+ $(JSON_CFLAGS)
+plugin_resource_native_la_LDFLAGS = -module -avoid-version
+plugin_resource_native_la_LIBADD = $(PLUGIN_RESOURCE_NATIVE_LIBS)
+
+plugin_LTLIBRARIES += plugin-resource-native.la
+
+# resource-client
+bin_PROGRAMS += resource-client
+
+resource_client_SOURCES = plugins/resource-native/resource-client.c
+resource_client_CFLAGS = $(AM_CFLAGS)
+resource_client_LDADD = libmurphy-common.la
+endif
+
+# domain control plugin
+DOMAIN_CONTROL_PLUGIN_SOURCES = plugins/domain-control/plugin-domain-control.c \
+ plugins/domain-control/domain-control.c \
+ plugins/domain-control/proxy.c \
+ plugins/domain-control/table.c \
+ plugins/domain-control/notify.c \
+ plugins/domain-control/message.c
+DOMAIN_CONTROL_PLUGIN_LOADER = linkedin-domain-control-loader.c
+
+
+DOMAIN_CONTROL_PLUGIN_CFLAGS =
+DOMAIN_CONTROL_PLUGIN_LIBS =
+
+if !DISABLED_PLUGIN_DOMAIN_CONTROL
+if BUILTIN_PLUGIN_DOMAIN_CONTROL
+LINKEDIN_PLUGINS += libmurphy-plugin-domain-control.la
+lib_LTLIBRARIES += libmurphy-plugin-domain-control.la
+DOMAIN_CONTROL_PLUGIN_CFLAGS += $(BUILTIN_CFLAGS)
+
+
+libmurphy_plugin_domain_control_ladir = \
+ $(includedir)/murphy/domain-control
+
+libmurphy_plugin_domain_control_la_SOURCES = \
+ $(DOMAIN_CONTROL_PLUGIN_SOURCES) \
+ $(DOMAIN_CONTROL_PLUGIN_LOADER)
+
+libmurphy_plugin_domain_control_la_CFLAGS = \
+ $(DOMAIN_CONTROL_PLUGIN_CFLAGS) \
+ $(AM_CFLAGS) \
+ $(JSON_CFLAGS)
+
+libmurphy_plugin_domain_control_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.domain-control \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_plugin_domain_control_la_LIBADD = \
+ libmurphy-core.la \
+ libmurphy-common.la \
+ murphy-db/mql/libmql.la \
+ murphy-db/mqi/libmqi.la \
+ murphy-db/mdb/libmdb.la
+
+libmurphy_plugin_domain_control_la_DEPENDENCIES = \
+ linker-script.domain-control \
+ $(filter %.la, $(libmurphy_plugin_domain_control_la_LIBADD))
+else
+plugin_domain_control_la_SOURCES = $(DOMAIN_CONTROL_PLUGIN_SOURCES)
+plugin_domain_control_la_CFLAGS = $(DOMAIN_CONTROL_PLUGIN_CFLAGS) \
+ $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_domain_control_la_LDFLAGS = -module -avoid-version
+plugin_domain_control_la_LIBADD = $(DOMAIN_CONTROL_PLUGIN_LIBS)
+plugin_LTLIBRARIES += plugin-domain-control.la
+endif
+
+
+# domain controller client library
+lib_LTLIBRARIES += libmurphy-domain-controller.la
+EXTRA_DIST += plugins/domain-control/murphy-domain-controller.pc
+pkgconfig_DATA += plugins/domain-control/murphy-domain-controller.pc
+
+libmurphy_domain_controller_ladir = \
+ $(includedir)/murphy/domain-control
+
+libmurphy_domain_controller_la_HEADERS = \
+ plugins/domain-control/client.h
+
+libmurphy_domain_controller_la_SOURCES = \
+ plugins/domain-control/client.c \
+ plugins/domain-control/message.c
+libmurphy_domain_controller_la_CFLAGS = \
+ $(JSON_CFLAGS)
+libmurphy_domain_controller_la_LIBADD = \
+ libmurphy-common.la \
+ murphy-db/mql/libmql.la \
+ murphy-db/mqi/libmqi.la \
+ murphy-db/mdb/libmdb.la \
+ $(JSON_LIBS)
+
+if WEBSOCKETS_ENABLED
+EXTRA_DIST += $(domain_control_DATA)
+domain_controldir = $(datadir)/murphy/domain-control
+domain_control_DATA = \
+ plugins/domain-control/domain-control-api.js \
+ plugins/domain-control/domain-control-test.html
+endif
+
+# test domain controller (disabled until we have a readline replacement)
+bin_PROGRAMS += test-domain-controller
+
+test_domain_controller_SOURCES = plugins/domain-control/test-client.c
+test_domain_controller_CFLAGS = $(AM_CFLAGS)
+test_domain_controller_LDADD = libmurphy-domain-controller.la \
+ libbreedline-murphy.la \
+ libbreedline.la \
+ libmurphy-common.la
+endif
+
+# linkedin domain control plugin linker script generation
+linker-script.domain-control: $(DOMAIN_CONTROL_PLUGIN_LOADER:%.c=%.h)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmurphy_domain_controller_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.domain-control
+
+# Lua plugin
+LUA_PLUGIN_SOURCES = plugins/plugin-lua.c
+LUA_PLUGIN_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS)
+LUA_PLUGIN_LIBS = $(LUA_LIBS)
+
+
+if BUILTIN_PLUGIN_LUA
+BUILTIN_PLUGINS += $(LUA_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(LUA_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(LUA_PLUGIN_LIBS)
+else
+plugin_lua_la_SOURCES = plugins/plugin-lua.c
+plugin_lua_la_CFLAGS = $(LUA_PLUGIN_CFLAGS) \
+ $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_lua_la_LDFLAGS = -module -avoid-version
+plugin_lua_la_LIBADD = $(LUA_PLUGIN_LIBS) $(LUA_LIBS)
+
+plugin_LTLIBRARIES += plugin-lua.la
+endif
+
+# systemd (logging) plugin
+if SYSTEMD_ENABLED
+SYSTEMD_PLUGIN_SOURCES = plugins/plugin-systemd.c
+SYSTEMD_PLUGIN_CFLAGS = $(SYSTEMD_CFLAGS)
+SYSTEMD_PLUGIN_LIBS = $(SYSTEMD_LIBS)
+
+if !DISABLED_PLUGIN_SYSTEMD
+if BUILTIN_PLUGIN_SYSTEMD
+BUILTIN_PLUGINS += $(SYSTEMD_PLUGIN_SOURCES)
+BUILTIN_CFLAGS += $(SYSTEMD_PLUGIN_CFLAGS)
+BUILTIN_LIBS += $(SYSTEMD_PLUGIN_LIBS)
+else
+plugin_systemd_la_SOURCES = $(SYSTEMD_PLUGIN_SOURCES)
+plugin_systemd_la_CFLAGS = $(SYSTEMD_PLUGIN_CFLAGS) \
+ $(MURPHY_CFLAGS) \
+ $(AM_CFLAGS)
+plugin_systemd_la_LDFLAGS = -module -avoid-version
+plugin_systemd_la_LIBADD = $(SYSTEMD_PLUGIN_LIBS)
+
+plugin_LTLIBRARIES += plugin-systemd.la
+endif
+endif
+endif
+
+
+###################################
+# murphy daemon
+#
+
+bin_PROGRAMS += murphyd
+EXTRA_DIST += $(config_DATA)
+configdir = $(sysconfdir)/murphy
+config_DATA = $(shell ls daemon/sample-config/*.{cfg,rules})
+
+murphyd_SOURCES = \
+ daemon/daemon.c \
+ daemon/config.c \
+ $(BUILTIN_PLUGINS) \
+ load-linkedin-plugins.c
+
+murphyd_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(BUILTIN_CFLAGS) \
+ $(JSON_CFLAGS)
+
+murphyd_LDADD = \
+ $(BUILTIN_LIBS) \
+ $(LINKEDIN_PLUGINS) \
+ $(RESOURCE_LIBRARY) \
+ libmurphy-resolver.la \
+ libmurphy-lua-decision.la \
+ libmurphy-core.la \
+ libmurphy-lua-utils.la \
+ libmurphy-common.la \
+ $(JSON_LIBS)
+
+if LIBDBUS_ENABLED
+murphyd_LDADD += \
+ libmurphy-dbus-libdbus.la \
+ -lrt $(LIBDBUS_LIBS)
+endif
+
+
+murphyd_LDFLAGS = -rdynamic
+
+install-data-hook::
+ rm -f $(DESTDIR)/$(sysconfdir)/murphy/murphy.cfg
+ echo "load-plugin lua config=\"$(sysconfdir)/murphy/main.cfg\"" \
+ > $(DESTDIR)/$(sysconfdir)/murphy/murphy.conf
+
+###################################
+# linkedin (DSO) loader generation
+#
+
+linkedin-%-loader.c:
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linkedin-loader \
+ -p $(shell echo $@ | \
+ sed 's/linkedin-//g;s/-loader.c//g') -o $@
+
+linkedin-%-loader.h:
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linkedin-loader \
+ -p $(shell echo $@ | \
+ sed 's/linkedin-//g;s/-loader.h//g') -o $@
+
+load-linkedin-plugins.c:
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linkedin-loader \
+ -o $@ -d $(shell echo $(LINKEDIN_PLUGINS) | \
+ sed 's/.*plugin-//g;s/\.[^\.]*$$//g')
+
+clean-local::
+ -rm -f linkedin-*-loader.[hc] load-linkedin-plugins.c
+
+###################################
+# murphy console client
+#
+
+if CONSOLE_ENABLED
+bin_PROGRAMS += murphy-console
+
+murphy_console_SOURCES = \
+ console-client/client.c
+
+murphy_console_CFLAGS = \
+ $(AM_CFLAGS)
+
+murphy_console_LDADD = \
+ libbreedline-murphy.la \
+ libbreedline.la \
+ libmurphy-common.la
+
+if LIBDBUS_ENABLED
+murphy_console_LDADD += libmurphy-dbus-libdbus.la
+endif
+
+murphy_console_LDFLAGS = -rdynamic
+endif
+
+# cleanup
+clean-local:: # clean-linker-script
+ -rm -f *~
--- /dev/null
+Copyright (c) 2012, Intel Corporation
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of Intel Corporation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+Breedline is a BSD-licensed simplistic alternative to readline.
+Please see the LICENSE-BSD file for the exact license.
+
+Breedline provides a limited alternative to readline. It has
+only support for the most basic readline-like editing interface
+as well as a callback-based interface for integrating to various
+mainloops. Glib and murphy mainloop integration convenience
+libraries are readily provided.
+
+Breedline should work with most terminal emulators that support
+just the following set of VT100 escape sequences:
+
+ - move cursor to beginning of line (ESC[0G)
+ - move cursor right by %d (ESC[%dC)
+ - erase right of cursor til the end of line (ESC[0K)
+
+I have tested it only with xterm, vt100, ansi, and linux console
+terminal settings in practice but in principle it should work with
+almost all non-dumb terminals out there.
+
+Currently breedline supports the following editing commands:
+
+- basic cursor positioning:
+ o left: left arrow/ctrl-b
+ o right: right arrow/ctrl-f
+ o beginning of line: home/ctrl-a/
+ o end of line: end/ctrl-e
+ o word left: ctrl left arrow
+ o word right: ctrl right arrow
+
+- basic editing:
+ o erase: backspace
+ o delete: ctrl-d
+ o kill line: ctrl-u (saves to yank buffer)
+ o kill rest of line: ctrl-k (saves to yank buffer)
+ o yank: ctrl-y
+
+- basic history:
+ o previous: up arrow/ctrl-p
+ o next: down arrow/ctrl-n
+
+- miscallanea:
+ o redraw line: ctrl-l
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <poll.h>
+#include <sys/poll.h>
+#include <glib.h>
+
+#include <breedline/breedline.h>
+
+typedef struct {
+ GIOChannel *ioc;
+ guint iow;
+ void (*cb)(int fd, int events, void *user_data);
+ void *user_data;
+} watch_t;
+
+
+static gboolean io_cb(GIOChannel *ioc, GIOCondition events, void *user_data)
+{
+ watch_t *w = (watch_t *)user_data;
+ int e = 0;
+ int fd = g_io_channel_unix_get_fd(ioc);
+
+ if (events & G_IO_IN)
+ e |= POLLIN;
+ if (events & G_IO_HUP)
+ e |= POLLHUP;
+
+ w->cb(fd, e, w->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_watch(void *mlp, int fd,
+ void (*cb)(int fd, int events, void *user_data),
+ void *user_data)
+{
+ GIOCondition events = G_IO_IN | G_IO_HUP;
+ watch_t *w;
+
+ (void)mlp;
+
+ w = malloc(sizeof(*w));
+
+ if (w != NULL) {
+ memset(w, 0, sizeof(*w));
+ w->cb = cb;
+ w->user_data = user_data;
+ w->ioc = g_io_channel_unix_new(fd);
+
+ if (w->ioc != NULL) {
+ w->iow = g_io_add_watch(w->ioc, events, io_cb, w);
+
+ if (w->iow != 0)
+ return w;
+
+ g_io_channel_unref(w->ioc);
+ }
+
+ free(w);
+ }
+
+ return NULL;
+}
+
+
+static void del_watch(void *wp)
+{
+ watch_t *w = (watch_t *)wp;
+
+ if (w != NULL) {
+ g_source_remove(w->iow);
+ g_io_channel_unref(w->ioc);
+
+ free(w);
+ }
+}
+
+
+static brl_mainloop_ops_t ml_ops = {
+ .add_watch = add_watch,
+ .del_watch = del_watch
+};
+
+
+brl_t *brl_create_with_glib(int fd, const char *prompt, GMainLoop *ml,
+ brl_line_cb_t cb, void *user_data)
+{
+ brl_t *brl;
+
+ brl = brl_create(fd, prompt);
+
+ if (brl != NULL) {
+ if (brl_use_mainloop(brl, ml, &ml_ops, cb, user_data) == 0)
+ return brl;
+ else
+ brl_destroy(brl);
+ }
+
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BREEDLINE_MURPHY_H__
+#define __BREEDLINE_MURPHY_H__
+
+#include <glib.h>
+#include <breedline/macros.h>
+#include <breedline/breedline.h>
+
+BRL_CDECL_BEGIN
+
+brl_t *brl_create_with_glib(int fd, const char *prompt, GMainLoop *ml,
+ brl_line_cb_t cb, void *user_data);
+
+BRL_CDECL_END
+
+
+
+#endif /* __BREEDLINE_MURPHY_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: breedline-glib
+Description: GLIB mainloop glue library for breedline.
+Version: @PACKAGE_VERSION@
+Requires: breedline glib-2.0
+Libs: -L${libdir} -lbreedline-glib
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common.h>
+#include <breedline/breedline.h>
+
+typedef struct {
+ mrp_io_watch_t *w;
+ void (*cb)(int fd, int events, void *user_data);
+ void *user_data;
+} watch_t;
+
+
+static void io_cb(mrp_io_watch_t *watch, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *w = (watch_t *)user_data;
+ int e = 0;
+
+ MRP_UNUSED(watch);
+
+ if (events & MRP_IO_EVENT_IN)
+ e |= POLLIN;
+ if (events & MRP_IO_EVENT_HUP)
+ e |= POLLHUP;
+
+ w->cb(fd, e, w->user_data);
+}
+
+
+static void *add_watch(void *mlp, int fd,
+ void (*cb)(int fd, int events, void *user_data),
+ void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)mlp;
+ mrp_io_event_t events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ watch_t *w;
+
+ w = mrp_allocz(sizeof(*w));
+
+ if (w != NULL) {
+ w->cb = cb;
+ w->user_data = user_data;
+ w->w = mrp_add_io_watch(ml, fd, events, io_cb, w);
+
+ if (w->w != NULL)
+ return w;
+ else
+ mrp_free(w);
+ }
+
+ return NULL;
+}
+
+
+static void del_watch(void *wp)
+{
+ watch_t *w = (watch_t *)wp;
+
+ if (w != NULL) {
+ mrp_del_io_watch(w->w);
+ mrp_free(w);
+ }
+}
+
+
+static brl_allocator_t allocator = {
+ .allocfn = mrp_mm_alloc,
+ .reallocfn = mrp_mm_realloc,
+ .strdupfn = mrp_mm_strdup,
+ .freefn = mrp_mm_free
+};
+
+
+static brl_mainloop_ops_t ml_ops = {
+ .add_watch = add_watch,
+ .del_watch = del_watch
+};
+
+
+brl_t *brl_create_with_murphy(int fd, const char *prompt, mrp_mainloop_t *ml,
+ brl_line_cb_t cb, void *user_data)
+{
+ brl_t *brl;
+
+ brl_set_allocator(&allocator);
+
+ brl = brl_create(fd, prompt);
+
+ if (brl != NULL) {
+ if (brl_use_mainloop(brl, ml, &ml_ops, cb, user_data) == 0)
+ return brl;
+ else
+ brl_destroy(brl);
+ }
+
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BREEDLINE_MURPHY_H__
+#define __BREEDLINE_MURPHY_H__
+
+#include <murphy/common/mainloop.h>
+#include <breedline/macros.h>
+#include <breedline/breedline.h>
+
+BRL_CDECL_BEGIN
+
+brl_t *brl_create_with_murphy(int fd, const char *prompt, mrp_mainloop_t *ml,
+ brl_line_cb_t cb, void *user_data);
+
+BRL_CDECL_END
+
+#endif /* __BREEDLINE_MURPHY_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: breedline-glib
+Description: GLIB mainloop glue library for breedline.
+Version: @PACKAGE_VERSION@
+Requires: breedline murphy-common
+Libs: -L${libdir} -lbreedline-murphy
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <ctype.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+
+#include "breedline/breedline.h"
+#include "breedline/mm.h"
+
+#ifndef TRUE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif
+
+#define BRL_UNUSED(var) (void)var
+
+#ifdef __GNUC__
+# define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
+#else
+# define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
+#endif
+
+#define BRL_CURSOR_START "\x1b[0G" /* move cursor to start of line */
+#define BRL_CURSOR_FORW "\x1b[%dC" /* move cursor forward by %d */
+#define BRL_ERASE_RIGHT "\x1b[0K" /* erase right of cursor */
+
+
+/*
+ * breedline modes
+ */
+
+typedef enum {
+ BRL_MODE_NORMAL, /* normal editing mode */
+ BRL_MODE_SEARCH_FORW, /* searching forward */
+ BRL_MODE_SEARCH_BACK, /* searching backward */
+} brl_mode_t;
+
+
+/*
+ * breedline key types
+ */
+
+typedef enum {
+ BRL_TYPE_INVALID = 0x000, /* invalid key */
+ BRL_TYPE_SELF = 0x100, /* self-inserting (normal) key */
+ BRL_TYPE_COMMAND = 0x200, /* editing command key */
+ BRL_TYPE_CSEQ = 0x400, /* control sequence indicator */
+ BRL_TYPE_MASK = 0x700, /* key type mask */
+} brl_type_t;
+
+#define BRL_INPUT_TYPE(in) ((in) & BRL_TYPE_MASK)
+#define BRL_TAG_INPUT(type, c) ((type & BRL_TYPE_MASK) | ((c) & ~BRL_TYPE_MASK))
+#define BRL_INPUT_DATA(in) ((in) & ~BRL_TYPE_MASK)
+
+/*
+ * breedline commands
+ */
+
+typedef enum {
+ BRL_CMD_INVALID = 0x00, /* invalid input */
+ BRL_CMD_SELF = 0xff, /* self-inserting input */
+ BRL_CMD_FORWARD = 0x01, /* cursor forward */
+ BRL_CMD_BACKWARD, /* cursor backward */
+ BRL_CMD_PREV_LINE, /* previous line from history */
+ BRL_CMD_NEXT_LINE, /* next line from history */
+ BRL_CMD_ERASE_BEFORE, /* erase before cursor */
+ BRL_CMD_ERASE_AT, /* erase at cursor */
+ BRL_CMD_LINE_START, /* cursor to start of line */
+ BRL_CMD_LINE_END, /* cursor to end of line */
+ BRL_CMD_ERASE_REST, /* erase till the end of line */
+ BRL_CMD_ERASE_ALL, /* erase the whole line */
+ BRL_CMD_YANK, /* yank at insertion point */
+ BRL_CMD_PREV_WORD, /* cursor to previous word boundary */
+ BRL_CMD_NEXT_WORD, /* cursor to next word boundary */
+ BRL_CMD_CANCEL, /* cancel special command or mode */
+ BRL_CMD_ENTER, /* enter input */
+ BRL_CMD_REDRAW, /* redraw input prompt */
+ BRL_CMD_SEARCH_BACK, /* search history backward */
+ BRL_CMD_SEARCH_FORW, /* search history forward */
+} brl_cmd_t;
+
+
+/*
+ * breedline extended control sequences
+ */
+
+typedef struct {
+ const char *seq; /* control sequence */
+ int len; /* sequence length */
+ int key; /* mapped to this key */
+} extmap_t;
+
+
+/*
+ * key mapping
+ */
+
+#undef CTRL
+#define CTRL(code) ((code) & 0x1f)
+#define ESC 0x1b
+#define DEL 0x7f
+#define BELL 0x7
+
+#define MAP(in, out) [(in)] = (out)
+#define MAP_RANGE(min, max, out) [(min) ... (max)] = (out)
+#define CMD(cmd) BRL_TAG_INPUT(BRL_TYPE_COMMAND, BRL_CMD_##cmd)
+
+static int key_map[256] = {
+ MAP_RANGE(' ' , '~', BRL_TYPE_SELF ),
+ MAP(CTRL('b') , CMD(BACKWARD) ),
+ MAP(CTRL('f') , CMD(FORWARD) ),
+ MAP(CTRL('p') , CMD(PREV_LINE) ),
+ MAP(CTRL('n') , CMD(NEXT_LINE) ),
+ MAP(CTRL('d') , CMD(ERASE_AT) ),
+ MAP( DEL , CMD(ERASE_BEFORE)),
+ MAP(CTRL('a') , CMD(LINE_START) ),
+ MAP(CTRL('e') , CMD(LINE_END) ),
+ MAP(CTRL('k') , CMD(ERASE_REST) ),
+ MAP(CTRL('u') , CMD(ERASE_ALL) ),
+ MAP(CTRL('y') , CMD(YANK) ),
+ MAP(CTRL('m') , CMD(ENTER) ),
+ MAP(CTRL('l') , CMD(REDRAW) ),
+ MAP(CTRL('r') , CMD(SEARCH_BACK) ),
+ MAP(CTRL('s') , CMD(SEARCH_FORW) ),
+ MAP( ESC , BRL_TYPE_CSEQ ),
+};
+
+static extmap_t ext_map[] = {
+ { "\x1b[A" , 3, CMD(PREV_LINE) },
+ { "\x1b[B" , 3, CMD(NEXT_LINE) },
+ { "\x1b[C" , 3, CMD(FORWARD) },
+ { "\x1b[D" , 3, CMD(BACKWARD) },
+ { "\x1b[F" , 3, CMD(LINE_END) },
+ { "\x1b[H" , 3, CMD(LINE_START) },
+ { "\x1b[1;5A", 6, BRL_TYPE_INVALID },
+ { "\x1b[1;5B", 6, BRL_TYPE_INVALID },
+ { "\x1b[1;5C", 6, CMD(NEXT_WORD) },
+ { "\x1b[1;5D", 6, CMD(PREV_WORD) },
+ { NULL , 0, BRL_TYPE_INVALID }
+};
+
+
+/*
+ * ring buffer for saving history
+ */
+
+typedef struct {
+ char **entries; /* ring buffer entries */
+ int size; /* buffer size */
+ int next; /* buffer insertion point */
+ int srch; /* buffer search point */
+ char *pattern; /* search pattern buffer */
+ int psize; /* pattern buffer size */
+ int plen; /* actual pattern length */
+} ringbuf_t;
+
+
+
+/*
+ * breedline context
+ */
+
+struct brl_s {
+ int fd; /* file descriptor to read */
+ struct termios term_mode; /* original terminal settings */
+ int term_flags; /* terminal descriptor flags */
+ int term_blck; /* originally in blocking mode */
+ int term_ncol; /* number of columns */
+ void *ml; /* mainloop, if any */
+ brl_mainloop_ops_t *ml_ops; /* mainloop operations, if any */
+ void *ml_w; /* I/O watch */
+ brl_line_cb_t line_cb; /* input callback */
+ void *user_data; /* opaque callback data */
+ char *prompt; /* prompt string */
+ int hidden; /* whether prompt is hidden */
+ int mode; /* current mode */
+ unsigned char *buf; /* input buffer */
+ int size; /* size of the buffer */
+ int data; /* amount of data in buffer */
+ int offs; /* buffer insertion offset */
+ unsigned char *yank; /* yank buffer */
+ int yank_size; /* yank buffer size */
+ int yank_data; /* yank buffer effective length */
+ int *map; /* key map */
+ extmap_t *ext; /* extended key map */
+ int esc; /* CS being collected */
+ unsigned char seq[8]; /* control sequence buffer */
+ int seq_len; /* sequence length */
+ ringbuf_t h; /* history ring buffer */
+ char *saved; /* input buffer saved during search */
+ int dump; /* whether to dump key input */
+ char *dbg_buf; /* debug buffer */
+ int dbg_size; /* debug buffer size */
+ int dbg_len; /* debug buffer effective length */
+};
+
+
+static int setup_terminal(brl_t *brl);
+static int cleanup_terminal(brl_t *brl);
+static int terminal_size(int fd, int *nrow, int *ncol);
+static int enable_rawmode(brl_t *brl);
+static int restore_rawmode(brl_t *brl);
+static int disable_blocking(brl_t *brl);
+static int restore_blocking(brl_t *brl);
+
+static void process_input(brl_t *brl);
+static void reset_input(brl_t *brl);
+static void dump_input(brl_t *brl);
+static void redraw_prompt(brl_t *brl);
+
+static int ringbuf_init(ringbuf_t *rb, int size);
+static void ringbuf_purge(ringbuf_t *rb);
+
+static void *_brl_default_alloc(size_t size, const char *file, int line,
+ const char *func);
+static void *_brl_default_realloc(void *ptr, size_t size,
+ const char *file, int line, const char *func);
+static char *_brl_default_strdup(const char *str, const char *file, int line,
+ const char *func);
+static void _brl_default_free(void *ptr,
+ const char *file, int line, const char *func);
+
+
+
+brl_t *brl_create(int fd, const char *prompt)
+{
+ static int dump = -1, dbg_size = -1;
+ brl_t *brl;
+
+ if (dump < 0) {
+ const char *val = getenv("__BREEDLINE_DUMP_KEYS");
+ dump = (val != NULL && (*val == 'y' || *val == 'Y'));
+ }
+
+ if (dbg_size < 0) {
+ const char *val = getenv("__BREEDLINE_DEBUG");
+ dbg_size = (val == NULL ? 0 : atoi(val));
+ }
+
+ brl = brl_allocz(sizeof(*brl));
+
+ if (brl != NULL) {
+ brl->fd = fd;
+ brl->map = key_map;
+ brl->ext = ext_map;
+ brl->prompt = brl_strdup(prompt ? prompt : "");
+
+ brl->dump = dump;
+ brl->dbg_size = dbg_size;
+ if (dbg_size > 0)
+ brl->dbg_buf = brl_allocz(dbg_size);
+
+ brl_limit_history(brl, BRL_DEFAULT_HISTORY);
+
+ if (!setup_terminal(brl) && !terminal_size(fd, NULL, &brl->term_ncol))
+ return brl;
+ else
+ brl_destroy(brl);
+ }
+
+ return NULL;
+}
+
+
+void brl_destroy(brl_t *brl)
+{
+ if (brl != NULL) {
+ brl_hide_prompt(brl);
+ brl_free(brl->prompt);
+ brl_free(brl->buf);
+ brl_free(brl->saved);
+ brl_free(brl->yank);
+ brl_free(brl->dbg_buf);
+ ringbuf_purge(&brl->h);
+ cleanup_terminal(brl);
+
+ brl_free(brl);
+ }
+}
+
+
+int brl_set_prompt(brl_t *brl, const char *prompt)
+{
+ brl_free(brl->prompt);
+ brl->prompt = brl_strdup(prompt);
+
+ return (brl->prompt != NULL || prompt == NULL);
+}
+
+
+void brl_hide_prompt(brl_t *brl)
+{
+ static int warned = 0;
+ char buf[32];
+ int n, o;
+
+ brl->hidden = TRUE;
+
+ n = snprintf(buf, sizeof(buf), "%s%s", BRL_CURSOR_START, BRL_ERASE_RIGHT);
+ o = write(brl->fd, buf, n);
+ restore_rawmode(brl);
+
+ if (BRL_UNLIKELY(o < 0 && !warned)) { /* make gcc happy */
+ fprintf(stderr, "write to fd %d failed\n", brl->fd);
+ warned = 1;
+ }
+}
+
+
+void brl_show_prompt(brl_t *brl)
+{
+ brl->hidden = FALSE;
+ enable_rawmode(brl);
+ redraw_prompt(brl);
+}
+
+
+static void debug(brl_t *brl, const char *fmt, ...)
+{
+ va_list ap;
+ int n;
+
+ va_start(ap, fmt);
+ n = vsnprintf(brl->dbg_buf, brl->dbg_size, fmt, ap);
+ va_end(ap);
+
+ if (n > 0)
+ brl->dbg_len = n < brl->dbg_size ? n : brl->dbg_size;
+ else
+ brl->dbg_len = 0;
+}
+
+
+static int ringbuf_init(ringbuf_t *rb, int size)
+{
+ int i;
+
+ for (i = 0; i < rb->size; i++)
+ brl_free(rb->entries[i]);
+
+ brl_free(rb->entries);
+ rb->entries = NULL;
+ rb->size = 0;
+ rb->next = 0;
+ rb->srch = 0;
+
+ brl_free(rb->pattern);
+ rb->pattern = NULL;
+ rb->psize = 0;
+ rb->plen = 0;
+
+ rb->entries = brl_allocz(size * sizeof(rb->entries[0]));
+
+ if (rb->entries == NULL && size != 0)
+ return -1;
+
+ rb->size = size;
+
+ return 0;
+}
+
+
+static void ringbuf_purge(ringbuf_t *rb)
+{
+ ringbuf_init(rb, 0);
+}
+
+
+static char **ringbuf_entry(ringbuf_t *rb, int idx)
+{
+ int i;
+
+ if (rb->entries == NULL) {
+ errno = ENOSPC;
+ return NULL;
+ }
+
+ if (idx >= rb->size) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ if (idx < 0 && -idx > rb->size) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ if (idx == 0)
+ return rb->entries + rb->next;
+
+ if (idx < 0) {
+ i = rb->next + idx;
+
+ if (i < 0)
+ i += rb->size;
+
+ return rb->entries + i;
+ }
+ else {
+ i = rb->next - 1 + idx;
+
+ if (i >= rb->size)
+ i -= rb->size;
+
+ return rb->entries + i;
+ }
+}
+
+
+static int ringbuf_add(ringbuf_t *rb, const char *str)
+{
+ char **entry = ringbuf_entry(rb, 0);
+
+ if (entry != NULL) {
+ brl_free(*entry);
+ *entry = brl_strdup(str);
+ rb->next = (rb->next + 1) % rb->size;
+
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+static void ringbuf_reset_search(ringbuf_t *rb)
+{
+ rb->srch = 0;
+ rb->plen = 0;
+ memset(rb->pattern, 0, rb->psize);
+}
+
+
+static char *ringbuf_search(ringbuf_t *rb, int dir, unsigned char c,
+ brl_mode_t mode, char *current)
+{
+ int i;
+ char **e;
+
+ if (!c && mode == BRL_MODE_NORMAL) {
+ i = rb->srch + (dir < 0 ? -1 : +1);
+
+ if (i > 0)
+ return NULL;
+
+ if (i == 0) {
+ rb->srch = 0;
+ return current;
+ }
+
+ e = ringbuf_entry(rb, i);
+
+ if (e != NULL && *e != NULL) {
+ rb->srch = i;
+ return *e;
+ }
+ else
+ return NULL;
+ }
+ else if (mode == BRL_MODE_SEARCH_BACK) {
+ int total = rb->plen + 1;
+ int found = 0;
+
+ if (c) {
+ if (rb->psize == 0) {
+ rb->psize = 32;
+ if (!(rb->pattern = brl_allocz(rb->psize))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+
+ if (rb->psize < total) {
+ if (rb->psize * 2 > total)
+ total = rb->psize * 2;
+ if (!brl_reallocz(rb->pattern, rb->psize, total)) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ rb->psize = total;
+ }
+
+ rb->pattern[rb->plen++] = c;
+
+ i = rb->srch;
+ }
+ else {
+ /* keep searching backwards */
+ i = rb->srch - 1;
+ }
+
+ if (!rb->pattern) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* start searching backwards from current search point */
+
+ do {
+ e = ringbuf_entry(rb, i);
+
+ if (e != NULL && *e != NULL) {
+ /* pattern matching */
+ if (strstr(*e, rb->pattern)) {
+ found = 1;
+ break;
+ }
+ }
+
+ i--;
+ } while (e != NULL && !found);
+
+ if (found) {
+ /* set the search position while we're in search mode */
+ rb->srch = i;
+ return *e;
+ }
+
+ errno = ENOENT;
+ return NULL;
+ }
+
+ errno = EOPNOTSUPP;
+ return NULL;
+}
+
+
+int brl_limit_history(brl_t *brl, size_t size)
+{
+ return ringbuf_init(&brl->h, size);
+}
+
+
+int brl_add_history(brl_t *brl, const char *str)
+{
+ return ringbuf_add(&brl->h, str);
+}
+
+
+static int _brl_process_input(brl_t *brl)
+{
+ if (brl->dump)
+ dump_input(brl);
+ else
+ process_input(brl);
+
+ return 0;
+}
+
+
+int brl_read_line(brl_t *brl, char *buf, size_t size)
+{
+ if (brl->ml == NULL && brl->ml_ops == NULL) {
+ reset_input(brl);
+ enable_rawmode(brl);
+ brl_show_prompt(brl);
+ redraw_prompt(brl);
+ _brl_process_input(brl);
+
+ if (brl->data > 0)
+ snprintf(buf, size, "%s", brl->buf);
+ else
+ buf[0] = '\0';
+
+ brl_hide_prompt(brl);
+ restore_rawmode(brl);
+
+ return brl->data;
+ }
+ else {
+ errno = EINPROGRESS;
+ return -1;
+ }
+}
+
+
+static void _brl_io_cb(int fd, int events, void *user_data)
+{
+ brl_t *brl = (brl_t *)user_data;
+
+ BRL_UNUSED(fd);
+
+ if (events & POLLIN)
+ _brl_process_input(brl);
+
+ if (events & POLLHUP) {
+ brl->ml_ops->del_watch(brl->ml_w);
+ brl->ml_w = NULL;
+ }
+}
+
+
+int brl_use_mainloop(brl_t *brl, void *ml, brl_mainloop_ops_t *ops,
+ brl_line_cb_t cb, void *user_data)
+{
+ if (brl != NULL) {
+ if (brl->ml != NULL || brl->ml_ops != NULL) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ brl->line_cb = cb;
+ brl->user_data = user_data;
+
+ brl->ml = ml;
+ brl->ml_ops = ops;
+ brl->ml_w = brl->ml_ops->add_watch(ml, brl->fd, _brl_io_cb, brl);
+
+ if (brl->ml_w != NULL) {
+ disable_blocking(brl);
+ return 0;
+ }
+ else
+ errno = EIO;
+ }
+ else
+ errno = EFAULT;
+
+ return -1;
+}
+
+
+static int enable_rawmode(brl_t *brl)
+{
+ struct termios mode;
+
+ memcpy(&mode, &brl->term_mode, sizeof(mode));
+
+ mode.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+ mode.c_oflag &= ~OPOST;
+ mode.c_cflag |= CS8;
+ mode.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+
+ mode.c_cc[VMIN] = 1;
+ mode.c_cc[VTIME] = 0;
+
+ return tcsetattr(brl->fd, TCSAFLUSH, &mode);
+}
+
+
+static int restore_rawmode(brl_t *brl)
+{
+ return tcsetattr(brl->fd, TCSAFLUSH, &brl->term_mode);
+}
+
+
+static int disable_blocking(brl_t *brl)
+{
+ int flags;
+
+ if (brl->term_blck) {
+ brl->term_flags = fcntl(brl->fd, F_GETFL);
+
+ if (brl->term_flags != -1) {
+ flags = brl->term_flags | O_NONBLOCK;
+
+ if (fcntl(brl->fd, F_SETFL, flags) == 0) {
+ brl->term_blck = FALSE;
+ return 0;
+ }
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int restore_blocking(brl_t *brl)
+{
+ if (!brl->term_blck) {
+ if (fcntl(brl->fd, F_SETFL, brl->term_flags) == 0) {
+ brl->term_blck = FALSE;
+ return 0;
+ }
+ else
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int setup_terminal(brl_t *brl)
+{
+ if (!isatty(brl->fd)) {
+ errno = ENOTTY;
+ return -1;
+ }
+
+ if (tcgetattr(brl->fd, &brl->term_mode) == -1)
+ return -1;
+
+ if (enable_rawmode(brl) != 0)
+ return -1;
+
+ brl->term_flags = fcntl(brl->fd, F_GETFL);
+ brl->term_blck = TRUE;
+
+#if 0
+ if (disable_blocking(brl) == 0)
+ return 0;
+ else
+ return -1;
+#else
+ return 0;
+#endif
+}
+
+
+static int cleanup_terminal(brl_t *brl)
+{
+ return (restore_rawmode(brl) | restore_blocking(brl));
+}
+
+
+static int terminal_size(int fd, int *nrow, int *ncol)
+{
+ struct winsize ws;
+ int col, row;
+
+ if (ioctl(fd, TIOCGWINSZ, &ws) == 0) {
+ row = (ws.ws_row > 0 ? ws.ws_row : 80);
+ col = (ws.ws_col > 0 ? ws.ws_col : 25);
+
+ if (nrow != NULL)
+ *nrow = row;
+ if (ncol != NULL)
+ *ncol = col;
+
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+static void redraw_prompt(brl_t *brl)
+{
+ static int warned = 0;
+
+ char *prompt, *buf, *p;
+ int plen, dlen, space, start, trunc;
+ int l, n, o;
+ char search_buf[256];
+
+ if (brl->mode == BRL_MODE_SEARCH_BACK) {
+ snprintf(search_buf, 256, "search backwards: '%s'",
+ brl->h.pattern ? brl->h.pattern : "");
+ prompt = search_buf;
+ }
+ else {
+ prompt = brl->prompt ? brl->prompt : "";
+ }
+
+ plen = strlen(prompt) + 2; /* '> ' or '><' */
+
+ if (brl->dbg_len > 0)
+ plen += brl->dbg_len + 2;
+
+ space = brl->term_ncol - 1 - plen - 1; /* - 1 for potential trailing > */
+
+ /* adjust start if the cursor would be past the right edge */
+ if (brl->offs >= space)
+ start = brl->offs - space;
+ else
+ start = 0;
+
+ /* truncate if there's more data than fits the screen */
+ dlen = brl->data - start;
+
+ if (dlen > space) {
+ dlen = space;
+ trunc = TRUE;
+ }
+ else
+ trunc = FALSE;
+
+ l = plen + dlen + 64;
+ buf = alloca(l);
+ p = buf;
+
+#if 0
+ printf("\n\rplen = %d, dlen = %d, start = %d, buf = '%*.*s'\n\r",
+ plen, dlen, start, brl->data, brl->data, brl->buf);
+ printf("brl->offs = %d, effective offset = %d\n\r", brl->offs,
+ plen + brl->offs - start);
+#endif
+
+ /* position cursor to beginning of line */
+ n = snprintf(p, l, "%s", BRL_CURSOR_START);
+ p += n;
+ l -= n;
+
+ /* print prompt + visible portion of buffer */
+ n = snprintf(p, l, "%s%s%s%s%s%*.*s%s", prompt,
+ brl->dbg_len ? "[" : "",
+ brl->dbg_len ? brl->dbg_buf : "",
+ brl->dbg_len ? "]" : "",
+ start > 0 ? "><" : "> ",
+ dlen, dlen, brl->buf + start, trunc ? ">" : "");
+ p += n;
+ l -= n;
+
+ /* erase the rest of the line (ie. make sure there's no trailing junk) */
+ n = snprintf(p, l, "%s", BRL_ERASE_RIGHT);
+ p += n;
+ l -= n;
+
+ /* okay, perform the actions collected so far */
+ o = write(brl->fd, buf, (p - buf));
+
+ l = plen + dlen + 64;
+ p = buf;
+
+ if (BRL_UNLIKELY(o < 0 && !warned)) { /* make gcc happy */
+ fprintf(stderr, "write to fd %d failed\n", brl->fd);
+ warned = 1;
+ }
+
+ /* re-position cursor to the current insertion offset */
+ n = snprintf(p, l, BRL_CURSOR_START""BRL_CURSOR_FORW,
+ plen + brl->offs - start);
+ p += n;
+ l -= n;
+ o = write(brl->fd, buf, (p - buf));
+
+ if (BRL_UNLIKELY(o < 0 && !warned)) { /* make gcc happy */
+ fprintf(stderr, "write to fd %d failed\n", brl->fd);
+ warned = 1;
+ }
+}
+
+
+/*
+ * input buffer handling
+ */
+
+static void reset_input(brl_t *brl)
+{
+ memset(brl->buf, 0, brl->size);
+ brl->data = 0;
+ brl->offs = 0;
+}
+
+
+static int insert_input(brl_t *brl, const char *input, int len)
+{
+ int total;
+
+ total = brl->data + len + 1;
+
+ if (brl->size < total) {
+ if (brl->size * 2 > total)
+ total = brl->size * 2;
+ if (!brl_reallocz(brl->buf, brl->size, total))
+ return -1;
+ brl->size = total;
+ }
+
+ if (brl->offs < brl->data) {
+ memmove(brl->buf + brl->offs + len, brl->buf + brl->offs,
+ brl->data - brl->offs);
+ memcpy(brl->buf + brl->offs, input, len);
+ }
+ else
+ memcpy(brl->buf + brl->offs, input, len);
+
+ brl->data += len;
+ brl->offs += len;
+ brl->buf[brl->data] = '\0';
+
+ return 0;
+}
+
+
+static int erase_input(brl_t *brl, int n)
+{
+ if (n < 0) {
+ if (-n > brl->offs)
+ n = -brl->offs;
+ if (brl->offs < brl->data)
+ memmove(brl->buf + brl->offs + n, brl->buf + brl->offs,
+ brl->data - brl->offs);
+ brl->data += n;
+ if (brl->offs > brl->data)
+ brl->offs = brl->data;
+ }
+ else {
+ if (n > brl->data - brl->offs)
+ n = brl->data - brl->offs;
+ memmove(brl->buf + brl->offs, brl->buf + brl->offs + n,
+ brl->data - (brl->offs + n));
+ brl->data -= n;
+ }
+
+ return 0;
+}
+
+
+static void save_input(brl_t *brl)
+{
+ brl_free(brl->saved);
+ brl->saved = brl_strdup((char *)brl->buf);
+}
+
+
+static void save_yank(brl_t *brl, int start, int end)
+{
+ int size, len;
+
+ if (start < 0 || start >= brl->data || end > brl->data)
+ return;
+
+ len = end - start + 1;
+ size = len + 1;
+
+ if (brl->yank_size < size) {
+ if (brl->yank_size * 2 > size)
+ size = brl->yank_size * 2;
+ if (!brl_reallocz(brl->yank, brl->yank_size, size))
+ return;
+ }
+
+ brl->yank_size = size;
+
+ memcpy(brl->yank, brl->buf + start, len);
+ brl->yank[len] = '\0';
+ brl->yank_data = len - 1;
+}
+
+
+static void restore_input(brl_t *brl)
+{
+ reset_input(brl);
+
+ if (brl->saved != NULL) {
+ insert_input(brl, brl->saved, strlen(brl->saved));
+ brl_free(brl->saved);
+ brl->saved = NULL;
+ }
+}
+
+
+static int input_delimiter(brl_t *brl, int dir)
+{
+ static const char delim[] = " ,;:.?!'\"-_/";
+ char *s, *p;
+
+ if (brl->data == 0)
+ return 0;
+
+ if ((dir < 0 && brl->offs == 0) || (dir >= 0 && brl->offs >= brl->data))
+ return 0;
+
+ s = (char *)brl->buf + brl->offs;
+
+ if (dir < 0) {
+ p = s - 1;
+ if (p > (char *)brl->buf && strchr(delim, *p) != NULL)
+ p--;
+ while (p >= (char *)brl->buf) {
+ if (strchr(delim, *p) != NULL) {
+ p += 1;
+ break;
+ }
+ else
+ p--;
+ }
+ return p - s;
+ }
+ else {
+ p = s;
+ if (strchr(delim, *p) != NULL && s < (char *)brl->buf + brl->data)
+ p++;
+ while (p < (char *)brl->buf + brl->data) {
+ if (strchr(delim, *p) != NULL)
+ break;
+ else
+ p++;
+ }
+ return p - s;
+ }
+
+ return 0;
+}
+
+
+static void move_cursor(brl_t *brl, int n)
+{
+ brl->offs += n;
+
+ if (brl->offs < 0)
+ brl->offs = 0;
+ if (brl->offs > brl->data)
+ brl->offs = brl->data;
+}
+
+
+static void bell(brl_t *brl)
+{
+ int fd;
+
+ if (brl->fd == fileno(stdin))
+ fd = fileno(stderr);
+ else
+ fd = brl->fd;
+
+ dprintf(fd, "%c", BELL);
+}
+
+
+/*
+ * input mapping
+ */
+
+static int map_input(brl_t *brl, unsigned char c)
+{
+ int mapped;
+
+ mapped = brl->map[c];
+
+ if (mapped == BRL_TYPE_SELF)
+ return BRL_TAG_INPUT(BRL_TYPE_SELF, c);
+ else
+ return mapped;
+}
+
+
+static int map_esc_sequence(brl_t *brl)
+{
+ BRL_UNUSED(brl);
+
+ return BRL_TYPE_INVALID;
+}
+
+
+static int map_ctrl_sequence(brl_t *brl)
+{
+ extmap_t *e;
+ int d;
+
+ if (brl->ext != NULL) {
+ for (e = brl->ext; e->seq != NULL; e++) {
+ if (e->len == brl->seq_len) {
+ d = strncmp((char *)brl->seq, e->seq, e->len);
+
+ if (d == 0)
+ return e->key;
+
+ if (d < 0)
+ break;
+ }
+
+ if (e->len > brl->seq_len)
+ break;
+ }
+ }
+
+ return BRL_TYPE_INVALID;
+}
+
+
+/*
+ * main input processing
+ */
+
+static void process_input(brl_t *brl)
+{
+ unsigned char c;
+ int mapped, type, in, n, diff;
+ char out, *line, *hentry;
+
+ while((n = read(brl->fd, &c, sizeof(c))) > 0) {
+ if (brl->esc) {
+ if (brl->seq_len < (int)sizeof(brl->seq))
+ brl->seq[brl->seq_len++] = c;
+
+ if (brl->seq_len == 2) {
+ if (c != '[') {
+ mapped = map_esc_sequence(brl);
+ brl->esc = FALSE;
+ }
+ else
+ continue;
+ }
+ else {
+ if (0x40 <= c && c <= 0x7e) {
+ mapped = map_ctrl_sequence(brl);
+ brl->esc = FALSE;
+ }
+ else {
+ if (brl->seq_len == (int)sizeof(brl->seq)) {
+ mapped = BRL_TYPE_INVALID;
+ brl->esc = FALSE;
+ }
+ else
+ continue;
+ }
+ }
+ }
+ else
+ mapped = map_input(brl, c);
+
+ type = BRL_INPUT_TYPE(mapped);
+ in = BRL_INPUT_DATA(mapped);
+
+ switch (type) {
+ case BRL_TYPE_SELF:
+ switch (brl->mode) {
+ case BRL_MODE_NORMAL:
+ out = (char)(in & 0xff);
+ insert_input(brl, &out, 1);
+ redraw_prompt(brl);
+ break;
+ case BRL_MODE_SEARCH_BACK:
+ out = (char)(in & 0xff);
+ hentry = ringbuf_search(&brl->h, 0, out, BRL_MODE_SEARCH_BACK, NULL);
+ if (hentry != NULL) {
+ reset_input(brl);
+ insert_input(brl, hentry, strlen(hentry));
+ }
+ else
+ bell(brl);
+ redraw_prompt(brl);
+ break;
+ case BRL_MODE_SEARCH_FORW:
+ /* TODO */
+ break;
+ }
+ break;
+
+ case BRL_TYPE_COMMAND:
+ switch (in) {
+ case BRL_CMD_PREV_LINE:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ if (brl->h.srch == 0)
+ save_input(brl);
+ hentry = ringbuf_search(&brl->h, -1, 0, BRL_MODE_NORMAL, (char *)brl->saved);
+ debug(brl, "s:%d,'%s'", brl->h.srch,
+ brl->saved ? brl->saved : "-");
+ if (hentry != NULL) {
+ reset_input(brl);
+ insert_input(brl, hentry, strlen(hentry));
+ redraw_prompt(brl);
+ }
+ else
+ bell(brl);
+ break;
+
+ case BRL_CMD_NEXT_LINE:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ hentry = ringbuf_search(&brl->h, +1, 0, BRL_MODE_NORMAL, (char *)brl->saved);
+ debug(brl, "s:%d,'%s'", brl->h.srch,
+ brl->saved ? brl->saved : "-");
+ if (hentry != NULL) {
+ if (hentry == brl->saved)
+ restore_input(brl);
+ else {
+ reset_input(brl);
+ insert_input(brl, hentry, strlen(hentry));
+ }
+ redraw_prompt(brl);
+ }
+ else
+ bell(brl);
+ break;
+
+ case BRL_CMD_SEARCH_BACK:
+ if (brl->mode == BRL_MODE_SEARCH_BACK) {
+ /* already in search mode, continue */
+ hentry = ringbuf_search(&brl->h, 0, 0, BRL_MODE_SEARCH_BACK, NULL);
+ if (hentry != NULL) {
+ reset_input(brl);
+ insert_input(brl, hentry, strlen(hentry));
+ }
+ else
+ bell(brl);
+ }
+ else {
+ if (brl->h.srch == 0)
+ save_input(brl);
+ brl->mode = BRL_MODE_SEARCH_BACK;
+ }
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_BACKWARD:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ move_cursor(brl, -1);
+ redraw_prompt(brl);
+ break;
+ case BRL_CMD_FORWARD:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ move_cursor(brl, +1);
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_LINE_START:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ move_cursor(brl, -brl->offs);
+ redraw_prompt(brl);
+ break;
+ case BRL_CMD_LINE_END:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ move_cursor(brl, brl->data - brl->offs);
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_ERASE_BEFORE:
+ switch(brl->mode) {
+ case BRL_MODE_NORMAL:
+ erase_input(brl, -1);
+ if (brl->offs < brl->data)
+ move_cursor(brl, -1);
+ redraw_prompt(brl);
+ break;
+ case BRL_MODE_SEARCH_BACK:
+ case BRL_MODE_SEARCH_FORW:
+ if (brl->h.plen > 0) {
+ brl->h.pattern[--brl->h.plen] = '\0';
+ }
+ else {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ restore_input(brl);
+ }
+ redraw_prompt(brl);
+ break;
+ }
+ break;
+ case BRL_CMD_ERASE_AT:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ erase_input(brl, 1);
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_ERASE_REST:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ save_yank(brl, brl->offs, brl->data);
+ erase_input(brl, brl->data - brl->offs);
+ redraw_prompt(brl);
+ break;
+ case BRL_CMD_ERASE_ALL:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ save_yank(brl, 0, brl->data);
+ reset_input(brl);
+ redraw_prompt(brl);
+ break;
+ case BRL_CMD_YANK:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ insert_input(brl, (char *)brl->yank, brl->yank_data);
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_PREV_WORD:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ diff = input_delimiter(brl, -1);
+ move_cursor(brl, diff);
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_NEXT_WORD:
+ if (brl->mode != BRL_MODE_NORMAL) {
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ }
+ diff = input_delimiter(brl, +1);
+ move_cursor(brl, diff);
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_REDRAW:
+ redraw_prompt(brl);
+ break;
+
+ case BRL_CMD_ENTER:
+ dprintf(brl->fd, "\n\r");
+ if (brl->line_cb != NULL) {
+ line = alloca(brl->data + 1);
+ strncpy(line, (char *)brl->buf, brl->data);
+ line[brl->data] = '\0';
+ reset_input(brl);
+ restore_rawmode(brl);
+ brl->line_cb(brl, line, brl->user_data);
+ enable_rawmode(brl);
+ ringbuf_reset_search(&brl->h);
+ brl->mode = BRL_MODE_NORMAL;
+ debug(brl, "");
+ redraw_prompt(brl);
+ }
+ else
+ return;
+ break;
+
+ default:
+#if 0
+ printf("editing command 0x%x\n\r", in);
+#endif
+ bell(brl);
+ }
+ break;
+
+ case BRL_TYPE_CSEQ:
+ brl->esc = TRUE;
+ brl->seq[0] = c;
+ brl->seq_len = 1;
+ break;
+
+ case BRL_TYPE_INVALID:
+ default:
+ bell(brl);
+ break;
+ }
+ }
+}
+
+
+static void dump_input(brl_t *brl)
+{
+ unsigned char c, seq[64], s[4] = " \0";
+ int i = 0;
+
+ printf("got input:");
+
+ while (read(brl->fd, &c, 1) == 1) {
+ printf(" 0x%2.2x", c);
+ seq[i++] = c;
+
+ if (c == 0x3)
+ exit(0);
+ }
+
+ printf("\n\r");
+ seq[i] = '\0';
+
+ printf(" ");
+ for (i = 0; seq[i] != 0; i++) {
+ printf(" %4d", seq[i]);
+ }
+ printf("\n\r");
+
+ seq[i] = '\0';
+ printf(" ");
+ for (i = 0; seq[i] != '\0'; i++) {
+ s[3] = c = seq[i];
+ printf(" %s", (isprint(c) && c != '\n' && c != '\r' && c != '\t') ?
+ (char *)s : (c == ESC ? "ESC" : "."));
+ }
+ printf("\n\r");
+}
+
+
+/*
+ * default passthru allocator
+ */
+
+static void *_brl_default_alloc(size_t size, const char *file, int line,
+ const char *func)
+{
+ BRL_UNUSED(file);
+ BRL_UNUSED(line);
+ BRL_UNUSED(func);
+
+ return malloc(size);
+}
+
+static void *_brl_default_realloc(void *ptr, size_t size,
+ const char *file, int line, const char *func)
+{
+ BRL_UNUSED(file);
+ BRL_UNUSED(line);
+ BRL_UNUSED(func);
+
+ return realloc(ptr, size);
+}
+
+static char *_brl_default_strdup(const char *str, const char *file, int line,
+ const char *func)
+{
+ BRL_UNUSED(file);
+ BRL_UNUSED(line);
+ BRL_UNUSED(func);
+
+ return strdup(str);
+}
+
+static void _brl_default_free(void *ptr,
+ const char *file, int line, const char *func)
+{
+ BRL_UNUSED(file);
+ BRL_UNUSED(line);
+ BRL_UNUSED(func);
+
+ free(ptr);
+}
+
+
+/* By default we use the libc memory allocator. */
+brl_allocator_t __brl_mm = {
+ .allocfn = _brl_default_alloc,
+ .reallocfn = _brl_default_realloc,
+ .strdupfn = _brl_default_strdup,
+ .freefn = _brl_default_free
+};
+
+/* Once an allocation is done, this will block changing the allocator. */
+int __brl_mm_busy = FALSE;
+
+
+int brl_set_allocator(brl_allocator_t *allocator)
+{
+ if (!__brl_mm_busy) {
+ __brl_mm = *allocator;
+
+ return 0;
+ }
+ else {
+ errno = EBUSY;
+
+ return -1;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BREEDLINE_H__
+#define __BREEDLINE_H__
+
+#include <breedline/macros.h>
+
+BRL_CDECL_BEGIN
+
+/** Default history buffer size (in number of items). */
+#define BRL_DEFAULT_HISTORY 64
+
+/** Type for opaque breedline context. */
+struct brl_s;
+typedef struct brl_s brl_t;
+
+/** Create a new breedline context for the given file descriptor. */
+brl_t *brl_create(int fd, const char *prompt);
+
+/** Destroy the given context. */
+void brl_destroy(brl_t *brl);
+
+/** Set breedline prompt. */
+int brl_set_prompt(brl_t *brl, const char *prompt);
+
+/** Hide breedline prompt. */
+void brl_hide_prompt(brl_t *brl);
+
+/** Show breedline prompt. */
+void brl_show_prompt(brl_t *brl);
+
+/** Limit the size of history to the given number of entries. */
+int brl_limit_history(brl_t *brl, size_t size);
+
+/** Read a single line of input and put it to the given buffer. */
+int brl_read_line(brl_t *brl, char *buf, size_t size);
+
+/** Add an entry to history. Replaces oldest entry if history buffer is full. */
+int brl_add_history(brl_t *brl, const char *entry);
+
+/** In put delivery callback type, used when running in mainloop mode. */
+typedef void (*brl_line_cb_t)(brl_t *brl, const char *line, void *user_data);
+
+/** Breedline mainloop subset abstraction. */
+typedef struct {
+ void *(*add_watch)(void *ml, int fd,
+ void (*cb)(int fd, int events, void *user_data),
+ void *user_data);
+ void (*del_watch)(void *w);
+} brl_mainloop_ops_t;
+
+/** Set up the given context to be pumped by the given mainloop. */
+int brl_use_mainloop(brl_t *brl, void *ml, brl_mainloop_ops_t *ops,
+ brl_line_cb_t cb, void *user_data);
+
+/** Memory allocation operations. */
+typedef struct {
+ void *(*allocfn)(size_t size, const char *file, int line, const char *func);
+ void *(*reallocfn)(void *ptr, size_t size, const char *file, int line,
+ const char *func);
+ char *(*strdupfn)(const char *str, const char *file, int line,
+ const char *func);
+ void (*freefn)(void *ptr, const char *file, int line, const char *func);
+} brl_allocator_t;
+
+/** Override the default memory allocator. */
+int brl_set_allocator(brl_allocator_t *allocator);
+
+BRL_CDECL_END
+
+#endif /* __BREEDLINE_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: breedline
+Description: A BSD-licensed simplistic alternative to readline.
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lbreedline
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BREEDLINE_MACROS_H__
+#define __BREEDLINE_MACROS_H__
+
+#define BRL_UNUSED(var) (void)var
+
+#ifdef __GNUC__
+# define BRL_UNLIKELY(cond) __builtin_expect((cond), 0)
+# define BRL_LIKELY(cond) __builtin_expect((cond), 1)
+#else
+# define BRL_UNLIKELY(cond) (cond)
+# define BRL_LIKELY(cond) (cond)
+#endif
+
+#ifdef __cplusplus
+# define BRL_CDECL_BEGIN extern "C" {
+# define BRL_CDECL_END }
+#else
+# define BRL_CDECL_BEGIN
+# define BRL_CDECL_END
+#endif
+
+#endif /* __BREEDLINE_MACROS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __BREEDLINE_MM_H__
+#define __BREEDLINE_MM_H__
+
+#include <stdlib.h>
+#include <string.h>
+
+/* Macro that can be used to pass the location of its usage further. */
+#define BRL_LOC __FILE__, __LINE__, __func__
+
+/* Memory allocation macros used by breedline. */
+#define brl_allocz(size) ({ \
+ void *_ptr; \
+ size_t _size = size; \
+ \
+ if ((_ptr = __brl_mm.allocfn(_size, BRL_LOC)) != NULL) \
+ memset(_ptr, 0, _size); \
+ \
+ __brl_mm_busy = TRUE; \
+ \
+ _ptr; })
+
+#define brl_reallocz(ptr, o, n) ({ \
+ typeof(ptr) _ptr; \
+ size_t _size = sizeof(*_ptr) * (n); \
+ typeof(n) _n = (n); \
+ typeof(o) _o; \
+ \
+ if ((ptr) != NULL) \
+ _o = o; \
+ else \
+ _o = 0; \
+ \
+ _ptr = __brl_mm.reallocfn(ptr, _size, BRL_LOC); \
+ if (_ptr != NULL || _n == 0) { \
+ if ((unsigned)(_n) > (unsigned)(_o)) \
+ memset(_ptr + (_o), 0, \
+ ((_n) - (_o)) * sizeof(*_ptr)); \
+ ptr = _ptr; \
+ } \
+ \
+ __brl_mm_busy = TRUE; \
+ \
+ _ptr; })
+
+#define brl_strdup(s) ({ \
+ __brl_mm_busy = TRUE; \
+ \
+ __brl_mm.strdupfn((s), BRL_LOC); \
+ })
+
+#define brl_free(ptr) __brl_mm.freefn((ptr), BRL_LOC)
+
+extern brl_allocator_t __brl_mm;
+extern int __brl_mm_busy;
+
+#endif /* __BREEDLINE_MM_H__ */
--- /dev/null
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
+
+# murphy breedline test
+noinst_PROGRAMS = breedline-murphy-test
+
+breedline_murphy_test_SOURCES = breedline-murphy-test.c
+breedline_murphy_test_CFLAGS = $(AM_CFLAGS)
+breedline_murphy_test_LDADD = ../../libbreedline-murphy.la \
+ ../../libbreedline.la \
+ ../../libmurphy-common.la
+
+# basic (blocking) breedline test
+noinst_PROGRAMS += breedline-test
+
+breedline_test_SOURCES = breedline-test.c
+breedline_test_CFLAGS = $(AM_CFLAGS)
+breedline_test_LDADD = ../../libbreedline.la
+
+if GLIB_ENABLED
+# breedline glib test
+noinst_PROGRAMS += breedline-glib-test
+
+breedline_glib_test_SOURCES = breedline-glib-test.c
+breedline_glib_test_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS)
+breedline_glib_test_LDADD = ../../libbreedline-glib.la \
+ ../../libbreedline.la \
+ $(GLIB_LIBS)
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/poll.h>
+
+#include <glib.h>
+
+#include "breedline/breedline-glib.h"
+
+static void line_cb(brl_t *brl, const char *line, void *user_data)
+{
+ GMainLoop *ml = (GMainLoop *)user_data;
+
+ (void)brl;
+
+ printf("got line: '%s'\n", line);
+
+ if (!strcmp(line, "exit") || !strcmp(line, "quit"))
+ g_main_loop_quit(ml);
+ else {
+ if (brl_add_history(brl, line) != 0)
+ fprintf(stderr, "Failed to save history entry.\n");
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ GMainLoop *ml;
+ brl_t *brl;
+ int fd;
+ const char *prompt;
+
+ ml = g_main_loop_new(NULL, FALSE);
+
+ if (ml == NULL) {
+ fprintf(stderr, "Failed to create mainloop.\n");
+ exit(1);
+ }
+
+ fd = fileno(stdin);
+ prompt = argc > 1 ? argv[1] : "breedline-glib";
+
+ brl = brl_create_with_glib(fd, prompt, ml, line_cb, ml);
+
+ if (brl == NULL) {
+ fprintf(stderr, "Failed to create breedline context (%d: %s).\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ brl_show_prompt(brl);
+ g_main_loop_run(ml);
+ brl_destroy(brl);
+ g_main_loop_unref(ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+
+#include "breedline/breedline-murphy.h"
+
+static void line_cb(brl_t *brl, const char *line, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+
+ MRP_UNUSED(brl);
+
+ printf("got line: '%s'\n", line);
+
+ if (!strcmp(line, "exit") || !strcmp(line, "quit"))
+ mrp_mainloop_quit(ml, 0);
+ else {
+ if (brl_add_history(brl, line) != 0)
+ fprintf(stderr, "Failed to save history entry.\n");
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ mrp_mainloop_t *ml;
+ brl_t *brl;
+ int fd;
+ const char *prompt;
+
+ ml = mrp_mainloop_create();
+
+ if (ml == NULL) {
+ fprintf(stderr, "Failed to create mainloop.\n");
+ exit(1);
+ }
+
+ fd = fileno(stdin);
+ prompt = argc > 1 ? argv[1] : "breedline-murphy";
+
+ brl = brl_create_with_murphy(fd, prompt, ml, line_cb, ml);
+
+ if (brl == NULL) {
+ fprintf(stderr, "Failed to create breedline context (%d: %s).\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ brl_show_prompt(brl);
+ mrp_mainloop_run(ml);
+ brl_destroy(brl);
+ mrp_mainloop_destroy(ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/poll.h>
+
+#include <breedline/breedline.h>
+
+
+int main(int argc, char *argv[])
+{
+ brl_t *brl;
+ char line[1024];
+ int n;
+
+ brl = brl_create(fileno(stdin), argc > 1 ? argv[1] : "breedline");
+
+ if (brl == NULL) {
+ fprintf(stderr, "Failed to create breedline context (%d: %s).\n",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+ while ((n = brl_read_line(brl, line, sizeof(line))) >= 0) {
+ printf("got line: '%s'\n", line);
+
+ if (!strcmp(line, "exit") || !strcmp(line, "quit"))
+ break;
+ else {
+ if (brl_add_history(brl, line) != 0)
+ fprintf(stderr, "Failed to save history entry.\n");
+ }
+ }
+
+ brl_destroy(brl);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_COMMON_H__
+#define __MURPHY_COMMON_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/file-utils.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+
+#endif
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_ERROR_H__
+#define __MURPHY_DBUS_ERROR_H__
+
+#define __ERR(error) "org.freedesktop.DBus.Error."#error
+
+#define MRP_DBUS_ERROR_FAILED __ERR(Failed)
+#define MRP_DBUS_ERROR_NO_MEMORY __ERR(NoMemory)
+#define MRP_DBUS_ERROR_SERVICE_UNKNOWN __ERR(ServiceUnknown)
+#define MRP_DBUS_ERROR_NAME_HAS_NO_OWNER __ERR(NameHasNoOwner)
+#define MRP_DBUS_ERROR_NO_REPLY __ERR(NoReply)
+#define MRP_DBUS_ERROR_IO_ERROR __ERR(IOError)
+#define MRP_DBUS_ERROR_BAD_ADDRESS __ERR(BadAddress)
+#define MRP_DBUS_ERROR_NOT_SUPPORTED __ERR(NotSupported)
+#define MRP_DBUS_ERROR_LIMITS_EXCEEDED __ERR(LimitsExceeded)
+#define MRP_DBUS_ERROR_ACCESS_DENIED __ERR(AccessDenied)
+#define MRP_DBUS_ERROR_AUTH_FAILED __ERR(AuthFailed)
+#define MRP_DBUS_ERROR_NO_SERVER __ERR(NoServer)
+#define MRP_DBUS_ERROR_TIMEOUT __ERR(Timeout)
+#define MRP_DBUS_ERROR_NO_NETWORK __ERR(NoNetwork)
+#define MRP_DBUS_ERROR_ADDRESS_IN_USE __ERR(AddressInUse)
+#define MRP_DBUS_ERROR_DISCONNECTED __ERR(Disconnected)
+#define MRP_DBUS_ERROR_INVALID_ARGS __ERR(InvalidArgs)
+#define MRP_DBUS_ERROR_FILE_NOT_FOUND __ERR(FileNotFound)
+#define MRP_DBUS_ERROR_FILE_EXISTS __ERR(FileExists)
+#define MRP_DBUS_ERROR_UNKNOWN_METHOD __ERR(UnknownMethod)
+#define MRP_DBUS_ERROR_UNKNOWN_OBJECT __ERR(UnknownObject)
+#define MRP_DBUS_ERROR_UNKNOWN_INTERFACE __ERR(UnknownInterface)
+#define MRP_DBUS_ERROR_UNKNOWN_PROPERTY __ERR(UnknownProperty)
+#define MRP_DBUS_ERROR_PROPERTY_READ_ONLY __ERR(PropertyReadOnly)
+#define MRP_DBUS_ERROR_TIMED_OUT __ERR(TimedOut)
+#define MRP_DBUS_ERROR_MATCH_RULE_NOT_FOUND __ERR(MatchRuleNotFound)
+#define MRP_DBUS_ERROR_MATCH_RULE_INVALID __ERR(MatchRuleInvalid)
+#define MRP_DBUS_ERROR_SPAWN_EXEC_FAILED __ERR(Spawn.ExecFailed)
+#define MRP_DBUS_ERROR_SPAWN_FORK_FAILED __ERR(Spawn.ForkFailed)
+#define MRP_DBUS_ERROR_SPAWN_CHILD_EXITED __ERR(Spawn.ChildExited)
+#define MRP_DBUS_ERROR_SPAWN_CHILD_SIGNALED __ERR(Spawn.ChildSignaled)
+#define MRP_DBUS_ERROR_SPAWN_FAILED __ERR(Spawn.Failed)
+#define MRP_DBUS_ERROR_SPAWN_SETUP_FAILED __ERR(Spawn.FailedToSetup)
+#define MRP_DBUS_ERROR_SPAWN_CONFIG_INVALID __ERR(Spawn.ConfigInvalid)
+#define MRP_DBUS_ERROR_SPAWN_SERVICE_INVALID __ERR(Spawn.ServiceNotValid)
+#define MRP_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND __ERR(Spawn.ServiceNotFound)
+#define MRP_DBUS_ERROR_SPAWN_PERMISSIONS_INVALID __ERR(Spawn.PermissionsInvalid)
+#define MRP_DBUS_ERROR_SPAWN_FILE_INVALID __ERR(Spawn.FileInvalid)
+#define MRP_DBUS_ERROR_SPAWN_NO_MEMORY __ERR(Spawn.NoMemory)
+#define MRP_DBUS_ERROR_UNIX_PROCESS_ID_UNKNOWN __ERR(UnixProcessIdUnknown)
+#define MRP_DBUS_ERROR_INVALID_SIGNATURE __ERR(InvalidSignature)
+#define MRP_DBUS_ERROR_INVALID_FILE_CONTENT __ERR(InvalidFileContent)
+#define MRP_DBUS_ERROR_ADT_AUDIT_DATA_UNKNOWN __ERR(AdtAuditDataUnknown)
+#define MRP_DBUS_ERROR_OBJECT_PATH_IN_USE __ERR(ObjectPathInUse)
+#define MRP_DBUS_ERROR_INCONSISTENT_MESSAGE __ERR(InconsistentMessage)
+#define MRP_DBUS_ERROR_SELINUX_SECURITY_CONTEXT_UNKNOWN __ERR(SELinuxSecurityContextUnknown)
+
+#endif /* __MURPHY_MRP_DBUS_ERROR_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ mrp_debug("adding D-BUS watch %p (%s)", dw,
+ dbus_watch_get_enabled(dw) ? "enabled" : "disabled");
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ mrp_debug("event mask for fd %d: %s%s", fd,
+ mask & MRP_IO_EVENT_IN ? "read" : "",
+ mask & MRP_IO_EVENT_OUT ? "write" : "");
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS watch %p...", dw);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ mrp_debug("Toggling D-BUS watch %p...", dw);
+
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("dispatching D-BUS timeout %p...", timer->dt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ mrp_debug("adding D-BUS timeout %p...", dt);
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS timeout %p...", dt);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ mrp_debug("toggling D-BUS timeout %p...", dt);
+
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_debug("waking up mainloop...");
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ mrp_debug("dispatching dbus connection %p...", glue->conn);
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_debug("dispatching status for %p: complete", conn);
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_debug("dispatching status for %p: not complete yet", conn);
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/dbus-libdbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH "/murphy/transport"
+#define TRANSPORT_INTERFACE "Murphy.Transport"
+#define TRANSPORT_MESSAGE "DeliverMessage"
+#define TRANSPORT_DATA "DeliverData"
+#define TRANSPORT_RAW "DeliverRaw"
+#define TRANSPORT_METHOD "DeliverMessage"
+
+#define ANY_ADDRESS "any"
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ mrp_dbus_t *dbus; /* D-BUS connection */
+ int bound : 1; /* whether bound to an address */
+ int peer_resolved : 1; /* connected and peer name known */
+ mrp_dbusaddr_t local; /* address we're bound to */
+ mrp_dbusaddr_t remote; /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto; /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *msg, const char **sender_id);
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag);
+static void *data_decode(mrp_dbus_msg_t *msg, uint16_t *tag,
+ const char **sender_id);
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size);
+static void *raw_decode(mrp_dbus_msg_t *msg, size_t *sizep,
+ const char **sender_id);
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+ socklen_t size)
+{
+ const char *p, *e;
+ char *q;
+ size_t l, n;
+
+ if (size < sizeof(*addr)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (strncmp(str, DBUS":", DBUSL + 1))
+ return 0;
+ else
+ str += DBUSL + 1;
+
+ /*
+ * The format of the address is
+ * dbus:[bus-address]@address/path
+ * eg.
+ * dbus:[session]@:1.33/client1, or
+ * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+ */
+
+ p = str;
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa) - 1;
+
+ /* get bus address */
+ if (*p != '[') {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ p++;
+
+ e = strchr(p, ']');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save bus address */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_bus = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e + 1;
+
+ /* get (local or remote) address on bus */
+ if (*p != '@')
+ addr->db_addr = ANY_ADDRESS;
+ else {
+ p++;
+ e = strchr(p, '/');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save address on bus */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_addr = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e;
+ }
+
+ /* get object path */
+ if (*p != '/') {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ addr->db_path = q;
+ addr->db_family = MRP_AF_DBUS;
+
+ return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+ char *p, *q;
+ size_t l, n;
+
+ dst->db_family = src->db_family;
+
+ /* copy bus address */
+ p = src->db_bus;
+ q = dst->db_fqa;
+ l = sizeof(dst->db_fqa) - 1;
+
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy address */
+ p = src->db_addr;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_addr = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy path */
+ p = src->db_path;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_path = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+
+ return (a && a->db_family == MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+ const char *path)
+{
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ const char *p;
+ char *q;
+ int l, n;
+
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa) - 1;
+ p = ANY_ADDRESS;
+ n = 3;
+
+ addr->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_addr = q;
+ p = sender;
+ n = strlen(sender);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_path = q;
+ p = path;
+ n = strlen(p);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+
+ return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ socklen_t len;
+
+ len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+
+ if (len > 0) {
+ if (typep != NULL)
+ *typep = DBUS;
+ }
+
+ return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+ MRP_UNUSED(mt);
+
+ return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+ t->dbus = mrp_dbus_ref(dbus);
+
+ if (t->dbus != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = NULL;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+ const char *method;
+
+ if (t->bound) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (t->dbus == NULL) {
+ dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (dbus == NULL) {
+ errno = ECONNRESET;
+ goto fail;
+ }
+ else {
+ t->dbus = dbus;
+
+ if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+ if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+ errno = EADDRINUSE; /* XXX TODO, should check error... */
+ goto fail;
+ }
+ }
+ }
+ }
+ else {
+ /* XXX TODO: should check given address against address of the bus */
+ }
+
+ copy_address(&t->local, addr);
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto fail;
+ }
+
+ if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t)) {
+ errno = EIO;
+ goto fail;
+ }
+ else {
+ t->bound = TRUE;
+ return TRUE;
+ }
+
+ fail:
+ if (dbus != NULL) {
+ mrp_dbus_unref(dbus);
+ t->dbus = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+ char astr[MRP_SOCKADDR_SIZE];
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+
+ alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+ if (alen > 0)
+ return dbus_bind(mt, &addr, alen);
+ else
+ return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr;
+ const char *method;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+ if (t->bound) {
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ default:
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ }
+
+ addr = (mrp_dbusaddr_t *)&t->local;
+ mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t);
+ }
+
+ if (t->connected && t->remote.db_addr != NULL)
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+
+ mrp_dbus_unref(t->dbus);
+ t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ mrp_msg_t *msg;
+
+ MRP_UNUSED(dbus);
+
+ msg = msg_decode(dmsg, &sender_path);
+
+ if (msg != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsg(mt, msg, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ uint16_t tag;
+ void *decoded;
+
+ MRP_UNUSED(dbus);
+
+ decoded = data_decode(dmsg, &tag, &sender_path);
+
+ if (decoded != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode custom data message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ void *data;
+ size_t size;
+
+ MRP_UNUSED(dbus);
+
+ data = raw_decode(dmsg, &size, &sender_path);
+
+ if (data != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvraw(mt, data, size, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvrawfrom(mt, data, size, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode raw message.");
+ }
+
+ return TRUE;
+}
+
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ dbus_t *t = (dbus_t *)user_data;
+ mrp_sockaddr_t addr;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ peer_address(&addr, owner, t->remote.db_path);
+ copy_address(&t->remote, (mrp_dbusaddr_t *)&addr);
+ t->peer_resolved = TRUE;
+ }
+ else {
+ /*
+ * XXX TODO:
+ * It would be really tempting here to call
+ * mt->evt.closed(mt, ECONNRESET, mt->user_data)
+ * to notify the user about the fact our peer went down.
+ * However, that would not be in line with the other
+ * transports which call the closed event handler only
+ * upon foricble transport closes upon errors.
+ *
+ * The transport interface abstraction (especially the
+ * available set of events) anyway needs some eyeballing,
+ * so the right thing to do might be to define a new event
+ * for disconnection and call the handler for that here...
+ */
+ }
+
+}
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (t->dbus == NULL) {
+ t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (t->dbus == NULL) {
+ errno = ECONNRESET;
+ return FALSE;
+ }
+ }
+ else {
+ /* XXX TODO: check given address against address of the bus */
+ }
+
+ if (!t->bound)
+ if (!dbus_autobind(mt, addrp))
+ return FALSE;
+
+ if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) {
+ copy_address(&t->remote, addr);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+
+ if (t->connected && t->remote.db_addr != NULL) {
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+ mrp_clear(&t->remote);
+ t->peer_resolved = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = msg_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_MESSAGE,
+ t->local.db_path, msg);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(data);
+ MRP_UNUSED(size);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = raw_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_RAW,
+ t->local.db_path, data, size);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = data_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_DATA,
+ t->local.db_path, data, tag);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+ case MRP_MSG_FIELD_##from: \
+ return MRP_DBUS_TYPE_##to##_AS_STRING;
+
+ switch (type) {
+ MAP(STRING, STRING);
+ MAP(BOOL , BOOLEAN);
+ MAP(UINT8 , UINT16);
+ MAP(SINT8 , INT16);
+ MAP(UINT16, UINT16);
+ MAP(SINT16, INT16);
+ MAP(UINT32, UINT32);
+ MAP(SINT32, INT32);
+ MAP(UINT64, UINT64);
+ MAP(SINT64, INT64);
+ MAP(DOUBLE, DOUBLE);
+ MAP(BLOB , BYTE);
+ default:
+ return NULL;
+ }
+}
+
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg)
+{
+ /*
+ * Notes: There is a type mismatch between our and DBUS types for
+ * 8-bit integers (D-BUS does not have a signed 8-bit type)
+ * and boolean types (D-BUS has uint32_t booleans, C99 fails
+ * to specify the type and gcc uses a signed char). The
+ * QUIRKY versions of the macros take care of these mismatches.
+ */
+
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+ uint16_t base;
+ uint32_t asize, i;
+ const char *sig;
+ int type, len;
+ void *vptr;
+ dbus_bool_t bln;
+ uint16_t u16, blb;
+ int16_t s16;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &msg->nfield))
+ goto fail;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ switch (f->type) {
+ BASIC_SIMPLE(m, STRING, STRING , f->str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, f->bln, bln);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , f->u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , f->s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , f->u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , f->s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , f->u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , f->s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , f->u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , f->s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , f->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ vptr = f->blb;
+ len = (int)f->size[0];
+ sig = get_array_signature(f->type);
+ asize = len;
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ blb = ((uint8_t *)f->blb)[i];
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, &blb))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ switch (base) {
+ ARRAY_SIMPLE(m, STRING, STRING , f->astr[i]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, f->abln[i], bln);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , f->au8[i] , u16);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , f->as8[i] , s16);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , f->au16[i]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , f->as16[i]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , f->au32[i]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , f->as32[i]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , f->au64[i]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , f->adbl[i]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ \
+ if (!mrp_msg_append(msg, tag, type, (_var))) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ if (!mrp_msg_append(msg, tag, type, (_mvar))) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+#define APPEND_ARRAY(_type, _var) \
+ case MRP_MSG_FIELD_##_type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY | \
+ MRP_MSG_FIELD_##_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ mrp_msg_t *msg;
+ mrp_msg_value_t v;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ uint16_t nfield, tag, type, base, i;
+ uint32_t n, j;
+ int asize;
+ const char *sender, *sig;
+
+ msg = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ switch (type) {
+ BASIC_SIMPLE(m, STRING, STRING , v.str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, v.bln, u32);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , v.u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , v.s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , v.u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , v.s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , v.u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , v.s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , v.u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , v.s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , v.dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ {
+ uint8_t blb[n];
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ asize = n;
+ if (!mrp_msg_append(msg, tag, type, asize, blb))
+ goto fail;
+ }
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = get_array_signature(base);
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ char *astr[n];
+ uint32_t dbln[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(m, STRING, STRING , astr[j]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, abln[j], dbln[j]);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , au8[j] , au16[j]);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , as8[j] , as16[j]);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , au16[j]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , as16[j]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , au32[j]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , as32[j]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , au64[j]);
+ ARRAY_SIMPLE(m, SINT64, INT64 , as64[j]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , adbl[j]);
+ default:
+ goto fail;
+ }
+ }
+
+ switch (base) {
+ APPEND_ARRAY(STRING, astr);
+ APPEND_ARRAY(BOOL , abln);
+ APPEND_ARRAY(UINT8 , au8 );
+ APPEND_ARRAY(SINT8 , as8 );
+ APPEND_ARRAY(UINT16, au16);
+ APPEND_ARRAY(SINT16, as16);
+ APPEND_ARRAY(UINT32, au32);
+ APPEND_ARRAY(SINT32, as32);
+ APPEND_ARRAY(UINT64, au64);
+ APPEND_ARRAY(SINT64, as64);
+ APPEND_ARRAY(DOUBLE, adbl);
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ errno = EBADMSG;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type, base;
+ mrp_msg_value_t *v;
+ void *vptr;
+ uint32_t n, j;
+ int i, blblen;
+ const char *sig;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t bln, asize;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ BASIC_SIMPLE(STRING, STRING , v->str);
+ BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln);
+ BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+ BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16);
+ BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+ BASIC_SIMPLE(SINT16, INT16 , v->s16);
+ BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+ BASIC_SIMPLE(SINT32, INT32 , v->s32);
+ BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+ BASIC_SIMPLE(SINT64, INT64 , v->s64);
+ BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ sig = get_array_signature(f->type);
+ blblen = mrp_data_get_blob_size(data, descr, i);
+ asize = blblen;
+
+ if (blblen == -1)
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < blblen; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE,
+ f->blb + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ n = mrp_data_get_array_size(data, descr, i);
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(STRING, STRING , v->astr[j]);
+ ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln);
+ ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+ ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16);
+ ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+ ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]);
+ ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+ ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]);
+ ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+ ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+static void *data_decode(mrp_dbus_msg_t *m, uint16_t *tagp,
+ const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+ void *data;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t tag, type, base;
+ mrp_msg_value_t *v;
+ uint32_t n, j, size;
+ int i;
+ const char *sender, *sig;
+ uint32_t u32;
+ uint16_t u16;
+ int16_t s16;
+
+ tag = 0;
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ *tagp = tag;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ if (nfield != descr->nfield)
+ goto fail;
+
+ fields = descr->fields;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (type) {
+ HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+ HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32);
+ HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+ HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16);
+ HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+ HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16);
+ HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+ HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32);
+ HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+ HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64);
+ HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &size))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t blb[size];
+
+ for (j = 0; j < size; j++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+
+ v->blb = mrp_alloc(size);
+
+ if (v->blb == NULL && size != 0)
+ goto fail;
+
+ memcpy(v->blb, blb, size);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ uint32_t dbln[n];
+ uint16_t au16[n];
+ int16_t as16[n];
+
+ switch (base) {
+ HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+ HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]);
+ HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+ HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]);
+ HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+ HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]);
+ HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+ HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]);
+ HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+ HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]);
+ HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+ }
+
+ if (base == MRP_MSG_FIELD_STRING) {
+ v->astr[j] = mrp_strdup(v->astr[j]);
+ if (v->astr[j] == NULL)
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+
+ if (f->type == MRP_MSG_FIELD_STRING) {
+ v->str = mrp_strdup(v->str);
+ if (v->str == NULL)
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ mrp_data_free(data, tag);
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size)
+{
+ mrp_dbus_msg_t *m;
+ const char *sig;
+ uint32_t i, n;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+ n = size;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, data + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+
+ return m;
+
+ fail:
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+}
+
+
+static void *raw_decode(mrp_dbus_msg_t *m, size_t *sizep,
+ const char **sender_id)
+{
+ const char *sender, *sig;
+ void *data;
+ uint32_t n, i;
+
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t databuf[n];
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, databuf + i))
+ goto fail;
+
+ data = mrp_alloc(n);
+
+ if (data == NULL && n != 0)
+ goto fail;
+
+ memcpy(data, databuf, n);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ if (sizep != NULL)
+ *sizep = (size_t)n;
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+ dbus_open, dbus_createfrom, dbus_close, NULL,
+ dbus_bind, NULL, NULL,
+ dbus_connect, dbus_disconnect,
+ dbus_sendmsg, dbus_sendmsgto,
+ dbus_sendraw, dbus_sendrawto,
+ dbus_senddata, dbus_senddatato,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-libdbus.h>
+
+
+#define DBUS_ADMIN_SERVICE "org.freedesktop.DBus"
+#define DBUS_ADMIN_INTERFACE "org.freedesktop.DBus"
+#define DBUS_ADMIN_PATH "/org/freedesktop/DBus"
+#define DBUS_NAME_CHANGED "NameOwnerChanged"
+
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ DBusConnection *conn; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+struct mrp_dbus_msg_s {
+ DBusMessage *msg; /* actual D-BUS message */
+ mrp_refcnt_t refcnt; /* reference count */
+ mrp_list_hook_t iterators; /* iterator stack */
+ mrp_list_hook_t arrays; /* implicitly freed related arrays */
+};
+
+
+typedef struct {
+ DBusMessageIter it; /* actual iterator */
+ char *peeked; /* peeked contents, or NULL */
+ mrp_list_hook_t hook; /* hook to iterator stack */
+} msg_iter_t;
+
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char **items;
+ size_t nitem;
+} msg_array_t;
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ DBusPendingCall *pend; /* pending DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ void *data);
+static void call_free(call_t *call);
+static void free_msg_array(msg_array_t *a);
+
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->conn, FALSE);
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ if (dbus->conn != NULL) {
+ if (dbus->signal_filter)
+ dbus_connection_remove_filter(dbus->conn, dispatch_signal,
+ dbus);
+ if (dbus->register_fallback)
+ dbus_connection_unregister_object_path(dbus->conn, "/");
+ if (dbus->priv)
+ dbus_connection_close(dbus->conn);
+ dbus_connection_unref(dbus->conn);
+ }
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ mrp_free(dbus->address);
+ dbus->conn = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp)
+{
+ static struct DBusObjectPathVTable vtable = {
+ .message_function = dispatch_method
+ };
+
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, errp);
+ else if (!strcmp(address, "session"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SESSION, errp);
+ else {
+ dbus->conn = dbus_connection_open_private(address, errp);
+ dbus->priv = TRUE;
+
+ if (dbus->conn == NULL || !dbus_bus_register(dbus->conn, errp))
+ goto fail;
+ }
+
+ if (dbus->conn == NULL)
+ goto fail;
+
+ dbus->address = mrp_strdup(address);
+ dbus->unique_name = dbus_bus_get_unique_name(dbus->conn);
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_connection(ml, dbus->conn))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (!dbus_connection_add_filter(dbus->conn, dispatch_signal, dbus, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (!dbus_connection_register_fallback(dbus->conn, "/", &vtable, dbus)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name_owner_change_cb, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ DBUS_ADMIN_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->conn, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int flags, status;
+
+ mrp_dbus_error_init(error);
+
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ status = dbus_bus_request_name(dbus->conn, name, flags, error);
+
+ if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ return TRUE;
+ else {
+ if (status == DBUS_REQUEST_NAME_REPLY_EXISTS) {
+ if (error)
+ dbus_error_free(error);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "name already taken");
+ }
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ mrp_dbus_error_init(error);
+
+ if (dbus_bus_release_name(dbus->conn, name, error) != -1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ DBusMessage *msg = m->msg;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &owner,
+ DBUS_TYPE_INVALID))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+ DBusMessage *msg;
+
+ MRP_UNUSED(data);
+
+ msg = m->msg;
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &prev,
+ DBUS_TYPE_STRING, &next,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+#if 0
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2. checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3. removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+#endif
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, "GetNameOwner", 5000,
+ name_owner_query_cb, t,
+ DBUS_TYPE_STRING, t->name,
+ DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ t->name, NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ return FALSE; \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ DBusError error;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ dbus_error_init(&error);
+ dbus_bus_add_match(dbus->conn, filter, &error);
+
+ if (dbus_error_is_set(&error)) {
+ mrp_log_error("Failed to install filter '%s' (error: %s).", filter,
+ mrp_dbus_errmsg(&error));
+ dbus_error_free(&error);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ dbus_bus_remove_match(dbus->conn, filter, NULL);
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+static inline mrp_dbus_msg_t *create_message(DBusMessage *msg)
+{
+ mrp_dbus_msg_t *m;
+
+ if (msg != NULL) {
+ if ((m = mrp_allocz(sizeof(*m))) != NULL) {
+ mrp_refcnt_init(&m->refcnt);
+ mrp_list_init(&m->iterators);
+ mrp_list_init(&m->arrays);
+ m->msg = dbus_message_ref(msg);
+ }
+ }
+ else
+ m = NULL;
+
+ return m;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m)
+{
+ return mrp_ref_obj(m, refcnt);
+}
+
+
+static void rewind_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_iter_t *it;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->iterators, p, n) {
+ it = mrp_list_entry(p, typeof(*it), hook);
+
+ mrp_list_delete(&it->hook);
+ mrp_free(it->peeked);
+ mrp_free(it);
+ }
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+
+ free_msg_array(a);
+ }
+}
+
+
+static void free_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_iter_t *it;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->iterators, p, n) {
+ it = mrp_list_entry(p, typeof(*it), hook);
+
+ mrp_list_delete(&it->hook);
+ mrp_free(it->peeked);
+ mrp_free(it);
+ }
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+
+ free_msg_array(a);
+ }
+
+ mrp_free(m);
+}
+
+
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m)
+{
+ DBusMessage *msg;
+
+ if (mrp_unref_obj(m, refcnt)) {
+ msg = m->msg;
+
+ free_message(m);
+
+ if (msg != NULL)
+ dbus_message_unref(msg);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ int r = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ if (m == NULL)
+ m = create_message(msg);
+
+ if (m != NULL && h->handler(dbus, m, h->user_data))
+ r = DBUS_HANDLER_RESULT_HANDLED;
+
+ goto out;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ out:
+ mrp_dbus_msg_unref(m);
+
+ if (r == DBUS_HANDLER_RESULT_NOT_YET_HANDLED)
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ return r;
+}
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__,
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ if (m == NULL)
+ m = create_message(msg);
+
+ if (m == NULL)
+ goto out;
+
+ h->handler(dbus, m, h->user_data);
+ handled = TRUE;
+
+ rewind_message(m);
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ out:
+ mrp_dbus_msg_unref(m);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static int append_args_inttype(DBusMessage *msg, int type, va_list ap)
+{
+ void *vptr;
+ void **aptr;
+ int atype, alen;
+ int r = TRUE;
+
+ while (type != MRP_DBUS_TYPE_INVALID && r) {
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE:
+ case MRP_DBUS_TYPE_BOOLEAN:
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16:
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32:
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64:
+ case MRP_DBUS_TYPE_DOUBLE:
+ case MRP_DBUS_TYPE_UNIX_FD:
+ vptr = va_arg(ap, void *);
+ r = dbus_message_append_args(msg, type, vptr, DBUS_TYPE_INVALID);
+ break;
+
+ case MRP_DBUS_TYPE_STRING:
+ case MRP_DBUS_TYPE_OBJECT_PATH:
+ case MRP_DBUS_TYPE_SIGNATURE:
+ vptr = va_arg(ap, void *);
+ r = dbus_message_append_args(msg, type, &vptr, DBUS_TYPE_INVALID);
+ break;
+
+ case MRP_DBUS_TYPE_ARRAY:
+ atype = va_arg(ap, int);
+ aptr = va_arg(ap, void **);
+ alen = va_arg(ap, int);
+ r = dbus_message_append_args(msg, DBUS_TYPE_ARRAY,
+ atype, &aptr, alen, DBUS_TYPE_INVALID);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ type = va_arg(ap, int);
+ }
+
+ return r;
+}
+
+
+static void call_reply_cb(DBusPendingCall *pend, void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ DBusMessage *reply;
+ mrp_dbus_msg_t *m;
+
+ reply = dbus_pending_call_steal_reply(pend);
+ m = create_message(reply);
+
+ call->pend = NULL;
+ mrp_list_delete(&call->hook);
+
+ call->cb(call->dbus, m, call->user_data);
+
+ mrp_dbus_msg_unref(m);
+ dbus_message_unref(reply);
+ dbus_pending_call_unref(pend);
+
+ call_free(call);
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ DBusMessage *msg;
+ DBusPendingCall *pend;
+ int success;
+
+ call = NULL;
+ pend = NULL;
+
+ msg = dbus_message_new_method_call(dest, path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ dbus_message_unref(msg);
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ mrp_dbus_msg_t *m)
+{
+ int32_t id;
+ call_t *call;
+ DBusPendingCall *pend;
+ DBusMessage *msg;
+ int method;
+
+ call = NULL;
+ pend = NULL;
+ msg = m->msg;
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ if (cb != NULL)
+ goto fail;
+ else
+ method = FALSE;
+ }
+ else
+ method = TRUE;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (!dbus_message_set_destination(msg, dest))
+ goto fail;
+ if (!dbus_message_set_path(msg, path))
+ goto fail;
+ if (!dbus_message_set_interface(msg, interface))
+ goto fail;
+ if (!dbus_message_set_member(msg, member))
+ goto fail;
+
+ if (cb == NULL) {
+ if (method)
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m)
+{
+ return dbus_connection_send(dbus->conn, m->msg, NULL);
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ dbus_pending_call_cancel(call->pend);
+ dbus_pending_call_unref(call->pend);
+ call->pend = NULL;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg, *rpl;
+ int success;
+
+ msg = m->msg;
+ rpl = dbus_message_new_method_return(msg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg, *rpl;
+ int success;
+
+ msg = m->msg;
+ rpl = dbus_message_new_error(msg, errname, errmsg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL)
+ mrp_free(call);
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->pend != NULL)
+ dbus_pending_call_unref(call->pend);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg;
+ int success;
+
+ msg = dbus_message_new_signal(path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (dest && *dest && !dbus_message_set_destination(msg, dest))
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ /*
+ * XXX TODO: Hmm... IIRC, libdbus unrefs messages upon failure. If it
+ * was really so, this would corrupt/crash. Check this from
+ * libdbus code.
+ */
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ return 0;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ mrp_dbus_msg_t *m;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_method_call(destination, path, interface, member);
+
+ if (msg != NULL) {
+ m = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else
+ m = NULL;
+
+ return m;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus,
+ mrp_dbus_msg_t *m)
+{
+ mrp_dbus_msg_t *mr;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_method_return(m->msg);
+
+ if (msg != NULL) {
+ mr = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else
+ mr = NULL;
+
+ return mr;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *m,
+ mrp_dbus_err_t *err)
+{
+ mrp_dbus_msg_t *me;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_error(m->msg, err->name, err->message);
+
+ if (msg != NULL) {
+ me = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else
+ me = NULL;
+
+ return me;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ mrp_dbus_msg_t *m;
+ DBusMessage *msg;
+
+ MRP_UNUSED(bus);
+
+ msg = dbus_message_new_signal(path, interface, member);
+
+ if (msg != NULL) {
+ if (!destination || dbus_message_set_destination(msg, destination)) {
+ m = create_message(msg);
+ dbus_message_unref(msg);
+ }
+ else {
+ dbus_message_unref(msg);
+ m = NULL;
+ }
+ }
+ else
+ m = NULL;
+
+ return m;
+}
+
+
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m)
+{
+ return (mrp_dbus_msg_type_t)dbus_message_get_type(m->msg);
+}
+
+#define WRAP_GETTER(type, what) \
+ type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \
+ { \
+ return dbus_message_get_##what(m->msg); \
+ } \
+ struct __mrp_dbus_allow_trailing_semicolon
+
+WRAP_GETTER(const char *, path);
+WRAP_GETTER(const char *, interface);
+WRAP_GETTER(const char *, member);
+WRAP_GETTER(const char *, destination);
+WRAP_GETTER(const char *, sender);
+
+#undef WRAP_GETTER
+
+
+static msg_iter_t *message_iterator(mrp_dbus_msg_t *m, int append)
+{
+ msg_iter_t *it;
+
+ if (mrp_list_empty(&m->iterators)) {
+ if ((it = mrp_allocz(sizeof(*it))) != NULL) {
+ mrp_list_init(&it->hook);
+ mrp_list_append(&m->iterators, &it->hook);
+
+ if (append)
+ dbus_message_iter_init_append(m->msg, &it->it);
+ else
+ dbus_message_iter_init(m->msg, &it->it);
+ }
+ }
+ else
+ it = mrp_list_entry(&m->iterators.next, typeof(*it), hook);
+
+ return it;
+}
+
+
+msg_iter_t *current_iterator(mrp_dbus_msg_t *m)
+{
+ msg_iter_t *it;
+
+ if (!mrp_list_empty(&m->iterators))
+ it = mrp_list_entry(m->iterators.prev, typeof(*it), hook);
+ else
+ it = NULL;
+
+ return it;
+}
+
+
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ msg_iter_t *it, *parent;
+
+ if ((parent = current_iterator(m)) == NULL &&
+ (parent = message_iterator(m, TRUE)) == NULL)
+ return FALSE;
+
+ if ((it = mrp_allocz(sizeof(*it))) != NULL) {
+ mrp_list_init(&it->hook);
+
+ if (dbus_message_iter_open_container(&parent->it, type, contents,
+ &it->it)) {
+ mrp_list_append(&m->iterators, &it->hook);
+
+ return TRUE;
+ }
+
+ mrp_free(it);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m)
+{
+ msg_iter_t *it, *parent;
+ int r;
+
+ it = current_iterator(m);
+
+ if (it == NULL || it == message_iterator(m, FALSE))
+ return FALSE;
+
+ mrp_list_delete(&it->hook);
+
+ if ((parent = current_iterator(m)) != NULL)
+ r = dbus_message_iter_close_container(&parent->it, &it->it);
+ else
+ r = FALSE;
+
+ mrp_free(it);
+
+ return r;
+}
+
+
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ msg_iter_t *it;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ if ((it = current_iterator(m)) != NULL ||
+ (it = message_iterator(m, TRUE)) != NULL) {
+ if (type != MRP_DBUS_TYPE_STRING &&
+ type != MRP_DBUS_TYPE_OBJECT_PATH &&
+ type != MRP_DBUS_TYPE_SIGNATURE)
+ return dbus_message_iter_append_basic(&it->it, type, valuep);
+ else
+ return dbus_message_iter_append_basic(&it->it, type, &valuep);
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ msg_iter_t *it, *parent;
+ char *signature;
+
+ if ((parent = current_iterator(m)) == NULL &&
+ (parent = message_iterator(m, FALSE)) == NULL)
+ return FALSE;
+
+ if (dbus_message_iter_get_arg_type(&parent->it) != type)
+ return FALSE;
+
+ if ((it = mrp_allocz(sizeof(*it))) != NULL) {
+ mrp_list_init(&it->hook);
+ mrp_list_append(&m->iterators, &it->hook);
+
+ dbus_message_iter_recurse(&parent->it, &it->it);
+
+ if (contents != NULL) {
+ /* XXX TODO: proper signature checking */
+ signature = dbus_message_iter_get_signature(&it->it);
+ if (strcmp(contents, signature))
+ mrp_log_error("*** %s(): signature mismath ('%s' != '%s')",
+ __FUNCTION__, contents, signature);
+ mrp_free(signature);
+ }
+
+ dbus_message_iter_next(&parent->it);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m)
+{
+ msg_iter_t *it;
+
+ if ((it = current_iterator(m)) == NULL || it == message_iterator(m, FALSE))
+ return FALSE;
+
+ mrp_list_delete(&it->hook);
+
+ mrp_free(it->peeked);
+ mrp_free(it);
+
+ return TRUE;
+}
+
+
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ msg_iter_t *it;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ if ((it = current_iterator(m)) != NULL ||
+ (it = message_iterator(m, FALSE)) != NULL) {
+ if (dbus_message_iter_get_arg_type(&it->it) == type) {
+ dbus_message_iter_get_basic(&it->it, valuep);
+ dbus_message_iter_next(&it->it);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void free_msg_array(msg_array_t *a)
+{
+ if (a == NULL)
+ return;
+
+ mrp_list_delete(&a->hook);
+ mrp_free(a->items);
+ mrp_free(a);
+}
+
+
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp)
+{
+ msg_iter_t *it;
+ msg_array_t *a;
+ DBusMessageIter sub;
+ void *items;
+ int nitem, atype;
+
+ if (!dbus_type_is_basic(type))
+ return FALSE;
+
+ if ((it = current_iterator(m)) == NULL &&
+ (it = message_iterator(m, FALSE)) == NULL)
+ return FALSE;
+
+ if (dbus_message_iter_get_arg_type(&it->it) != DBUS_TYPE_ARRAY)
+ return FALSE;
+
+ dbus_message_iter_recurse(&it->it, &sub);
+ atype = dbus_message_iter_get_arg_type(&sub);
+
+ if (atype == MRP_DBUS_TYPE_INVALID) {
+ items = NULL;
+ nitem = 0;
+
+ goto out;
+ }
+
+ if (atype != type)
+ return FALSE;
+
+ /* for fixed types, just use the libdbus function */
+ if (type != MRP_DBUS_TYPE_STRING && type != MRP_DBUS_TYPE_OBJECT_PATH) {
+ nitem = -1;
+ items = NULL;
+ dbus_message_iter_get_fixed_array(&sub, (void *)&items, &nitem);
+
+ if (nitem == -1)
+ return FALSE;
+ }
+ /* for string-like types, collect items into an implicitly freed array */
+ else {
+ a = mrp_allocz(sizeof(*a));
+
+ if (a == NULL)
+ return FALSE;
+
+ mrp_list_init(&a->hook);
+
+ while (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_INVALID) {
+ if (mrp_reallocz(a->items, a->nitem, a->nitem + 1) != NULL) {
+ dbus_message_iter_get_basic(&sub, a->items + a->nitem);
+ a->nitem++;
+ dbus_message_iter_next(&sub);
+ }
+ else {
+ free_msg_array(a);
+ return FALSE;
+ }
+ }
+
+ mrp_list_append(&m->arrays, &a->hook);
+
+ items = a->items;
+ nitem = a->nitem;
+ }
+
+ out:
+ dbus_message_iter_next(&it->it);
+
+ *itemsp = items;
+ *nitemp = (size_t)nitem;
+
+ return TRUE;
+}
+
+
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents)
+{
+ msg_iter_t *it;
+ DBusMessageIter sub;
+ char type;
+
+ if ((it = current_iterator(m)) != NULL ||
+ (it = message_iterator(m, FALSE)) != NULL) {
+ type = dbus_message_iter_get_arg_type(&it->it);
+
+ if (dbus_type_is_container(type)) {
+ mrp_free(it->peeked);
+
+ if (contents != NULL) {
+ dbus_message_iter_recurse(&it->it, &sub);
+ it->peeked = dbus_message_iter_get_signature(&sub);
+ *contents = it->peeked;
+ }
+ }
+
+ return type;
+ }
+
+ return MRP_DBUS_TYPE_INVALID;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_LIBDBUS_H__
+#define __MURPHY_DBUS_LIBDBUS_H__
+
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-error.h>
+#include <dbus/dbus.h>
+
+/** Type for a D-Bus (connection). */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** Type for a D-Bus message. */
+struct mrp_dbus_msg_s;
+typedef struct mrp_dbus_msg_s mrp_dbus_msg_t;
+
+/** Type for a D-Bus error. */
+typedef DBusError mrp_dbus_err_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp);
+#define mrp_dbus_get mrp_dbus_connect
+
+/** Set up a DBusConnection with a mainloop. */
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Type for a name tracking callback. */
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int,
+ const char *, void *);
+/** Start tracking the given name. */
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+/** Stop tracking the given name. */
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+
+/** Export a method to the bus. */
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Remove an exported method. */
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Install a filter and add a handler for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove the signal handler and filter for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Remove a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove a filter for the given message on the bus. */
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Add a signal handler for the gvien signal on the bus. */
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Remove the given signal handler for the given signal on the bus. */
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Type of a method call reply callback. */
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
+ void *user_data);
+
+/** Call the given method on the bus. */
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest,
+ const char *path, const char *interface,
+ const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ int dbus_type, ...);
+
+/** Cancel an ongoing method call on the bus. */
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+/** Send a reply to the given method call on the bus. */
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, int type, ...);
+
+/** Send an error reply to the given method call on the bus. */
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
+ const char *errname, const char *errmsg,
+ int type, ...);
+
+/** Emit the given signal on the bus. */
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...);
+
+/** Send the given method call message on the bus. */
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ mrp_dbus_msg_t *msg);
+
+/** Send the given message on the bus. */
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg);
+
+/** Get our unique name on the bus. */
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+/** Initialize the given error. */
+static inline mrp_dbus_err_t *mrp_dbus_error_init(mrp_dbus_err_t *err)
+{
+ if (err != NULL)
+ dbus_error_init(err);
+
+ return err;
+}
+
+/** Set the given error buffer up with the error name and message. */
+static inline mrp_dbus_err_t *mrp_dbus_error_set(mrp_dbus_err_t *err,
+ const char *name,
+ const char *message)
+{
+ dbus_set_error_const(err, name, message);
+
+ return err;
+}
+
+
+/** Get the error message from the given bus error message. */
+static inline const char *mrp_dbus_errmsg(mrp_dbus_err_t *err)
+{
+ if (err && dbus_error_is_set(err))
+ return err->message;
+ else
+ return "unknown DBUS error";
+}
+
+
+/** Increase the reference count of a message. */
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m);
+
+/** Decrease the reference count of a message, freeing it if necessary. */
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m);
+
+
+/** Create a new method call message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Create a new method return message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus,
+ mrp_dbus_msg_t *msg);
+
+/** Create a new error reply message. */
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *msg,
+ mrp_dbus_err_t *err);
+
+/** Create a new signal message. */
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Bus message types. */
+typedef enum {
+# define TYPE(type) MRP_DBUS_MESSAGE_TYPE_##type = DBUS_MESSAGE_TYPE_##type
+ TYPE(INVALID),
+ TYPE(METHOD_CALL),
+ TYPE(METHOD_RETURN),
+ TYPE(ERROR),
+ TYPE(SIGNAL)
+# undef TYPE
+} mrp_dbus_msg_type_t;
+
+/** Get the type of the given message. */
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *msg);
+
+/** Message type checking convenience functions. */
+#define TYPE_CHECK_FUNCTION(type, TYPE) \
+ static inline int mrp_dbus_msg_is_##type(mrp_dbus_msg_t *msg) \
+ { \
+ return mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_##TYPE; \
+ } \
+ struct __mrp_dbus_allow_traling_semicolon
+
+TYPE_CHECK_FUNCTION(method_call , METHOD_CALL);
+TYPE_CHECK_FUNCTION(method_return, METHOD_RETURN);
+TYPE_CHECK_FUNCTION(error , ERROR);
+TYPE_CHECK_FUNCTION(signal , SIGNAL);
+
+/** Message argument types. */
+typedef enum {
+#define TYPE(t) MRP_DBUS_TYPE_##t = DBUS_TYPE_##t
+ TYPE(INVALID),
+ TYPE(BYTE),
+ TYPE(BOOLEAN),
+ TYPE(INT16),
+ TYPE(UINT16),
+ TYPE(INT32),
+ TYPE(UINT32),
+ TYPE(INT64),
+ TYPE(UINT64),
+ TYPE(DOUBLE),
+ TYPE(STRING),
+ TYPE(OBJECT_PATH),
+ TYPE(SIGNATURE),
+ TYPE(UNIX_FD),
+ TYPE(ARRAY),
+ TYPE(VARIANT),
+ TYPE(STRUCT),
+ TYPE(DICT_ENTRY),
+#undef TYPE
+} mrp_dbus_type_t;
+
+/** Message argument types as strings. */
+#define MRP_DBUS_TYPE_BYTE_AS_STRING DBUS_TYPE_BYTE_AS_STRING
+#define MRP_DBUS_TYPE_BOOLEAN_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING
+#define MRP_DBUS_TYPE_INT16_AS_STRING DBUS_TYPE_INT16_AS_STRING
+#define MRP_DBUS_TYPE_UINT16_AS_STRING DBUS_TYPE_UINT16_AS_STRING
+#define MRP_DBUS_TYPE_INT32_AS_STRING DBUS_TYPE_INT32_AS_STRING
+#define MRP_DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_UINT32_AS_STRING
+#define MRP_DBUS_TYPE_INT64_AS_STRING DBUS_TYPE_INT64_AS_STRING
+#define MRP_DBUS_TYPE_UINT64_AS_STRING DBUS_TYPE_UINT64_AS_STRING
+#define MRP_DBUS_TYPE_DOUBLE_AS_STRING DBUS_TYPE_DOUBLE_AS_STRING
+#define MRP_DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+#define MRP_DBUS_TYPE_OBJECT_PATH_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING
+#define MRP_DBUS_TYPE_SIGNATURE_AS_STRING DBUS_TYPE_SIGNATURE_AS_STRING
+#define MRP_DBUS_TYPE_UNIX_FD_AS_STRING DBUS_TYPE_UNIX_FD_AS_STRING
+#define MRP_DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_ARRAY_AS_STRING
+#define MRP_DBUS_TYPE_VARIANT_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+#define MRP_DBUS_TYPE_STRUCT_AS_STRING DBUS_TYPE_STRUCT_AS_STRING
+#define MRP_DBUS_TYPE_DICT_ENTRY_AS_STRING DBUS_TYPE_DICT_ENTRY_AS_STRING
+
+/** Get the path of the given message. */
+const char *mrp_dbus_msg_path(mrp_dbus_msg_t *msg);
+
+/** Get the interface of the given message. */
+const char *mrp_dbus_msg_interface(mrp_dbus_msg_t *msg);
+
+/** Get the member of the given message. */
+const char *mrp_dbus_msg_member(mrp_dbus_msg_t *msg);
+
+/** Get the destination of the given message. */
+const char *mrp_dbus_msg_destination(mrp_dbus_msg_t *msg);
+
+/** Get the sender of the given message. */
+const char *mrp_dbus_msg_sender(mrp_dbus_msg_t *msg);
+
+/** Open a new container of the given type and cotained types. */
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents);
+
+/** Close the current container. */
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m);
+
+/** Append an argument of a basic type to the given message. */
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Get the type of the current message argument. */
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents);
+
+/** Open the current container (of the given type and contents) for reading. */
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *msg, char type,
+ const char *contents);
+
+/** Exit from the container being currently read. */
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m);
+
+/** Read the next argument (of basic type) from the given message. */
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Read the next array of one of the basic types. */
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *msg, char type,
+ void **itemsp, size_t *nitemp);
+
+#endif /* __MURPHY_DBUS_LIBDBUS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+#include <murphy/common/dbus-sdbus.h>
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000)
+
+typedef struct {
+ sd_bus *bus;
+ mrp_mainloop_t *ml;
+ mrp_subloop_t *sl;
+ int events;
+} bus_glue_t;
+
+
+static int bus_prepare(void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ return FALSE;
+}
+
+
+static int bus_query(void *user_data, struct pollfd *fds, int nfd, int *timeout)
+{
+ bus_glue_t *b = (bus_glue_t *)user_data;
+ uint64_t usec;
+
+ if (nfd > 0) {
+ fds[0].fd = sd_bus_get_fd(b->bus);
+ fds[0].events = sd_bus_get_events(b->bus) | POLLIN | POLLHUP;
+ fds[0].revents = 0;
+
+ if (sd_bus_get_timeout(b->bus, &usec) < 0)
+ *timeout = -1;
+ else
+ *timeout = USEC_TO_MSEC(usec);
+
+ mrp_debug("fd: %d, events: 0x%x, timeout: %u", fds[0].fd,
+ fds[0].events, *timeout);
+ }
+
+ return 1;
+}
+
+
+static int bus_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ bus_glue_t *b = (bus_glue_t *)user_data;
+
+ if (nfd > 0) {
+ b->events = fds[0].revents;
+
+ if (b->events != 0)
+ return TRUE;
+ }
+ else
+ b->events = 0;
+
+ return FALSE;
+}
+
+
+static void bus_dispatch(void *user_data)
+{
+ bus_glue_t *b = (bus_glue_t *)user_data;
+
+ mrp_debug("dispatching events 0x%x to sd_bus %p", b->events, b->bus);
+
+ if (b->events & MRP_IO_EVENT_HUP)
+ mrp_debug("sd_bus peer has closed the connection");
+
+ while (sd_bus_process(b->bus, NULL) > 0)
+ sd_bus_flush(b->bus);
+
+ mrp_debug("done dispatching");
+}
+
+
+int mrp_dbus_setup_with_mainloop(mrp_mainloop_t *ml, sd_bus *bus)
+{
+ static mrp_subloop_ops_t bus_ops = {
+ .prepare = bus_prepare,
+ .query = bus_query,
+ .check = bus_check,
+ .dispatch = bus_dispatch
+ };
+
+ bus_glue_t *b;
+
+
+ if ((b = mrp_allocz(sizeof(*b))) != NULL) {
+ /* XXX TODO: Hmm... is this really needed ? */
+ while (sd_bus_process(bus, NULL) > 0)
+ sd_bus_flush(bus);
+
+ b->bus = bus;
+ b->ml = ml;
+ b->sl = mrp_add_subloop(ml, &bus_ops, b);
+
+ if (b->sl != NULL)
+ return TRUE;
+ else
+ mrp_free(b);
+ }
+
+ return FALSE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/dbus-sdbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH "/murphy/transport"
+#define TRANSPORT_INTERFACE "Murphy.Transport"
+#define TRANSPORT_MESSAGE "DeliverMessage"
+#define TRANSPORT_DATA "DeliverData"
+#define TRANSPORT_RAW "DeliverRaw"
+#define TRANSPORT_METHOD "DeliverMessage"
+
+#define ANY_ADDRESS "any"
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ mrp_dbus_t *dbus; /* D-BUS connection */
+ int bound : 1; /* whether bound to an address */
+ int peer_resolved : 1; /* connected and peer name known */
+ mrp_dbusaddr_t local; /* address we're bound to */
+ mrp_dbusaddr_t remote; /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto; /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data);
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *msg, const char **sender_id);
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag);
+static void *data_decode(mrp_dbus_msg_t *msg, uint16_t *tag,
+ const char **sender_id);
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size);
+static void *raw_decode(mrp_dbus_msg_t *msg, size_t *sizep,
+ const char **sender_id);
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+ socklen_t size)
+{
+ const char *p, *e;
+ char *q;
+ size_t l, n;
+
+ if (size < sizeof(*addr)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (strncmp(str, DBUS":", DBUSL + 1))
+ return 0;
+ else
+ str += DBUSL + 1;
+
+ /*
+ * The format of the address is
+ * dbus:[bus-address]@address/path
+ * eg.
+ * dbus:[session]@:1.33/client1, or
+ * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+ */
+
+ p = str;
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+
+ /* get bus address */
+ if (*p != '[') {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ p++;
+
+ e = strchr(p, ']');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save bus address */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_bus = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e + 1;
+
+ /* get (local or remote) address on bus */
+ if (*p != '@')
+ addr->db_addr = ANY_ADDRESS;
+ else {
+ p++;
+ e = strchr(p, '/');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save address on bus */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_addr = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e;
+ }
+
+ /* get object path */
+ if (*p != '/') {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ addr->db_path = q;
+ addr->db_family = MRP_AF_DBUS;
+
+ return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+ char *p, *q;
+ size_t l, n;
+
+ dst->db_family = src->db_family;
+
+ /* copy bus address */
+ p = src->db_bus;
+ q = dst->db_fqa;
+ l = sizeof(dst->db_fqa);
+
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy address */
+ p = src->db_addr;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_addr = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy path */
+ p = src->db_path;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_path = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+
+ return (a && a->db_family == MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+ const char *path)
+{
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ const char *p;
+ char *q;
+ int l, n;
+
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+ p = ANY_ADDRESS;
+ n = 3;
+
+ addr->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_addr = q;
+ p = sender;
+ n = strlen(sender);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_path = q;
+ p = path;
+ n = strlen(p);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+
+ return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ socklen_t len;
+
+ len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+
+ if (len > 0) {
+ if (typep != NULL)
+ *typep = DBUS;
+ }
+
+ return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+ MRP_UNUSED(mt);
+
+ return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+ t->dbus = mrp_dbus_ref(dbus);
+
+ if (t->dbus != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = NULL;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+ const char *method;
+
+ if (t->bound) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (t->dbus == NULL) {
+ dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (dbus == NULL) {
+ errno = ECONNRESET;
+ goto fail;
+ }
+ else {
+ t->dbus = dbus;
+
+ if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+ if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+ errno = EADDRINUSE; /* XXX TODO, should check error... */
+ goto fail;
+ }
+ }
+ }
+ }
+ else {
+ /* XXX TODO: should check given address against address of the bus */
+ }
+
+ copy_address(&t->local, addr);
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto fail;
+ }
+
+ if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t)) {
+ errno = EIO;
+ goto fail;
+ }
+ else {
+ t->bound = TRUE;
+ return TRUE;
+ }
+
+ fail:
+ if (dbus != NULL) {
+ mrp_dbus_unref(dbus);
+ t->dbus = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+ char astr[MRP_SOCKADDR_SIZE];
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+
+ alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+ if (alen > 0)
+ return dbus_bind(mt, &addr, alen);
+ else
+ return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr;
+ const char *method;
+ int (*cb)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+ if (t->bound) {
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ default:
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ }
+
+ addr = (mrp_dbusaddr_t *)&t->local;
+ mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t);
+ }
+
+ if (t->connected && t->remote.db_addr != NULL)
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+
+ mrp_dbus_unref(t->dbus);
+ t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ mrp_msg_t *msg;
+
+ MRP_UNUSED(dbus);
+
+ msg = msg_decode(dmsg, &sender_path);
+
+ if (msg != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsg(mt, msg, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ uint16_t tag;
+ void *decoded;
+
+ MRP_UNUSED(dbus);
+
+ decoded = data_decode(dmsg, &tag, &sender_path);
+
+ if (decoded != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode custom data message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ void *data;
+ size_t size;
+
+ MRP_UNUSED(dbus);
+
+ data = raw_decode(dmsg, &size, &sender_path);
+
+ if (data != NULL) {
+ sender = mrp_dbus_msg_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvraw(mt, data, size, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvrawfrom(mt, data, size, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode raw message.");
+ }
+
+ return TRUE;
+}
+
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ dbus_t *t = (dbus_t *)user_data;
+ mrp_sockaddr_t addr;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ peer_address(&addr, owner, t->remote.db_path);
+ copy_address(&t->remote, (mrp_dbusaddr_t *)&addr);
+ t->peer_resolved = TRUE;
+ }
+ else {
+ /*
+ * XXX TODO:
+ * It would be really tempting here to call
+ * mt->evt.closed(mt, ECONNRESET, mt->user_data)
+ * to notify the user about the fact our peer went down.
+ * However, that would not be in line with the other
+ * transports which call the closed event handler only
+ * upon foricble transport closes upon errors.
+ *
+ * The transport interface abstraction (especially the
+ * available set of events) anyway needs some eyeballing,
+ * so the right thing to do might be to define a new event
+ * for disconnection and call the handler for that here...
+ */
+ }
+
+}
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (t->dbus == NULL) {
+ t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (t->dbus == NULL) {
+ errno = ECONNRESET;
+ return FALSE;
+ }
+ }
+ else {
+ /* XXX TODO: check given address against address of the bus */
+ }
+
+ if (!t->bound)
+ if (!dbus_autobind(mt, addrp))
+ return FALSE;
+
+ if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) {
+ copy_address(&t->remote, addr);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+
+ if (t->connected && t->remote.db_addr != NULL) {
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+ mrp_clear(&t->remote);
+ t->peer_resolved = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = msg_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_MESSAGE,
+ t->local.db_path, msg);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(data);
+ MRP_UNUSED(size);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = raw_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_RAW,
+ t->local.db_path, data, size);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ mrp_dbus_msg_t *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = data_encode(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_DATA,
+ t->local.db_path, data, tag);
+
+ if (m != NULL) {
+ if (mrp_dbus_send_msg(t->dbus, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ mrp_dbus_msg_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+ case MRP_MSG_FIELD_##from: \
+ return MRP_DBUS_TYPE_##to##_AS_STRING;
+
+ switch (type) {
+ MAP(STRING, STRING);
+ MAP(BOOL , BOOLEAN);
+ MAP(UINT8 , UINT16);
+ MAP(SINT8 , INT16);
+ MAP(UINT16, UINT16);
+ MAP(SINT16, INT16);
+ MAP(UINT32, UINT32);
+ MAP(SINT32, INT32);
+ MAP(UINT64, UINT64);
+ MAP(SINT64, INT64);
+ MAP(DOUBLE, DOUBLE);
+ MAP(BLOB , BYTE);
+ default:
+ return NULL;
+ }
+}
+
+
+static mrp_dbus_msg_t *msg_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ mrp_msg_t *msg)
+{
+ /*
+ * Notes: There is a type mismatch between our and DBUS types for
+ * 8-bit integers (D-BUS does not have a signed 8-bit type)
+ * and boolean types (D-BUS has uint32_t booleans, C99 fails
+ * to specify the type and gcc uses a signed char). The
+ * QUIRKY versions of the macros take care of these mismatches.
+ */
+
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_STRING(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = (_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_STRING(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = _val; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+ uint16_t base;
+ uint32_t asize, i;
+ const char *sig;
+ int type, len;
+ void *vptr;
+ uint32_t bln;
+ uint16_t u16, blb;
+ int16_t s16;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &msg->nfield))
+ goto fail;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ switch (f->type) {
+ BASIC_STRING(m, STRING, STRING , f->str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, f->bln, bln);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , f->u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , f->s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , f->u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , f->s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , f->u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , f->s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , f->u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , f->s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , f->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ vptr = f->blb;
+ len = (int)f->size[0];
+ sig = get_array_signature(f->type);
+ asize = len;
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ blb = ((uint8_t *)f->blb)[i];
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &blb))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ switch (base) {
+ ARRAY_STRING(m, STRING, STRING , f->astr[i]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, f->abln[i], bln);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , f->au8[i] , u16);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , f->as8[i] , s16);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , f->au16[i]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , f->as16[i]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , f->au32[i]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , f->as32[i]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , f->au64[i]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , f->adbl[i]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_STRING
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_STRING
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(mrp_dbus_msg_t *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ \
+ if (!mrp_msg_append(msg, tag, type, (_var))) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ if (!mrp_msg_append(msg, tag, type, (_mvar))) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(_i, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+#define APPEND_ARRAY(_type, _var) \
+ case MRP_MSG_FIELD_##_type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY | \
+ MRP_MSG_FIELD_##_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ mrp_msg_t *msg;
+ mrp_msg_value_t v;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ uint16_t nfield, tag, type, base, i;
+ uint32_t n, j;
+ int asize;
+ const char *sender, *sig;
+
+ msg = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ switch (type) {
+ BASIC_SIMPLE(m, STRING, STRING , v.str);
+ BASIC_QUIRKY(m, BOOL , BOOLEAN, v.bln, u32);
+ BASIC_QUIRKY(m, UINT8 , UINT16 , v.u8 , u16);
+ BASIC_QUIRKY(m, SINT8 , INT16 , v.s8 , s16);
+ BASIC_SIMPLE(m, UINT16, UINT16 , v.u16);
+ BASIC_SIMPLE(m, SINT16, INT16 , v.s16);
+ BASIC_SIMPLE(m, UINT32, UINT32 , v.u32);
+ BASIC_SIMPLE(m, SINT32, INT32 , v.s32);
+ BASIC_SIMPLE(m, UINT64, UINT64 , v.u64);
+ BASIC_SIMPLE(m, SINT64, INT64 , v.s64);
+ BASIC_SIMPLE(m, DOUBLE, DOUBLE , v.dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ {
+ uint8_t blb[n];
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ asize = n;
+ if (!mrp_msg_append(msg, tag, type, asize, blb))
+ goto fail;
+ }
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = get_array_signature(base);
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ char *astr[n];
+ uint32_t dbln[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(m, STRING, STRING , astr[j]);
+ ARRAY_QUIRKY(m, BOOL , BOOLEAN, abln[j], dbln[j]);
+ ARRAY_QUIRKY(m, UINT8 , UINT16 , au8[j] , au16[j]);
+ ARRAY_QUIRKY(m, SINT8 , INT16 , as8[j] , as16[j]);
+ ARRAY_SIMPLE(m, UINT16, UINT16 , au16[j]);
+ ARRAY_SIMPLE(m, SINT16, INT16 , as16[j]);
+ ARRAY_SIMPLE(m, UINT32, UINT32 , au32[j]);
+ ARRAY_SIMPLE(m, SINT32, INT32 , as32[j]);
+ ARRAY_SIMPLE(m, UINT64, UINT64 , au64[j]);
+ ARRAY_SIMPLE(m, SINT64, INT64 , as64[j]);
+ ARRAY_SIMPLE(m, DOUBLE, DOUBLE , adbl[j]);
+ default:
+ goto fail;
+ }
+ }
+
+ switch (base) {
+ APPEND_ARRAY(STRING, astr);
+ APPEND_ARRAY(BOOL , abln);
+ APPEND_ARRAY(UINT8 , au8 );
+ APPEND_ARRAY(SINT8 , as8 );
+ APPEND_ARRAY(UINT16, au16);
+ APPEND_ARRAY(SINT16, as16);
+ APPEND_ARRAY(UINT32, au32);
+ APPEND_ARRAY(SINT32, as32);
+ APPEND_ARRAY(UINT64, au64);
+ APPEND_ARRAY(SINT64, as64);
+ APPEND_ARRAY(DOUBLE, adbl);
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ errno = EBADMSG;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static mrp_dbus_msg_t *data_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_STRING(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = _val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_STRING(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ vptr = _val; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = MRP_DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!mrp_dbus_msg_append_basic(m, type, vptr)) \
+ goto fail; \
+ break
+
+ mrp_dbus_msg_t *m;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type, base;
+ mrp_msg_value_t *v;
+ void *vptr;
+ uint32_t n, j;
+ int i, blblen;
+ const char *sig;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t bln, asize;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->tag) ||
+ !mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ BASIC_STRING(STRING, STRING , v->str);
+ BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln);
+ BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+ BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16);
+ BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+ BASIC_SIMPLE(SINT16, INT16 , v->s16);
+ BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+ BASIC_SIMPLE(SINT32, INT32 , v->s32);
+ BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+ BASIC_SIMPLE(SINT64, INT64 , v->s64);
+ BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ sig = get_array_signature(f->type);
+ blblen = mrp_data_get_blob_size(data, descr, i);
+ asize = blblen;
+
+ if (blblen == -1)
+ goto fail;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < blblen; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE,
+ f->blb + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ n = mrp_data_get_array_size(data, descr, i);
+ sig = get_array_signature(base);
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_STRING(STRING, STRING , v->astr[j]);
+ ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln);
+ ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+ ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16);
+ ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+ ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]);
+ ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+ ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]);
+ ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+ ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+static void *data_decode(mrp_dbus_msg_t *m, uint16_t *tagp,
+ const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_var))) \
+ goto fail; \
+ break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_##_dtype, \
+ &(_dvar))) \
+ goto fail; \
+ \
+ _mvar = _dvar; \
+ break
+
+ void *data;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t tag, type, base;
+ mrp_msg_value_t *v;
+ uint32_t n, j, size;
+ int i;
+ const char *sender, *sig;
+ uint32_t u32;
+ uint16_t u16;
+ int16_t s16;
+
+ tag = 0;
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ *tagp = tag;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ if (nfield != descr->nfield)
+ goto fail;
+
+ fields = descr->fields;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT16, &type))
+ goto fail;
+
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (type) {
+ HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+ HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32);
+ HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+ HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16);
+ HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+ HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16);
+ HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+ HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32);
+ HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+ HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64);
+ HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &size))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t blb[size];
+
+ for (j = 0; j < size; j++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE,
+ blb + j))
+ goto fail;
+
+ v->blb = mrp_alloc(size);
+
+ if (v->blb == NULL && size != 0)
+ goto fail;
+
+ memcpy(v->blb, blb, size);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, NULL))
+ goto fail;
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ uint32_t dbln[n];
+ uint16_t au16[n];
+ int16_t as16[n];
+
+ switch (base) {
+ HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+ HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]);
+ HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+ HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]);
+ HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+ HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]);
+ HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+ HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]);
+ HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+ HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]);
+ HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+ }
+
+ if (base == MRP_MSG_FIELD_STRING) {
+ v->astr[j] = mrp_strdup(v->astr[j]);
+ if (v->astr[j] == NULL)
+ goto fail;
+ }
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+ }
+
+ if (f->type == MRP_MSG_FIELD_STRING) {
+ v->str = mrp_strdup(v->str);
+ if (v->str == NULL)
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ mrp_data_free(data, tag);
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+static mrp_dbus_msg_t *raw_encode(mrp_dbus_t *dbus, const char *destination,
+ const char *path, const char *interface,
+ const char *member, const char *sender_id,
+ void *data, size_t size)
+{
+ mrp_dbus_msg_t *m;
+ const char *sig;
+ uint32_t i, n;
+
+ m = mrp_dbus_msg_method_call(dbus, destination, path, interface, member);
+
+ if (m == NULL)
+ return NULL;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_OBJECT_PATH,
+ (void *)sender_id))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+ n = size;
+
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!mrp_dbus_msg_open_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_append_basic(m, MRP_DBUS_TYPE_BYTE, data + i))
+ goto fail;
+
+ if (!mrp_dbus_msg_close_container(m))
+ goto fail;
+
+ return m;
+
+ fail:
+ mrp_dbus_msg_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+}
+
+
+static void *raw_decode(mrp_dbus_msg_t *m, size_t *sizep,
+ const char **sender_id)
+{
+ const char *sender, *sig;
+ void *data;
+ uint32_t n, i;
+
+ data = NULL;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_OBJECT_PATH, &sender))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ sig = MRP_DBUS_TYPE_BYTE_AS_STRING;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sig))
+ goto fail;
+
+ {
+ uint8_t databuf[n];
+
+ for (i = 0; i < n; i++)
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_BYTE, databuf + i))
+ goto fail;
+
+ data = mrp_alloc(n);
+
+ if (data == NULL && n != 0)
+ goto fail;
+
+ memcpy(data, databuf, n);
+ }
+
+ if (!mrp_dbus_msg_exit_container(m))
+ goto fail;
+
+ if (sizep != NULL)
+ *sizep = (size_t)n;
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+ dbus_open, dbus_createfrom, dbus_close, NULL,
+ dbus_bind, NULL, NULL,
+ dbus_connect, dbus_disconnect,
+ dbus_sendmsg, dbus_sendmsgto,
+ dbus_sendraw, dbus_sendrawto,
+ dbus_senddata, dbus_senddatato,
+ NULL, NULL,
+ NULL, NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-sdbus.h>
+
+#define BUS_SERVICE "org.freedesktop.DBus"
+#define BUS_PATH "/org/freedesktop/DBus"
+#define BUS_INTERFACE "org.freedesktop.DBus"
+#define BUS_NAME_CHANGED "NameOwnerChanged"
+#define BUS_GET_OWNER "GetNameOwner"
+
+/* XXX check these... */
+#define SDBUS_ERROR_FAILED "org.DBus.error.failed"
+
+/* D-Bus name request flags and reply statuses. */
+#define SDBUS_NAME_REQUEST_REPLACE 0x2
+#define SDBUS_NAME_REQUEST_DONTQ 0x4
+
+#define SDBUS_NAME_STATUS_OWNER 0x1
+#define SDBUS_NAME_STATUS_QUEUING 0x2
+#define SDBUS_NAME_STATUS_EXISTS 0x3
+#define SDBUS_NAME_STATUS_GOTIT 0x4
+
+#define SDBUS_NAME_STATUS_RELEASED 0x1
+#define SDBUS_NAME_STATUS_UNKNOWN 0x2
+#define SDBUS_NAME_STATUS_FOREIGN 0x3
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+#define MSEC_TO_USEC(msec) ((uint64_t)(msec) * 1000)
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ sd_bus *bus; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_subloop_t *sl; /* subloop for pumping the bus */
+ mrp_htbl_t *objects; /* object path (refcount) table */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+struct mrp_dbus_msg_s {
+ sd_bus_message *msg; /* actual D-Bus message */
+ mrp_refcnt_t refcnt; /* reference count */
+ mrp_list_hook_t arrays; /* implicitly freed related arrays */
+};
+
+
+typedef struct {
+ mrp_dbus_type_t type;
+ mrp_list_hook_t hook;
+ void *items;
+ size_t nitem;
+} msg_array_t;
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+typedef struct {
+ char *path; /* object path */
+ int cnt; /* reference count */
+} object_t;
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ uint64_t serial; /* DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+ sd_bus_message *msg; /* original message */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static int dispatch_signal(sd_bus *b, int r, sd_bus_message *msg, void *data);
+static int dispatch_method(sd_bus *b, int r, sd_bus_message *msg, void *data);
+
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ void *data);
+static void call_free(call_t *call);
+static void object_free_cb(void *key, void *entry);
+
+
+static int purge_objects(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ object_t *o = (object_t *)entry;
+
+ MRP_UNUSED(key);
+
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->bus, FALSE);
+
+ if (dbus->objects) {
+ mrp_htbl_foreach(dbus->signals, purge_objects, dbus);
+ mrp_htbl_destroy(dbus->objects, TRUE);
+ }
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ if (dbus->bus != NULL) {
+ if (dbus->signal_filter)
+ sd_bus_remove_filter(dbus->bus, dispatch_signal, dbus);
+ if (dbus->register_fallback)
+ sd_bus_remove_fallback(dbus->bus, "/", dispatch_method, dbus);
+ if (dbus->priv)
+ sd_bus_close(dbus->bus);
+ else
+ sd_bus_unref(dbus->bus);
+ }
+
+ mrp_free(dbus->address);
+ dbus->bus = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp)
+{
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system")) {
+ if (sd_bus_open_system(&dbus->bus) != 0)
+ goto fail;
+ }
+ else if (!strcmp(address, "session")) {
+ if (sd_bus_open_user(&dbus->bus) != 0)
+ goto fail;
+ }
+ else {
+ dbus->priv = TRUE;
+
+ if (sd_bus_new(&dbus->bus) != 0)
+ goto fail;
+ else {
+ if (sd_bus_set_address(dbus->bus, address) != 0)
+ goto fail;
+
+ if (sd_bus_start(dbus->bus) != 0)
+ goto fail;
+ }
+ }
+
+ dbus->address = mrp_strdup(address);
+ if (sd_bus_get_unique_name(dbus->bus, &dbus->unique_name) != 0)
+ goto fail;
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_with_mainloop(ml, dbus->bus))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (sd_bus_add_filter(dbus->bus, dispatch_signal, dbus) != 0) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (sd_bus_add_fallback(dbus->bus, "/", dispatch_method, dbus) != 0) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = object_free_cb;
+
+ if ((dbus->objects = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS object path table.");
+ goto fail;
+ }
+
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name_owner_change_cb,
+ NULL)) {
+ mrp_dbus_error_set(errp, SDBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, BUS_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->bus, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int flags = SDBUS_NAME_REQUEST_REPLACE | SDBUS_NAME_REQUEST_DONTQ;
+ int status;
+
+ mrp_dbus_error_init(error);
+
+ status = sd_bus_request_name(dbus->bus, name, flags);
+
+ if (status == SDBUS_NAME_STATUS_OWNER || status == SDBUS_NAME_STATUS_GOTIT)
+ return TRUE;
+ else {
+ mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to request name");
+
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error)
+{
+ int status;
+
+ mrp_dbus_error_init(error);
+
+ status = sd_bus_release_name(dbus->bus, name);
+
+ if (status == SDBUS_NAME_STATUS_RELEASED)
+ return TRUE;
+ else {
+ mrp_dbus_error_set(error, SDBUS_ERROR_FAILED, "failed to release name");
+
+ return FALSE;
+ }
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = !mrp_dbus_msg_is_error(m);
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &owner))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ MRP_UNUSED(data);
+
+ if (mrp_dbus_msg_type(m) != MRP_DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &name) ||
+ !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &prev) ||
+ !mrp_dbus_msg_read_basic(m, MRP_DBUS_TYPE_STRING, &next))
+ return FALSE;
+
+#if 0
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1. administering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2. checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3. removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+#endif
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name,
+ NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_GET_OWNER, 5000,
+ name_owner_query_cb, t,
+ MRP_DBUS_TYPE_STRING, t->name,
+ MRP_DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, name,
+ NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus,
+ BUS_SERVICE, BUS_PATH, BUS_INTERFACE,
+ BUS_NAME_CHANGED, t->name,
+ NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+static void object_free_cb(void *key, void *entry)
+{
+ object_t *o = (object_t *)entry;
+
+ MRP_UNUSED(key);
+
+ mrp_free(o->path);
+ mrp_free(o);
+}
+
+
+static object_t *object_add(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ o = mrp_alloc(sizeof(*o));
+
+ if (o != NULL) {
+ o->path = mrp_strdup(path);
+ o->cnt = 1;
+
+ if (o->path == NULL) {
+ mrp_free(o);
+ return NULL;
+ }
+
+ if (sd_bus_add_object(dbus->bus, o->path, dispatch_method, dbus) == 0) {
+ if (mrp_htbl_insert(dbus->objects, o->path, o))
+ return o;
+ else
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+ }
+
+ mrp_free(o->path);
+ mrp_free(o);
+ }
+
+ return NULL;
+}
+
+
+static object_t *object_lookup(mrp_dbus_t *dbus, const char *path)
+{
+ return mrp_htbl_lookup(dbus->objects, (void *)path);
+}
+
+
+static int object_ref(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ if ((o = object_lookup(dbus, path)) != NULL) {
+ o->cnt++;
+ return TRUE;
+ }
+
+ if (object_add(dbus, path) != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static void object_unref(mrp_dbus_t *dbus, const char *path)
+{
+ object_t *o;
+
+ if ((o = object_lookup(dbus, path)) != NULL) {
+ o->cnt--;
+
+ if (o->cnt <= 0) {
+ mrp_htbl_remove(dbus->objects, (void *)path, FALSE);
+ sd_bus_remove_object(dbus->bus, o->path, dispatch_method, dbus);
+
+ mrp_free(o->path);
+ mrp_free(o);
+ }
+ }
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if (!object_ref(dbus, path))
+ return FALSE;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ goto fail;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+
+ return TRUE;
+ }
+
+ fail:
+ object_unref(dbus, path);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ object_unref(dbus, path);
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ return FALSE; \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ if (sd_bus_add_match(dbus->bus, filter, NULL, NULL) != 0) {
+ mrp_log_error("Failed to install filter '%s'.", filter);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal");
+ ADD_TAG("sender" , sender);
+ ADD_TAG("path" , path);
+ ADD_TAG("interface", interface);
+ ADD_TAG("member" , member);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val);
+ i++;
+ }
+ va_end(ap);
+
+ sd_bus_remove_match(dbus->bus, filter, NULL, NULL);
+
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+static int element_size(mrp_dbus_type_t type)
+{
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE: return sizeof(char);
+ case MRP_DBUS_TYPE_BOOLEAN: return sizeof(uint32_t);
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16: return sizeof(uint16_t);
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32: return sizeof(uint32_t);
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64: return sizeof(uint64_t);
+ case MRP_DBUS_TYPE_DOUBLE: return sizeof(double);
+ case MRP_DBUS_TYPE_STRING: return sizeof(char *);
+ case MRP_DBUS_TYPE_OBJECT_PATH: return sizeof(char *);
+ case MRP_DBUS_TYPE_SIGNATURE: return sizeof(char *);
+ default:
+ return FALSE;
+ }
+
+}
+
+
+static inline mrp_dbus_msg_t *create_message(sd_bus_message *msg, int ref)
+{
+ mrp_dbus_msg_t *m;
+
+ if (msg != NULL) {
+ if ((m = mrp_allocz(sizeof(*m))) != NULL) {
+ mrp_refcnt_init(&m->refcnt);
+ mrp_list_init(&m->arrays);
+ if (ref)
+ m->msg = sd_bus_message_ref(msg);
+ else
+ m->msg = msg;
+ }
+
+ return m;
+ }
+ else
+ return NULL;
+}
+
+
+static void free_msg_array(msg_array_t *a)
+{
+ void *ptr;
+ size_t esize, i;
+ int string;
+
+ if (a == NULL)
+ return;
+
+ mrp_list_delete(&a->hook);
+
+ if ((esize = element_size(a->type)) != 0) {
+ if (a->type == MRP_DBUS_TYPE_STRING ||
+ a->type == MRP_DBUS_TYPE_OBJECT_PATH ||
+ a->type == MRP_DBUS_TYPE_SIGNATURE)
+ string = TRUE;
+ else
+ string = FALSE;
+
+ if (string)
+ for (i = 0, ptr = a->items; i < a->nitem; i++, ptr += esize)
+ mrp_free(ptr);
+
+ mrp_free(a->items);
+ }
+ else
+ mrp_log_error("Hmm... looks like we have a corrupted implicit array.");
+
+ mrp_free(a);
+}
+
+
+static void free_message(mrp_dbus_msg_t *m)
+{
+ mrp_list_hook_t *p, *n;
+ msg_array_t *a;
+
+ mrp_list_foreach(&m->arrays, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_msg_array(a);
+ }
+
+ mrp_free(m);
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m)
+{
+ return mrp_ref_obj(m, refcnt);
+}
+
+
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m)
+{
+ if (mrp_unref_obj(m, refcnt)) {
+ sd_bus_message_unref(m->msg);
+ free_message(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static inline int verify_type(sd_bus_message *msg, int expected_type)
+{
+ uint8_t type;
+
+ if (sd_bus_message_get_type(msg, &type) != 0 || type == expected_type)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static int dispatch_method(sd_bus *bus,int ret, sd_bus_message *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ const char *path = sd_bus_message_get_path(msg);
+ const char *interface = sd_bus_message_get_interface(msg);
+ const char *member = sd_bus_message_get_member(msg);
+ int r = FALSE;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(bus);
+ MRP_UNUSED(ret);
+
+ if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) || !member)
+ return r;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ sd_bus_message_rewind(msg, TRUE);
+
+ if (m == NULL)
+ m = create_message(msg, TRUE);
+
+ if (h->handler(dbus, m, h->user_data))
+ r = TRUE;
+
+ goto out;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ out:
+ if (!r)
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ mrp_dbus_msg_unref(m);
+
+ return r;
+}
+
+
+static int dispatch_signal(sd_bus *bus,int ret, sd_bus_message *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_dbus_msg_t *m = NULL;
+ const char *path = sd_bus_message_get_path(msg);
+ const char *interface = sd_bus_message_get_interface(msg);
+ const char *member = sd_bus_message_get_member(msg);
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(bus);
+ MRP_UNUSED(ret);
+
+ if (!verify_type(msg, MRP_DBUS_MESSAGE_TYPE_SIGNAL) || !member)
+ return FALSE;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__, SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ sd_bus_message_rewind(msg, TRUE);
+
+ if (m == NULL)
+ m = create_message(msg, TRUE);
+
+ h->handler(dbus, m, h->user_data);
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ mrp_dbus_msg_unref(m);
+
+ return FALSE;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static int append_args_strtype(mrp_dbus_msg_t *msg, const char *types,
+ va_list ap)
+{
+ MRP_UNUSED(msg);
+ MRP_UNUSED(types);
+ MRP_UNUSED(ap);
+
+ return FALSE;
+}
+
+
+static int append_args_inttype(sd_bus_message *msg, int type, va_list ap)
+{
+ void *vptr;
+ int atype, elen, i;
+ void **aptr;
+ int alen;
+ char stype[2] = { '\0', '\0' };
+ int r = 0;
+
+ (void)append_args_strtype;
+
+ while (type != MRP_DBUS_TYPE_INVALID) {
+ switch (type) {
+ case MRP_DBUS_TYPE_BYTE:
+ case MRP_DBUS_TYPE_BOOLEAN:
+ case MRP_DBUS_TYPE_INT16:
+ case MRP_DBUS_TYPE_UINT16:
+ case MRP_DBUS_TYPE_INT32:
+ case MRP_DBUS_TYPE_UINT32:
+ case MRP_DBUS_TYPE_INT64:
+ case MRP_DBUS_TYPE_UINT64:
+ case MRP_DBUS_TYPE_DOUBLE:
+ case MRP_DBUS_TYPE_STRING:
+ case MRP_DBUS_TYPE_OBJECT_PATH:
+ case MRP_DBUS_TYPE_SIGNATURE:
+ case MRP_DBUS_TYPE_UNIX_FD:
+ vptr = va_arg(ap, void *);
+ r = sd_bus_message_append_basic(msg, type, vptr);
+ break;
+
+ case MRP_DBUS_TYPE_ARRAY:
+ atype = va_arg(ap, int);
+ aptr = va_arg(ap, void **);
+ alen = va_arg(ap, int);
+
+ switch (atype) {
+#define LEN(_type, _size) case MRP_DBUS_TYPE_##_type: elen = _size; break
+ LEN(BYTE , sizeof(uint8_t));
+ LEN(BOOLEAN , sizeof(uint32_t));
+ LEN(INT16 , sizeof(int16_t));
+ LEN(UINT16 , sizeof(uint16_t));
+ LEN(INT32 , sizeof(int32_t));
+ LEN(UINT32 , sizeof(uint32_t));
+ LEN(INT64 , sizeof(int64_t));
+ LEN(UINT64 , sizeof(uint64_t));
+ LEN(DOUBLE , sizeof(double));
+ LEN(STRING , sizeof(const char *));
+ LEN(OBJECT_PATH, sizeof(const char *));
+ LEN(SIGNATURE , sizeof(const char *));
+ LEN(UNIX_FD , sizeof(int));
+#undef LEN
+ default:
+ return FALSE;
+ }
+
+ stype[0] = atype;
+ if (sd_bus_message_open_container(msg, type, stype) != 0)
+ return FALSE;
+ for (i = 0; i < alen; i++, aptr += elen)
+ if (sd_bus_message_append_basic(msg, atype, aptr) != 0)
+ return FALSE;
+ if (sd_bus_message_close_container(msg) != 0)
+ return FALSE;
+ else
+ return TRUE;
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ type = va_arg(ap, int);
+ }
+
+ return (r == 0 ? TRUE : FALSE);
+}
+
+
+static int call_reply_cb(sd_bus *bus, int ret, sd_bus_message *msg,
+ void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ mrp_dbus_msg_t *reply = create_message(msg, TRUE);
+ sd_bus_error error;
+
+ MRP_UNUSED(bus);
+
+ call->serial = 0;
+ mrp_list_delete(&call->hook);
+
+ if (ret == 0) {
+ reply = create_message(msg, TRUE);
+ sd_bus_message_rewind(reply->msg, TRUE);
+ }
+ else {
+ sd_bus_message *err = NULL;
+
+ if (ret == ETIMEDOUT)
+ error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_TIMEOUT,
+ "D-Bus call timed out");
+ else
+ error = SD_BUS_ERROR_MAKE(MRP_DBUS_ERROR_FAILED,
+ "D-Bus call failed");
+
+ if (sd_bus_message_new_method_error(bus, call->msg, &error, &err) == 0)
+ reply = create_message(err, FALSE);
+ else
+ reply = NULL;
+ }
+
+ call->cb(call->dbus, reply, call->user_data);
+ call_free(call);
+ mrp_dbus_msg_unref(reply);
+
+ return TRUE;
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ sd_bus_message *msg;
+ int success;
+
+ call = NULL;
+
+ if (sd_bus_message_new_method_call(dbus->bus, dest, path, interface, member,
+ &msg) != 0)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == MRP_DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ sd_bus_message_set_no_reply(msg, TRUE);
+ if (sd_bus_send(dbus->bus, msg, NULL) != 0)
+ goto fail;
+ sd_bus_message_unref(msg);
+ }
+ else {
+ if (sd_bus_send_with_reply(dbus->bus, msg, call_reply_cb, call,
+ timeout * 1000, &call->serial) != 0)
+ goto fail;
+
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->msg = msg;
+ }
+
+ return id;
+
+ fail:
+ sd_bus_message_unref(msg);
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *m)
+{
+ /*bus_message_dump(m->msg);*/
+
+ if (sd_bus_send(dbus->bus, m->msg, NULL) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ sd_bus_send_with_reply_cancel(dbus->bus, call->serial);
+ call->serial = 0;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *m, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *rpl;
+ int success;
+
+ if (sd_bus_message_new_method_return(dbus->bus, m->msg, &rpl) != 0)
+ return FALSE;
+
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, rpl, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *rpl;
+ int success;
+ sd_bus_error err = SD_BUS_ERROR_NULL;;
+
+ sd_bus_error_set_const(&err, errname, errmsg);
+
+ if (sd_bus_message_new_method_error(dbus->bus, m->msg, &err, &rpl) != 0)
+ return FALSE;
+
+ va_start(ap, type);
+ success = append_args_inttype(rpl, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, rpl, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL) {
+ sd_bus_message_unref(call->msg);
+ mrp_free(call);
+ }
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->serial != 0)
+ sd_bus_send_with_reply_cancel(dbus->bus, call->serial);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ sd_bus_message *msg;
+ int success;
+
+ if (sd_bus_message_new_signal(dbus->bus, path, interface, member,
+ &msg) != 0)
+ return 0;
+
+ va_start(ap, type);
+ success = append_args_inttype(msg, type, ap);
+ va_end(ap);
+
+ if (!success)
+ goto fail;
+
+ if (dest != NULL)
+ if (sd_bus_message_set_destination(msg, dest) != 0)
+ goto fail;
+
+ if (sd_bus_send(dbus->bus, msg, NULL) != 0)
+ goto fail;
+
+ sd_bus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ sd_bus_message_unref(msg);
+
+ return 0;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *dbus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ sd_bus_message *msg;
+
+ if (sd_bus_message_new_method_call(dbus->bus, destination,
+ path, interface, member, &msg) == 0)
+ return create_message(msg, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *dbus,
+ mrp_dbus_msg_t *msg)
+{
+ sd_bus_message *req, *rpl;
+
+ req = (sd_bus_message *)msg;
+
+ if (sd_bus_message_new_method_return(dbus->bus, req, &rpl) == 0)
+ return create_message(rpl, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *m,
+ mrp_dbus_err_t *err)
+{
+ sd_bus_message *req, *rpl;
+
+ req = m->msg;
+
+ if (sd_bus_message_new_method_error(dbus->bus, req, err, &rpl) == 0)
+ return create_message(rpl, FALSE);
+ else
+ return NULL;
+}
+
+
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *dbus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member)
+{
+ sd_bus_message *msg = NULL;
+
+ if (sd_bus_message_new_signal(dbus->bus, path, interface, member,
+ &msg) == 0) {
+ if (destination != NULL) {
+ if (sd_bus_message_set_destination(msg, destination) != 0) {
+ sd_bus_message_unref(msg);
+ msg = NULL;
+ }
+ }
+ }
+
+ return create_message(msg, FALSE);
+}
+
+
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *m)
+{
+ uint8_t type;
+
+ if (sd_bus_message_get_type(m->msg, &type) == 0)
+ return (mrp_dbus_msg_type_t)type;
+ else
+ return MRP_DBUS_MESSAGE_TYPE_INVALID;
+}
+
+#define WRAP_GETTER(type, what) \
+ type mrp_dbus_msg_##what(mrp_dbus_msg_t *m) \
+ { \
+ return sd_bus_message_get_##what((sd_bus_message *)m->msg); \
+ } \
+ struct __mrp_dbus_allow_trailing_semicolon
+
+WRAP_GETTER(const char *, path);
+WRAP_GETTER(const char *, interface);
+WRAP_GETTER(const char *, member);
+WRAP_GETTER(const char *, destination);
+WRAP_GETTER(const char *, sender);
+
+#undef WRAP_GETTER
+
+
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ return sd_bus_message_open_container(m->msg, type, contents) == 0;
+}
+
+
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m)
+{
+ return sd_bus_message_close_container(m->msg) == 0;
+}
+
+
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ return sd_bus_message_append_basic(m->msg, type, valuep) == 0;
+}
+
+
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *m, char type,
+ const char *contents)
+{
+ return sd_bus_message_enter_container(m->msg, type, contents) == 1;
+}
+
+
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m)
+{
+ return sd_bus_message_exit_container(m->msg) == 1;
+}
+
+
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep)
+{
+ return sd_bus_message_read_basic(m->msg, type, valuep) == 1;
+}
+
+
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp)
+{
+ char sub[2] = { (char)type, '\0' };
+ msg_array_t *a;
+ int offs;
+ size_t esize;
+
+ if ((esize = element_size(type)) == 0)
+ return FALSE;
+
+ if (!mrp_dbus_msg_enter_container(m, MRP_DBUS_TYPE_ARRAY, sub))
+ return FALSE;
+
+ if ((a = mrp_allocz(sizeof(*a))) == NULL)
+ goto fail;
+
+ a->type = type;
+ mrp_list_init(&a->hook);
+
+ offs = 0;
+ while (mrp_dbus_msg_arg_type(m, NULL) != MRP_DBUS_TYPE_INVALID) {
+ if (!mrp_realloc(a->items, offs + esize))
+ goto fail;
+
+ if (!mrp_dbus_msg_read_basic(m, type, a->items + offs))
+ goto fail;
+ else
+ a->nitem++;
+
+ offs += esize;
+ }
+
+ mrp_dbus_msg_exit_container(m);
+
+ mrp_list_append(&m->arrays, &a->hook);
+ *itemsp = a->items;
+ *nitemp = a->nitem;
+
+ return TRUE;
+
+ fail:
+ mrp_dbus_msg_exit_container(m);
+ free_msg_array(a);
+
+ return FALSE;
+}
+
+
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents)
+{
+ char type;
+
+ if (sd_bus_message_peek_type(m->msg, &type, contents) >= 0)
+ return (mrp_dbus_type_t)type;
+ else
+ return MRP_DBUS_TYPE_INVALID;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SD_BUS_H__
+#define __MURPHY_SD_BUS_H__
+
+#include <systemd/sd-bus.h>
+#include <systemd/sd-bus-protocol.h>
+
+#include <murphy/common/mainloop.h>
+#include <murphy/common/dbus-error.h>
+
+/** Type for a D-Bus (connection). */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** Type for a D-Bus message. */
+typedef struct mrp_dbus_msg_s mrp_dbus_msg_t;
+
+/** Type for a D-Bus error. */
+typedef sd_bus_error mrp_dbus_err_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, mrp_dbus_msg_t *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ mrp_dbus_err_t *errp);
+#define mrp_dbus_get mrp_dbus_connect
+
+/** Set up an sd-bus instance with a mainloop. */
+int mrp_dbus_setup_sd_bus(mrp_mainloop_t *ml, sd_bus *bus);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_err_t *error);
+
+/** Type for a name tracking callback. */
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int,
+ const char *, void *);
+/** Start tracking the given name. */
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+/** Stop tracking the given name. */
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+
+/** Export a method to the bus. */
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Remove an exported method. */
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+/** Install a filter and add a handler for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove the signal handler and filter for the given signal on the bus. */
+MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Install a filter for the given message on the bus. */
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Remove a filter for the given message on the bus. */
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+/** Remove a filter for the given message on the bus. */
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+/** Add a signal handler for the gvien signal on the bus. */
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Remove the given signal handler for the given signal on the bus. */
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+/** Type of a method call reply callback. */
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
+ void *user_data);
+
+/** Call the given method on the bus. */
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest,
+ const char *path, const char *interface,
+ const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ int dbus_type, ...);
+
+/** Cancel an ongoing method call on the bus. */
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+/** Send a reply to the given method call on the bus. */
+int mrp_dbus_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, int type, ...);
+
+/** Send an error reply to the given method call on the bus. */
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
+ const char *errname, const char *errmsg,
+ int type, ...);
+
+/** Emit the given signal on the bus. */
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...);
+
+/** Send the given message on the bus. */
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg);
+
+/** Get our unique name on the bus. */
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+/** Initialize the given error. */
+static inline mrp_dbus_err_t *mrp_dbus_error_init(mrp_dbus_err_t *err)
+{
+ if (err != NULL)
+ memset(err, 0, sizeof(*err));
+
+ return err;
+}
+
+
+/** Set the given error buffer up with the error name and message. */
+static inline mrp_dbus_err_t *mrp_dbus_error_set(mrp_dbus_err_t *err,
+ const char *name,
+ const char *message)
+{
+ sd_bus_error_set(err, name, "%s", message);
+
+ return err;
+}
+
+
+/** Get the error message from the given bus error message. */
+static inline const char *mrp_dbus_errmsg(mrp_dbus_err_t *err)
+{
+ if (err && sd_bus_error_is_set(err))
+ return err->message;
+ else
+ return "unknown DBUS error";
+}
+
+
+/** Increase the reference count of a message. */
+mrp_dbus_msg_t *mrp_dbus_msg_ref(mrp_dbus_msg_t *m);
+
+/** Decrease the reference count of a message, freeing it if necessary. */
+int mrp_dbus_msg_unref(mrp_dbus_msg_t *m);
+
+
+/** Create a new method call message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_call(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Create a new method return message. */
+mrp_dbus_msg_t *mrp_dbus_msg_method_return(mrp_dbus_t *bus,
+ mrp_dbus_msg_t *msg);
+
+/** Create a new error reply message. */
+mrp_dbus_msg_t *mrp_dbus_msg_error(mrp_dbus_t *bus, mrp_dbus_msg_t *msg,
+ mrp_dbus_err_t *err);
+
+/** Create a new signal message. */
+mrp_dbus_msg_t *mrp_dbus_msg_signal(mrp_dbus_t *bus,
+ const char *destination,
+ const char *path,
+ const char *interface,
+ const char *member);
+
+/** Bus message types. */
+typedef enum {
+#ifndef SD_BUS_MESSAGE_TYPE_INVALID
+# define SD_BUS_MESSAGE_TYPE_INVALID _SD_BUS_MESSAGE_TYPE_INVALID
+#endif
+# define MAP(t, f) MRP_DBUS_MESSAGE_TYPE_##t = SD_BUS_MESSAGE_TYPE_##f
+ MAP(INVALID , INVALID),
+ MAP(METHOD_CALL , METHOD_CALL),
+ MAP(METHOD_RETURN, METHOD_RETURN),
+ MAP(ERROR , METHOD_ERROR),
+ MAP(SIGNAL , SIGNAL)
+# undef MAP
+} mrp_dbus_msg_type_t;
+
+/** Get the type of the given message. */
+mrp_dbus_msg_type_t mrp_dbus_msg_type(mrp_dbus_msg_t *msg);
+
+/** Message type checking convenience functions. */
+#define TYPE_CHECK_FUNCTION(type, TYPE) \
+ static inline int mrp_dbus_msg_is_##type(mrp_dbus_msg_t *msg) \
+ { \
+ return mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_##TYPE; \
+ } \
+ struct __mrp_dbus_allow_traling_semicolon
+
+TYPE_CHECK_FUNCTION(method_call , METHOD_CALL);
+TYPE_CHECK_FUNCTION(method_return, METHOD_RETURN);
+TYPE_CHECK_FUNCTION(error , ERROR);
+TYPE_CHECK_FUNCTION(signal , SIGNAL);
+
+/** Message argument types. */
+typedef enum {
+#ifndef SD_BUS_TYPE_INVALID
+# define SD_BUS_TYPE_INVALID _SD_BUS_TYPE_INVALID
+#endif
+#define TYPE(t) MRP_DBUS_TYPE_##t = SD_BUS_TYPE_##t
+ TYPE(INVALID),
+ TYPE(BYTE),
+ TYPE(BOOLEAN),
+ TYPE(INT16),
+ TYPE(UINT16),
+ TYPE(INT32),
+ TYPE(UINT32),
+ TYPE(INT64),
+ TYPE(UINT64),
+ TYPE(DOUBLE),
+ TYPE(STRING),
+ TYPE(OBJECT_PATH),
+ TYPE(SIGNATURE),
+ TYPE(UNIX_FD),
+ TYPE(ARRAY),
+ TYPE(VARIANT),
+ TYPE(STRUCT),
+ TYPE(DICT_ENTRY),
+ TYPE(STRUCT_BEGIN),
+ TYPE(STRUCT_END),
+ TYPE(DICT_ENTRY_BEGIN),
+ TYPE(DICT_ENTRY_END)
+#undef TYPE
+} mrp_dbus_type_t;
+
+/** Message argument types as strings. */
+static const char _type_as_string[][2] = {
+#define MAP(_type) [SD_BUS_TYPE_##_type] = { SD_BUS_TYPE_##_type, '\0' }
+ MAP(BYTE),
+ MAP(BOOLEAN),
+ MAP(INT16),
+ MAP(UINT16),
+ MAP(INT32),
+ MAP(UINT32),
+ MAP(INT64),
+ MAP(UINT64),
+ MAP(DOUBLE),
+ MAP(STRING),
+ MAP(OBJECT_PATH),
+ MAP(SIGNATURE),
+ MAP(UNIX_FD),
+ MAP(ARRAY),
+ MAP(VARIANT),
+ MAP(STRUCT),
+ MAP(DICT_ENTRY),
+ MAP(STRUCT_BEGIN),
+ MAP(STRUCT_END),
+ MAP(DICT_ENTRY_BEGIN),
+ MAP(DICT_ENTRY_END)
+#undef MAP
+};
+
+#define _STRTYPE(_type) _type_as_string[SD_BUS_TYPE_##_type]
+#define _EVAL(_type) _type
+#define MRP_DBUS_TYPE_BYTE_AS_STRING _EVAL(_STRTYPE(BYTE))
+#define MRP_DBUS_TYPE_BOOLEAN_AS_STRING _EVAL(_STRTYPE(BOOLEAN))
+#define MRP_DBUS_TYPE_INT16_AS_STRING _EVAL(_STRTYPE(INT16))
+#define MRP_DBUS_TYPE_UINT16_AS_STRING _EVAL(_STRTYPE(UINT16))
+#define MRP_DBUS_TYPE_INT32_AS_STRING _EVAL(_STRTYPE(INT32))
+#define MRP_DBUS_TYPE_UINT32_AS_STRING _EVAL(_STRTYPE(UINT32))
+#define MRP_DBUS_TYPE_INT64_AS_STRING _EVAL(_STRTYPE(INT64))
+#define MRP_DBUS_TYPE_UINT64_AS_STRING _EVAL(_STRTYPE(UINT64))
+#define MRP_DBUS_TYPE_DOUBLE_AS_STRING _EVAL(_STRTYPE(DOUBLE))
+#define MRP_DBUS_TYPE_STRING_AS_STRING _EVAL(_STRTYPE(STRING))
+#define MRP_DBUS_TYPE_OBJECT_PATH_AS_STRING _EVAL(_STRTYPE(OBJECT_PATH))
+#define MRP_DBUS_TYPE_SIGNATURE_AS_STRING _EVAL(_STRTYPE(SIGNATURE))
+#define MRP_DBUS_TYPE_UNIX_FD_AS_STRING _EVAL(_STRTYPE(UNIX_FD))
+#define MRP_DBUS_TYPE_ARRAY_AS_STRING _EVAL(_STRTYPE(ARRAY))
+#define MRP_DBUS_TYPE_VARIANT_AS_STRING _EVAL(_STRTYPE(VARIANT))
+#define MRP_DBUS_TYPE_STRUCT_AS_STRING _EVAL(_STRTYPE(STRUCT))
+#define MRP_DBUS_TYPE_DICT_ENTRY_AS_STRING _EVAL(_STRTYPE(DICT_ENTRY))
+
+/** Get the path of the given message. */
+const char *mrp_dbus_msg_path(mrp_dbus_msg_t *msg);
+
+/** Get the interface of the given message. */
+const char *mrp_dbus_msg_interface(mrp_dbus_msg_t *msg);
+
+/** Get the member of the given message. */
+const char *mrp_dbus_msg_member(mrp_dbus_msg_t *msg);
+
+/** Get the destination of the given message. */
+const char *mrp_dbus_msg_destination(mrp_dbus_msg_t *msg);
+
+/** Get the sender of the given message. */
+const char *mrp_dbus_msg_sender(mrp_dbus_msg_t *msg);
+
+/** Open a new container of the given type and cotained types. */
+int mrp_dbus_msg_open_container(mrp_dbus_msg_t *m, char type,
+ const char *contents);
+
+/** Close the current container. */
+int mrp_dbus_msg_close_container(mrp_dbus_msg_t *m);
+
+/** Append an argument of a basic type to the given message. */
+int mrp_dbus_msg_append_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Get the type of the current message argument. */
+mrp_dbus_type_t mrp_dbus_msg_arg_type(mrp_dbus_msg_t *m, const char **contents);
+
+/** Open the current container (of the given type and contents) for reading. */
+int mrp_dbus_msg_enter_container(mrp_dbus_msg_t *msg, char type,
+ const char *contents);
+
+/** Exit from the container being currently read. */
+int mrp_dbus_msg_exit_container(mrp_dbus_msg_t *m);
+
+/** Read the next argument (of basic type) from the given message. */
+int mrp_dbus_msg_read_basic(mrp_dbus_msg_t *m, char type, void *valuep);
+
+/** Read the next array of one of the basic types. */
+int mrp_dbus_msg_read_array(mrp_dbus_msg_t *m, char type,
+ void **itemsp, size_t *nitemp);
+
+/** Set up an sd_bus to be pumped by a murphy mainloop. */
+int mrp_dbus_setup_with_mainloop(mrp_mainloop_t *ml, sd_bus *bus);
+#endif /* __MURPHY_SD_BUS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/libdbus.h>
+#include <murphy/common/dbus-transport.h>
+
+#define DBUS "dbus"
+#define DBUSL 4
+
+#define TRANSPORT_PATH "/murphy/transport"
+#define TRANSPORT_INTERFACE "Murphy.Transport"
+#define TRANSPORT_MESSAGE "DeliverMessage"
+#define TRANSPORT_DATA "DeliverData"
+#define TRANSPORT_RAW "DeliverRaw"
+#define TRANSPORT_METHOD "DeliverMessage"
+
+#define ANY_ADDRESS "any"
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ mrp_dbus_t *dbus; /* D-BUS connection */
+ int bound : 1; /* whether bound to an address */
+ int peer_resolved : 1; /* connected and peer name known */
+ mrp_dbusaddr_t local; /* address we're bound to */
+ mrp_dbusaddr_t remote; /* address we're connected to */
+} dbus_t;
+
+
+static uint32_t nauto; /* for autobinding */
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+static int dbus_data_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+static int dbus_raw_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data);
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+static DBusMessage *msg_encode(const char *sender_id, mrp_msg_t *msg);
+static mrp_msg_t *msg_decode(DBusMessage *m, const char **sender_id);
+
+static DBusMessage *data_encode(const char *sender_id,
+ void *data, uint16_t tag);
+static void *data_decode(DBusMessage *m, uint16_t *tag, const char **sender_id);
+
+static DBusMessage *raw_encode(const char *sender_id, void *data, size_t size);
+static void *raw_decode(DBusMessage *m, size_t *sizep, const char **sender_id);
+
+
+
+static socklen_t parse_address(const char *str, mrp_dbusaddr_t *addr,
+ socklen_t size)
+{
+ const char *p, *e;
+ char *q;
+ size_t l, n;
+
+ if (size < sizeof(*addr)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (strncmp(str, DBUS":", DBUSL + 1))
+ return 0;
+ else
+ str += DBUSL + 1;
+
+ /*
+ * The format of the address is
+ * dbus:[bus-address]@address/path
+ * eg.
+ * dbus:[session]@:1.33/client1, or
+ * dbus:[unix:abstract=/tmp/dbus-Xx2Kpi...a572]@:1.33/client1
+ */
+
+ p = str;
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+
+ /* get bus address */
+ if (*p != '[') {
+ errno = EINVAL;
+ return 0;
+ }
+ else
+ p++;
+
+ e = strchr(p, ']');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save bus address */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_bus = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e + 1;
+
+ /* get (local or remote) address on bus */
+ if (*p != '@')
+ addr->db_addr = ANY_ADDRESS;
+ else {
+ p++;
+ e = strchr(p, '/');
+
+ if (e == NULL) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = e - p;
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ /* save address on bus */
+ strncpy(q, p, n);
+ q[n] = '\0';
+ addr->db_addr = q;
+
+ q += n + 1;
+ l -= n + 1;
+ p = e;
+ }
+
+ /* get object path */
+ if (*p != '/') {
+ errno = EINVAL;
+ return 0;
+ }
+
+ n = snprintf(q, l, "%s%s", TRANSPORT_PATH, p);
+ if (n >= l) {
+ errno = ENAMETOOLONG;
+ return 0;
+ }
+
+ addr->db_path = q;
+ addr->db_family = MRP_AF_DBUS;
+
+ return sizeof(*addr);
+}
+
+
+static mrp_dbusaddr_t *copy_address(mrp_dbusaddr_t *dst, mrp_dbusaddr_t *src)
+{
+ char *p, *q;
+ size_t l, n;
+
+ dst->db_family = src->db_family;
+
+ /* copy bus address */
+ p = src->db_bus;
+ q = dst->db_fqa;
+ l = sizeof(dst->db_fqa);
+
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy address */
+ p = src->db_addr;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_addr = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ /* copy path */
+ p = src->db_path;
+ n = strlen(p);
+ if (l < n + 1) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ dst->db_path = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ return dst;
+}
+
+
+static inline int check_address(mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addr;
+
+ return (a && a->db_family == MRP_AF_DBUS && addrlen == sizeof(*a));
+}
+
+
+static size_t peer_address(mrp_sockaddr_t *addrp, const char *sender,
+ const char *path)
+{
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ const char *p;
+ char *q;
+ int l, n;
+
+ q = addr->db_fqa;
+ l = sizeof(addr->db_fqa);
+ p = ANY_ADDRESS;
+ n = 3;
+
+ addr->db_bus = q;
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_addr = q;
+ p = sender;
+ n = strlen(sender);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+ q += n + 1;
+ l -= n + 1;
+
+ addr->db_path = q;
+ p = path;
+ n = strlen(p);
+
+ if (l < n + 1)
+ return 0;
+
+ memcpy(q, p, n + 1);
+
+ return sizeof(addrp);
+}
+
+
+static socklen_t dbus_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ socklen_t len;
+
+ len = parse_address(str, (mrp_dbusaddr_t *)addr, size);
+
+ if (len > 0) {
+ if (typep != NULL)
+ *typep = DBUS;
+ }
+
+ return len;
+}
+
+
+static int dbus_open(mrp_transport_t *mt)
+{
+ MRP_UNUSED(mt);
+
+ return TRUE;
+}
+
+
+static int dbus_createfrom(mrp_transport_t *mt, void *conn)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = (mrp_dbus_t *)conn;
+
+ t->dbus = mrp_dbus_ref(dbus);
+
+ if (t->dbus != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int dbus_bind(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbus_t *dbus = NULL;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ int (*cb)(mrp_dbus_t *, DBusMessage *, void *);
+ const char *method;
+
+ if (t->bound) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ if (t->dbus == NULL) {
+ dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (dbus == NULL) {
+ errno = ECONNRESET;
+ goto fail;
+ }
+ else {
+ t->dbus = dbus;
+
+ if (addr->db_addr != NULL && strcmp(addr->db_addr, ANY_ADDRESS)) {
+ if (!mrp_dbus_acquire_name(t->dbus, addr->db_addr, NULL)) {
+ errno = EADDRINUSE; /* XXX TODO, should check error... */
+ goto fail;
+ }
+ }
+ }
+ }
+ else {
+ /* XXX TODO: should check given address against address of the bus */
+ }
+
+ copy_address(&t->local, addr);
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ break;
+ default:
+ errno = EPROTOTYPE;
+ goto fail;
+ }
+
+ if (!mrp_dbus_export_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t)) {
+ errno = EIO;
+ goto fail;
+ }
+ else {
+ t->bound = TRUE;
+ return TRUE;
+ }
+
+ fail:
+ if (dbus != NULL) {
+ mrp_dbus_unref(dbus);
+ t->dbus = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int dbus_autobind(mrp_transport_t *mt, mrp_sockaddr_t *addrp)
+{
+ mrp_dbusaddr_t *a = (mrp_dbusaddr_t *)addrp;
+ char astr[MRP_SOCKADDR_SIZE];
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ snprintf(astr, sizeof(astr), "dbus:[%s]/auto/%u", a->db_bus, nauto++);
+
+ alen = dbus_resolve(astr, &addr, sizeof(addr), NULL);
+
+ if (alen > 0)
+ return dbus_bind(mt, &addr, alen);
+ else
+ return FALSE;
+}
+
+
+static void dbus_close(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr;
+ const char *method;
+ int (*cb)(mrp_dbus_t *, DBusMessage *, void *);
+
+ if (t->bound) {
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ method = TRANSPORT_DATA;
+ cb = dbus_data_cb;
+ break;
+ case MRP_TRANSPORT_MODE_RAW:
+ method = TRANSPORT_RAW;
+ cb = dbus_raw_cb;
+ break;
+ default:
+ case MRP_TRANSPORT_MODE_MSG:
+ method = TRANSPORT_MESSAGE;
+ cb = dbus_msg_cb;
+ }
+
+ addr = (mrp_dbusaddr_t *)&t->local;
+ mrp_dbus_remove_method(t->dbus, addr->db_path, TRANSPORT_INTERFACE,
+ method, cb, t);
+ }
+
+ if (t->connected && t->remote.db_addr != NULL)
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+
+ mrp_dbus_unref(t->dbus);
+ t->dbus = NULL;
+}
+
+
+static int dbus_msg_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ mrp_msg_t *msg;
+
+ MRP_UNUSED(dbus);
+
+ msg = msg_decode(dmsg, &sender_path);
+
+ if (msg != NULL) {
+ sender = dbus_message_get_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsg(mt, msg, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvmsgfrom(mt, msg, &addr, alen, mt->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_data_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ uint16_t tag;
+ void *decoded;
+
+ MRP_UNUSED(dbus);
+
+ decoded = data_decode(dmsg, &tag, &sender_path);
+
+ if (decoded != NULL) {
+ sender = dbus_message_get_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdata(mt, decoded, tag, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvdatafrom(mt, decoded, tag, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode custom data message.");
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_raw_cb(mrp_dbus_t *dbus, DBusMessage *dmsg, void *user_data)
+{
+ mrp_transport_t *mt = (mrp_transport_t *)user_data;
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *sender, *sender_path;
+ void *data;
+ size_t size;
+
+ MRP_UNUSED(dbus);
+
+ data = raw_decode(dmsg, &size, &sender_path);
+
+ if (data != NULL) {
+ sender = dbus_message_get_sender(dmsg);
+
+ if (mt->connected) {
+ if (!t->peer_resolved || !strcmp(t->remote.db_addr, sender))
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvraw(mt, data, size, mt->user_data);
+ });
+ }
+ else {
+ peer_address(&addr, sender, sender_path);
+ alen = sizeof(addr);
+
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.recvrawfrom(mt, data, size, &addr, alen,
+ mt->user_data);
+ });
+ }
+
+ mt->check_destroy(mt);
+ }
+ else {
+ mrp_log_error("Failed to decode raw message.");
+ }
+
+ return TRUE;
+}
+
+
+static void peer_state_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ dbus_t *t = (dbus_t *)user_data;
+ mrp_sockaddr_t addr;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ peer_address(&addr, owner, t->remote.db_path);
+ copy_address(&t->remote, (mrp_dbusaddr_t *)&addr);
+ t->peer_resolved = TRUE;
+ }
+ else {
+ /*
+ * XXX TODO:
+ * It would be really tempting here to call
+ * mt->evt.closed(mt, ECONNRESET, mt->user_data)
+ * to notify the user about the fact our peer went down.
+ * However, that would not be in line with the other
+ * transports which call the closed event handler only
+ * upon foricble transport closes upon errors.
+ *
+ * The transport interface abstraction (especially the
+ * available set of events) anyway needs some eyeballing,
+ * so the right thing to do might be to define a new event
+ * for disconnection and call the handler for that here...
+ */
+ }
+
+}
+
+
+static int dbus_connect(mrp_transport_t *mt, mrp_sockaddr_t *addrp,
+ socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+
+ if (!check_address(addrp, addrlen)) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ if (t->dbus == NULL) {
+ t->dbus = mrp_dbus_connect(t->ml, addr->db_bus, NULL);
+
+ if (t->dbus == NULL) {
+ errno = ECONNRESET;
+ return FALSE;
+ }
+ }
+ else {
+ /* XXX TODO: check given address against address of the bus */
+ }
+
+ if (!t->bound)
+ if (!dbus_autobind(mt, addrp))
+ return FALSE;
+
+ if (mrp_dbus_follow_name(t->dbus, addr->db_addr, peer_state_cb, t)) {
+ copy_address(&t->remote, addr);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dbus_disconnect(mrp_transport_t *mt)
+{
+ dbus_t *t = (dbus_t *)mt;
+
+ if (t->connected && t->remote.db_addr != NULL) {
+ mrp_dbus_forget_name(t->dbus, t->remote.db_addr, peer_state_cb, t);
+ mrp_clear(&t->remote);
+ t->peer_resolved = FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static int dbus_sendmsgto(mrp_transport_t *mt, mrp_msg_t *msg,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ DBusMessage *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = msg_encode(t->local.db_path, msg);
+
+ if (m != NULL) {
+ if (mrp_dbus_send(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_MESSAGE,
+ 0, NULL, NULL, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ dbus_message_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendmsg(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendmsgto(mt, msg, addr, alen);
+}
+
+
+static int dbus_sendrawto(mrp_transport_t *mt, void *data, size_t size,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ DBusMessage *m;
+ int success;
+
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(data);
+ MRP_UNUSED(size);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = raw_encode(t->local.db_path, data, size);
+
+ if (m != NULL) {
+ if (mrp_dbus_send(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_RAW,
+ 0, NULL, NULL, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ dbus_message_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_sendrawto(mt, data, size, addr, alen);
+}
+
+
+static int dbus_senddatato(mrp_transport_t *mt, void *data, uint16_t tag,
+ mrp_sockaddr_t *addrp, socklen_t addrlen)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_dbusaddr_t *addr = (mrp_dbusaddr_t *)addrp;
+ DBusMessage *m;
+ int success;
+
+ if (check_address(addrp, addrlen)) {
+ if (t->dbus == NULL && !dbus_autobind(mt, addrp))
+ return FALSE;
+
+ m = data_encode(t->local.db_path, data, tag);
+
+ if (m != NULL) {
+ if (mrp_dbus_send(t->dbus, addr->db_addr, addr->db_path,
+ TRANSPORT_INTERFACE, TRANSPORT_DATA,
+ 0, NULL, NULL, m))
+ success = TRUE;
+ else {
+ errno = ECOMM;
+ success = FALSE;
+ }
+
+ dbus_message_unref(m);
+ }
+ else
+ success = FALSE;
+ }
+ else {
+ errno = EINVAL;
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int dbus_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ dbus_t *t = (dbus_t *)mt;
+ mrp_sockaddr_t *addr = (mrp_sockaddr_t *)&t->remote;
+ socklen_t alen = sizeof(t->remote);
+
+ return dbus_senddatato(mt, data, tag, addr, alen);
+}
+
+
+static const char *get_array_signature(uint16_t type)
+{
+#define MAP(from, to) \
+ case MRP_MSG_FIELD_##from: \
+ return DBUS_TYPE_##to##_AS_STRING;
+
+ switch (type) {
+ MAP(STRING, STRING);
+ MAP(BOOL , BOOLEAN);
+ MAP(UINT8 , UINT16);
+ MAP(SINT8 , INT16);
+ MAP(UINT16, UINT16);
+ MAP(SINT16, INT16);
+ MAP(UINT32, UINT32);
+ MAP(SINT32, INT32);
+ MAP(UINT64, UINT64);
+ MAP(SINT64, INT64);
+ MAP(DOUBLE, DOUBLE);
+ MAP(BLOB , BYTE );
+ default:
+ return NULL;
+ }
+}
+
+
+static DBusMessage *msg_encode(const char *sender_id, mrp_msg_t *msg)
+{
+ /*
+ * Notes: There is a type mismatch between our and DBUS types for
+ * 8-bit integers (D-BUS does not have a signed 8-bit type)
+ * and boolean types (D-BUS has uint32_t booleans, C99 fails
+ * to specify the type and gcc uses a signed char). The
+ * QUIRKY versions of the macros take care of these mismatches.
+ */
+
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!dbus_message_iter_append_basic(_i, type, vptr)) \
+ goto fail; \
+ break
+
+ DBusMessage *m;
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+ uint16_t base;
+ uint32_t asize, i;
+ DBusMessageIter im, ia;
+ const char *sig;
+ int type, len;
+ void *vptr;
+ dbus_bool_t bln;
+ uint16_t u16;
+ int16_t s16;
+
+ m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+ if (m == NULL)
+ return NULL;
+
+ dbus_message_iter_init_append(m, &im);
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_OBJECT_PATH, &sender_id))
+ goto fail;
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &msg->nfield))
+ goto fail;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->tag) ||
+ !dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ switch (f->type) {
+ BASIC_SIMPLE(&im, STRING, STRING , f->str);
+ BASIC_QUIRKY(&im, BOOL , BOOLEAN, f->bln, bln);
+ BASIC_QUIRKY(&im, UINT8 , UINT16 , f->u8 , u16);
+ BASIC_QUIRKY(&im, SINT8 , INT16 , f->s8 , s16);
+ BASIC_SIMPLE(&im, UINT16, UINT16 , f->u16);
+ BASIC_SIMPLE(&im, SINT16, INT16 , f->s16);
+ BASIC_SIMPLE(&im, UINT32, UINT32 , f->u32);
+ BASIC_SIMPLE(&im, SINT32, INT32 , f->s32);
+ BASIC_SIMPLE(&im, UINT64, UINT64 , f->u64);
+ BASIC_SIMPLE(&im, SINT64, INT64 , f->s64);
+ BASIC_SIMPLE(&im, DOUBLE, DOUBLE , f->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ vptr = f->blb;
+ len = (int)f->size[0];
+ sig = get_array_signature(f->type);
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia) ||
+ !dbus_message_iter_append_fixed_array(&ia, sig[0],
+ &vptr, len) ||
+ !dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ sig = get_array_signature(base);
+
+ if (!dbus_message_iter_append_basic(&im,
+ DBUS_TYPE_UINT32, &asize))
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia))
+ goto fail;
+
+ for (i = 0; i < asize; i++) {
+ switch (base) {
+ ARRAY_SIMPLE(&ia, STRING, STRING , f->astr[i]);
+ ARRAY_QUIRKY(&ia, BOOL , BOOLEAN, f->abln[i], bln);
+ ARRAY_QUIRKY(&ia, UINT8 , UINT16 , f->au8[i] , u16);
+ ARRAY_QUIRKY(&ia, SINT8 , INT16 , f->as8[i] , s16);
+ ARRAY_SIMPLE(&ia, UINT16, UINT16 , f->au16[i]);
+ ARRAY_SIMPLE(&ia, SINT16, INT16 , f->as16[i]);
+ ARRAY_SIMPLE(&ia, UINT32, UINT32 , f->au32[i]);
+ ARRAY_SIMPLE(&ia, SINT32, INT32 , f->as32[i]);
+ ARRAY_SIMPLE(&ia, UINT64, UINT64 , f->au64[i]);
+ ARRAY_SIMPLE(&ia, DOUBLE, DOUBLE , f->adbl[i]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ dbus_message_unref(m);
+
+ errno = ECOMM;
+
+ return FALSE;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_msg_t *msg_decode(DBusMessage *m, const char **sender_id)
+{
+#define BASIC_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_var)); \
+ dbus_message_iter_next(_i); \
+ \
+ if (!mrp_msg_append(msg, tag, type, (_var))) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_dvar)); \
+ dbus_message_iter_next(_i); \
+ \
+ _mvar = _dvar; \
+ if (!mrp_msg_append(msg, tag, type, (_mvar))) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_var)); \
+ dbus_message_iter_next(_i); \
+ break
+
+#define ARRAY_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_dvar)); \
+ dbus_message_iter_next(_i); \
+ \
+ _mvar = _dvar; \
+ break
+
+#define APPEND_ARRAY(_type, _var) \
+ case MRP_MSG_FIELD_##_type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY | \
+ MRP_MSG_FIELD_##_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ mrp_msg_t *msg;
+ mrp_msg_value_t v;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ DBusMessageIter im, ia;
+ uint16_t nfield, tag, type, base, i;
+ uint32_t n, j;
+ int asize;
+ const char *sender;
+
+ msg = NULL;
+
+ if (!dbus_message_iter_init(m, &im))
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &sender);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &nfield);
+ dbus_message_iter_next(&im);
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &tag);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &type);
+ dbus_message_iter_next(&im);
+
+ switch (type) {
+ BASIC_SIMPLE(&im, STRING, STRING , v.str);
+ BASIC_QUIRKY(&im, BOOL , BOOLEAN, v.bln, u32);
+ BASIC_QUIRKY(&im, UINT8 , UINT16 , v.u8 , u16);
+ BASIC_QUIRKY(&im, SINT8 , INT16 , v.s8 , s16);
+ BASIC_SIMPLE(&im, UINT16, UINT16 , v.u16);
+ BASIC_SIMPLE(&im, SINT16, INT16 , v.s16);
+ BASIC_SIMPLE(&im, UINT32, UINT32 , v.u32);
+ BASIC_SIMPLE(&im, SINT32, INT32 , v.s32);
+ BASIC_SIMPLE(&im, UINT64, UINT64 , v.u64);
+ BASIC_SIMPLE(&im, SINT64, INT64 , v.s64);
+ BASIC_SIMPLE(&im, DOUBLE, DOUBLE , v.dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (dbus_message_iter_get_element_type(&im) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_get_fixed_array(&ia, &v.blb, &asize);
+ dbus_message_iter_next(&im);
+ if (!mrp_msg_append(msg, tag, type, asize, v.blb))
+ goto fail;
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT32)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &n);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+ goto fail;
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_next(&im);
+
+ {
+ char *astr[n];
+ uint32_t dbln[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(&ia, STRING, STRING , astr[j]);
+ ARRAY_QUIRKY(&ia, BOOL , BOOLEAN, abln[j], dbln[j]);
+ ARRAY_QUIRKY(&ia, UINT8 , UINT16 , au8[j] , au16[j]);
+ ARRAY_QUIRKY(&ia, SINT8 , INT16 , as8[j] , as16[j]);
+ ARRAY_SIMPLE(&ia, UINT16, UINT16 , au16[j]);
+ ARRAY_SIMPLE(&ia, SINT16, INT16 , as16[j]);
+ ARRAY_SIMPLE(&ia, UINT32, UINT32 , au32[j]);
+ ARRAY_SIMPLE(&ia, SINT32, INT32 , as32[j]);
+ ARRAY_SIMPLE(&ia, UINT64, UINT64 , au64[j]);
+ ARRAY_SIMPLE(&ia, SINT64, INT64 , as64[j]);
+ ARRAY_SIMPLE(&ia, DOUBLE, DOUBLE , adbl[j]);
+ default:
+ goto fail;
+ }
+ }
+
+ switch (base) {
+ APPEND_ARRAY(STRING, astr);
+ APPEND_ARRAY(BOOL , abln);
+ APPEND_ARRAY(UINT8 , au8 );
+ APPEND_ARRAY(SINT8 , as8 );
+ APPEND_ARRAY(UINT16, au16);
+ APPEND_ARRAY(SINT16, as16);
+ APPEND_ARRAY(UINT32, au32);
+ APPEND_ARRAY(SINT32, as32);
+ APPEND_ARRAY(UINT64, au64);
+ APPEND_ARRAY(SINT64, as64);
+ APPEND_ARRAY(DOUBLE, adbl);
+ default:
+ goto fail;
+ }
+ }
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ errno = EBADMSG;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+#undef APPEND_ARRAY
+}
+
+
+static DBusMessage *data_encode(const char *sender_id, void *data, uint16_t tag)
+{
+#define BASIC_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &(_val); \
+ \
+ if (!dbus_message_iter_append_basic(&im, type, vptr)) \
+ goto fail; \
+ break
+
+#define BASIC_QUIRKY(_mtype, _dtype, _mval, _dval) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dval = _mval; \
+ vptr = &_dval; \
+ \
+ if (!dbus_message_iter_append_basic(&im, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_SIMPLE(_mtype, _dtype, _val) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ vptr = &_val; \
+ \
+ if (!dbus_message_iter_append_basic(&ia, type, vptr)) \
+ goto fail; \
+ break
+
+#define ARRAY_QUIRKY(_mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ type = DBUS_TYPE_##_dtype; \
+ _dvar = _mvar; \
+ vptr = &_dvar; \
+ \
+ if (!dbus_message_iter_append_basic(&ia, type, vptr)) \
+ goto fail; \
+ break
+
+ DBusMessage *m;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type, base;
+ mrp_msg_value_t *v;
+ void *vptr;
+ uint32_t n, j;
+ int i, blblen;
+ DBusMessageIter im, ia;
+ const char *sig;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t bln;
+
+ m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+ if (m == NULL)
+ return NULL;
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+
+ dbus_message_iter_init_append(m, &im);
+
+ if (!dbus_message_iter_append_basic(&im,
+ DBUS_TYPE_OBJECT_PATH, &sender_id))
+ goto fail;
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &tag))
+ goto fail;
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &nfield))
+ goto fail;
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->tag) ||
+ !dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT16, &f->type))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ BASIC_SIMPLE(STRING, STRING , v->str);
+ BASIC_QUIRKY(BOOL , BOOLEAN, v->bln, bln);
+ BASIC_QUIRKY(UINT8 , UINT16 , v->u8 , u16);
+ BASIC_QUIRKY(SINT8 , INT16 , v->s8 , s16);
+ BASIC_SIMPLE(UINT16, UINT16 , v->u16);
+ BASIC_SIMPLE(SINT16, INT16 , v->s16);
+ BASIC_SIMPLE(UINT32, UINT32 , v->u32);
+ BASIC_SIMPLE(SINT32, INT32 , v->s32);
+ BASIC_SIMPLE(UINT64, UINT64 , v->u64);
+ BASIC_SIMPLE(SINT64, INT64 , v->s64);
+ BASIC_SIMPLE(DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ sig = get_array_signature(f->type);
+ blblen = mrp_data_get_blob_size(data, descr, i);
+
+ if (blblen == -1)
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia) ||
+ !dbus_message_iter_append_fixed_array(&ia, sig[0],
+ &f->blb, blblen) ||
+ !dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ n = mrp_data_get_array_size(data, descr, i);
+ sig = get_array_signature(base);
+
+ if (!dbus_message_iter_append_basic(&im, DBUS_TYPE_UINT32, &n))
+ goto fail;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY,
+ sig, &ia))
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ ARRAY_SIMPLE(STRING, STRING , v->astr[j]);
+ ARRAY_QUIRKY(BOOL , BOOLEAN, v->abln[j], bln);
+ ARRAY_QUIRKY(UINT8 , UINT16 , v->au8[j] , u16);
+ ARRAY_QUIRKY(SINT8 , INT16 , v->as8[j] , s16);
+ ARRAY_SIMPLE(UINT16, UINT16 , v->au16[j]);
+ ARRAY_SIMPLE(SINT16, INT16 , v->as16[j]);
+ ARRAY_SIMPLE(UINT32, UINT32 , v->au32[j]);
+ ARRAY_SIMPLE(SINT32, INT32 , v->as32[j]);
+ ARRAY_SIMPLE(UINT64, UINT64 , v->au64[j]);
+ ARRAY_SIMPLE(DOUBLE, DOUBLE , v->adbl[j]);
+
+ case MRP_MSG_FIELD_BLOB:
+ goto fail;
+
+ default:
+ goto fail;
+ }
+ }
+
+ if (!dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+ }
+ }
+
+ return m;
+
+ fail:
+ if (m != NULL)
+ dbus_message_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+
+#undef BASIC_SIMPLE
+#undef BASIC_QUIRKY
+#undef ARRAY_SIMPLE
+#undef ARRAY_QUIRKY
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+static void *data_decode(DBusMessage *m, uint16_t *tagp, const char **sender_id)
+{
+#define HANDLE_SIMPLE(_i, _mtype, _dtype, _var) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_var)); \
+ dbus_message_iter_next(_i); \
+ break
+
+#define HANDLE_QUIRKY(_i, _mtype, _dtype, _mvar, _dvar) \
+ case MRP_MSG_FIELD_##_mtype: \
+ if (dbus_message_iter_get_arg_type(_i) != DBUS_TYPE_##_dtype) \
+ goto fail; \
+ dbus_message_iter_get_basic(_i, &(_dvar)); \
+ dbus_message_iter_next(_i); \
+ \
+ _mvar = _dvar; \
+ break
+
+ void *data;
+ mrp_data_descr_t *descr;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t tag, type, base;
+ mrp_msg_value_t *v;
+ uint32_t n, j, size;
+ int i, blblen;
+ DBusMessageIter im, ia;
+ const char *sender;
+ uint32_t u32;
+ uint16_t u16;
+ int16_t s16;
+
+ tag = 0;
+ data = NULL;
+
+ if (!dbus_message_iter_init(m, &im))
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &sender);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &tag);
+ dbus_message_iter_next(&im);
+
+ descr = mrp_msg_find_type(tag);
+
+ if (descr == NULL)
+ goto fail;
+
+ *tagp = tag;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &nfield);
+ dbus_message_iter_next(&im);
+
+ if (nfield != descr->nfield)
+ goto fail;
+
+ fields = descr->fields;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ goto fail;
+
+ for (i = 0; i < nfield; i++) {
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &tag);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT16)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &type);
+ dbus_message_iter_next(&im);
+
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto fail;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (type) {
+ HANDLE_SIMPLE(&im, STRING, STRING , v->str);
+ HANDLE_QUIRKY(&im, BOOL , BOOLEAN, v->bln, u32);
+ HANDLE_QUIRKY(&im, UINT8 , UINT16 , v->u8 , u16);
+ HANDLE_QUIRKY(&im, SINT8 , INT16 , v->s8 , s16);
+ HANDLE_SIMPLE(&im, UINT16, UINT16 , v->u16);
+ HANDLE_SIMPLE(&im, SINT16, INT16 , v->s16);
+ HANDLE_SIMPLE(&im, UINT32, UINT32 , v->u32);
+ HANDLE_SIMPLE(&im, SINT32, INT32 , v->s32);
+ HANDLE_SIMPLE(&im, UINT64, UINT64 , v->u64);
+ HANDLE_SIMPLE(&im, SINT64, INT64 , v->s64);
+ HANDLE_SIMPLE(&im, DOUBLE, DOUBLE , v->dbl);
+
+ case MRP_MSG_FIELD_BLOB:
+ if (dbus_message_iter_get_element_type(&ia) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_get_fixed_array(&ia, &v->blb, &blblen);
+ dbus_message_iter_next(&im);
+ v->blb = mrp_datadup(v->blb, blblen);
+ if (v->blb == NULL)
+ goto fail;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY))
+ goto fail;
+
+ base = type & ~(MRP_MSG_FIELD_ARRAY);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_UINT32)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &n);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_next(&im);
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto fail;
+
+ for (j = 0; j < n; j++) {
+ uint32_t dbln[n];
+ uint16_t au16[n];
+ int16_t as16[n];
+
+ switch (base) {
+ HANDLE_SIMPLE(&ia, STRING, STRING , v->astr[j]);
+ HANDLE_QUIRKY(&ia, BOOL , BOOLEAN, v->abln[j], dbln[j]);
+ HANDLE_QUIRKY(&ia, UINT8 , UINT16 , v->au8[j] , au16[j]);
+ HANDLE_QUIRKY(&ia, SINT8 , INT16 , v->as8[j] , as16[j]);
+ HANDLE_SIMPLE(&ia, UINT16, UINT16 , v->au16[j]);
+ HANDLE_SIMPLE(&ia, SINT16, INT16 , v->as16[j]);
+ HANDLE_SIMPLE(&ia, UINT32, UINT32 , v->au32[j]);
+ HANDLE_SIMPLE(&ia, SINT32, INT32 , v->as32[j]);
+ HANDLE_SIMPLE(&ia, UINT64, UINT64 , v->au64[j]);
+ HANDLE_SIMPLE(&ia, SINT64, INT64 , v->as64[j]);
+ HANDLE_SIMPLE(&ia, DOUBLE, DOUBLE , v->adbl[j]);
+ }
+
+ if (base == MRP_MSG_FIELD_STRING) {
+ v->astr[j] = mrp_strdup(v->astr[j]);
+ if (v->astr[j] == NULL)
+ goto fail;
+ }
+ }
+ }
+
+ if (f->type == MRP_MSG_FIELD_STRING) {
+ v->str = mrp_strdup(v->str);
+ if (v->str == NULL)
+ goto fail;
+ }
+ }
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ mrp_data_free(data, tag);
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+static DBusMessage *raw_encode(const char *sender_id, void *data, size_t size)
+{
+ DBusMessage *m;
+ DBusMessageIter im, ia;
+ const char *sig;
+ int len;
+
+ m = dbus_message_new(DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+ if (m != NULL) {
+ dbus_message_iter_init_append(m, &im);
+
+ if (!dbus_message_iter_append_basic(&im,
+ DBUS_TYPE_OBJECT_PATH, &sender_id))
+ goto fail;
+
+ sig = DBUS_TYPE_BYTE_AS_STRING;
+ len = (int)size;
+
+ if (!dbus_message_iter_open_container(&im, DBUS_TYPE_ARRAY, sig, &ia) ||
+ !dbus_message_iter_append_fixed_array(&ia, sig[0], &data, len) ||
+ !dbus_message_iter_close_container(&im, &ia))
+ goto fail;
+
+ return m;
+ }
+ else
+ return NULL;
+
+ fail:
+ if (m != NULL)
+ dbus_message_unref(m);
+
+ errno = ECOMM;
+
+ return NULL;
+}
+
+
+static void *raw_decode(DBusMessage *m, size_t *sizep, const char **sender_id)
+{
+ DBusMessageIter im, ia;
+ const char *sender;
+ void *data;
+ int len;
+
+ data = NULL;
+
+ if (!dbus_message_iter_init(m, &im))
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_OBJECT_PATH)
+ goto fail;
+
+ dbus_message_iter_get_basic(&im, &sender);
+ dbus_message_iter_next(&im);
+
+ if (dbus_message_iter_get_element_type(&ia) != DBUS_TYPE_BYTE)
+ goto fail;
+
+ if (dbus_message_iter_get_arg_type(&im) != DBUS_TYPE_ARRAY)
+ goto fail;
+
+ dbus_message_iter_recurse(&im, &ia);
+ dbus_message_iter_get_fixed_array(&ia, &data, &len);
+
+ data = mrp_datadup(data, len);
+
+ if (sizep != NULL)
+ *sizep = (size_t)len;
+
+ if (sender_id != NULL)
+ *sender_id = sender;
+
+ return data;
+
+ fail:
+ errno = EBADMSG;
+
+ return NULL;
+}
+
+
+MRP_REGISTER_TRANSPORT(dbus, DBUS, dbus_t, dbus_resolve,
+ dbus_open, dbus_createfrom, dbus_close, NULL,
+ dbus_bind, NULL, NULL,
+ dbus_connect, dbus_disconnect,
+ dbus_sendmsg, dbus_sendmsgto,
+ dbus_sendraw, dbus_sendrawto,
+ dbus_senddata, dbus_senddatato,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_TRANSPORT_H__
+#define __MURPHY_DBUS_TRANSPORT_H__
+
+#include <murphy/common/transport.h>
+
+#define MRP_AF_DBUS 0xDB
+
+#define MRP_DBUSADDR_BASE \
+ __SOCKADDR_COMMON(db_); \
+ char *db_bus; /* D-BUS bus address */ \
+ char *db_addr; /* address on bus */ \
+ char *db_path /* instance path */ \
+
+typedef struct {
+ MRP_DBUSADDR_BASE;
+} _mrp_dbusaddr_base_t;
+
+
+typedef struct {
+ MRP_DBUSADDR_BASE;
+ char db_fqa[MRP_SOCKADDR_SIZE - sizeof(_mrp_dbusaddr_base_t)];
+} mrp_dbusaddr_t;
+
+
+
+#endif /* __MURPHY_DBUS_TRANSPORT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+static void __attribute__((constructor)) register_debug_data(void)
+{
+ mrp_debug_file_t *df;
+ int i;
+
+ for (i = 0; (df = debug_files[i]) != NULL; i++)
+ mrp_debug_register_file(df);
+}
+
+static void __attribute__((destructor)) unregister_debug_data(void)
+{
+ mrp_debug_file_t *df;
+ int i;
+
+ for (i = 0; (df = debug_files[i]) != NULL; i++)
+ mrp_debug_unregister_file(df);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DEBUG_INFO_H__
+#define __MURPHY_DEBUG_INFO_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * line number information for a single function
+ */
+
+typedef struct {
+ const char *func; /* name of the function */
+ int line; /* start at this line */
+} mrp_debug_info_t;
+
+
+/*
+ * funcion - line number mapping for a single file
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook for startup registration */
+ const char *file; /* file name */
+ mrp_debug_info_t *info; /* function information */
+} mrp_debug_file_t;
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_DEBUG_INFO_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE
+#include <link.h>
+#include <elf.h>
+
+#include <stdarg.h>
+#include <limits.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/debug.h>
+
+#define WILDCARD "*"
+
+int mrp_debug_stamp = 0; /* debug config stamp */
+
+static int debug_enabled; /* debug messages enabled */
+static mrp_htbl_t *rules_on; /* enabling rules */
+static mrp_htbl_t *rules_off; /* disabling rules */
+
+static void free_rule_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ mrp_free(entry);
+}
+
+
+static int init_rules(void)
+{
+ mrp_htbl_config_t hcfg;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = free_rule_cb;
+
+ rules_on = mrp_htbl_create(&hcfg);
+ rules_off = mrp_htbl_create(&hcfg);
+
+ if (rules_on == NULL || rules_off == NULL)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static void reset_rules(void)
+{
+ if (rules_on != NULL) {
+ mrp_htbl_destroy(rules_on , TRUE);
+ rules_on = NULL;
+ }
+ if (rules_off != NULL) {
+ mrp_htbl_destroy(rules_off, TRUE);
+ rules_off = NULL;
+ }
+}
+
+
+void mrp_debug_reset(void)
+{
+ debug_enabled = FALSE;
+ reset_rules();
+}
+
+
+int mrp_debug_enable(int enabled)
+{
+ int prev = debug_enabled;
+
+ debug_enabled = !!enabled;
+ mrp_log_enable(MRP_LOG_MASK_DEBUG);
+ mrp_debug_stamp++;
+
+ return prev;
+}
+
+
+static int add_rule(const char *func, const char *file, int line, int off)
+{
+ mrp_htbl_t *ht;
+ char *rule, *r, buf[PATH_MAX * 2];
+
+ if (rules_on == NULL)
+ if (!init_rules())
+ return FALSE;
+
+ r = rule = NULL;
+
+ if (!off)
+ ht = rules_on;
+ else
+ ht = rules_off;
+
+ if (func != NULL && file == NULL && line == 0) {
+ r = mrp_htbl_lookup(ht, (void *)func);
+ rule = (char *)func;
+ }
+ else if (func != NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ r = mrp_htbl_lookup(ht, (void *)buf);
+ rule = buf;
+ }
+ else if (func == NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "@%s", file);
+ r = mrp_htbl_lookup(ht, (void *)buf);
+ rule = buf;
+ }
+ else if (func == NULL && file != NULL && line > 0) {
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ r = mrp_htbl_lookup(ht, (void *)buf);
+ rule = buf;
+ }
+
+ if (r != NULL)
+ return FALSE;
+
+ rule = mrp_strdup(rule);
+ if (rule == NULL)
+ return FALSE;
+
+ if (mrp_htbl_insert(ht, rule, rule)) {
+ mrp_debug_stamp++;
+
+ return TRUE;
+ }
+ else {
+ mrp_free(rule);
+
+ return FALSE;
+ }
+}
+
+
+static int del_rule(const char *func, const char *file, int line, int off)
+{
+ mrp_htbl_t *ht;
+ char *r, buf[PATH_MAX * 2];
+
+ if (rules_on == NULL)
+ if (!init_rules())
+ return FALSE;
+
+ r = NULL;
+
+ if (!off)
+ ht = rules_on;
+ else
+ ht = rules_off;
+
+ if (func != NULL && file == NULL && line == 0) {
+ r = mrp_htbl_remove(ht, (void *)func, TRUE);
+ }
+ else if (func != NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+ }
+ else if (func == NULL && file != NULL && line == 0) {
+ snprintf(buf, sizeof(buf), "@%s", file);
+ r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+ }
+ else if (func == NULL && file != NULL && line > 0) {
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ r = mrp_htbl_remove(ht, (void *)buf, TRUE);
+ }
+
+ if (r != NULL) {
+ mrp_debug_stamp++;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_debug_set_config(const char *cmd)
+{
+ char buf[2 * PATH_MAX + 1], *colon, *at, *eq;
+ char *func, *file, *end;
+ size_t len;
+ int del, off, line;
+
+ if (*cmd == '+' || *cmd == '-')
+ del = (*cmd++ == '-');
+ else
+ del = FALSE;
+
+ eq = strchr(cmd, '=');
+
+ if (eq == NULL) {
+ strncpy(buf, cmd, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = '\0';
+ off = FALSE;
+ }
+ else {
+ if (!strcmp(eq + 1, "on"))
+ off = FALSE;
+ else if (!strcmp(eq + 1, "off"))
+ off = TRUE;
+ else
+ return FALSE;
+
+ len = eq - cmd;
+ if (len >= sizeof(buf))
+ len = sizeof(buf) - 1;
+
+ strncpy(buf, cmd, len);
+ buf[len] = '\0';
+ }
+
+ colon = strchr(buf, ':');
+
+ if (colon != NULL) {
+ if (strchr(buf, '@') != NULL)
+ return FALSE;
+
+ *colon = '\0';
+ func = NULL;
+ file = buf;
+ line = strtoul(colon + 1, &end, 10);
+
+ if (end && *end)
+ return FALSE;
+
+ mrp_log_info("%s file='%s', line=%d, %s", del ? "del" : "add",
+ file, line, off ? "off" : "on");
+ }
+ else {
+ at = strchr(buf, '@');
+
+ if (at != NULL) {
+ *at = '\0';
+ func = (at == buf ? NULL : buf);
+ file = at + 1;
+ line = 0;
+
+ mrp_log_info("%s func='%s', file='%s', %s", del ? "del" : "add",
+ func ? func : "", file, off ? "off" : "on");
+ }
+ else {
+ func = buf;
+ file = NULL;
+ line = 0;
+
+ mrp_log_info("%s func='%s' %s", del ? "del" : "add",
+ func, off ? "off" : "on");
+ }
+ }
+
+ if (!del)
+ return add_rule(func, file, line, off);
+ else
+ return del_rule(func, file, line, off);
+
+ return TRUE;
+}
+
+
+typedef struct {
+ FILE *fp;
+ int on;
+} dump_t;
+
+
+static int dump_rule_cb(void *key, void *object, void *user_data)
+{
+ dump_t *d = (dump_t *)user_data;
+ FILE *fp = d->fp;
+ const char *state = d->on ? "on" : "off";
+
+ MRP_UNUSED(key);
+
+ fprintf(fp, " %s %s\n", (char *)object, state);
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+int mrp_debug_dump_config(FILE *fp)
+{
+ dump_t d;
+
+ fprintf(fp, "Debugging is %sabled\n", debug_enabled ? "en" : "dis");
+
+ if (rules_on != NULL) {
+ fprintf(fp, "Debugging rules:\n");
+
+ d.fp = fp;
+ d.on = TRUE;
+ mrp_htbl_foreach(rules_on , dump_rule_cb, &d);
+ d.on = FALSE;
+ mrp_htbl_foreach(rules_off, dump_rule_cb, &d);
+ }
+ else
+ fprintf(fp, "No debugging rules defined.\n");
+
+ return TRUE;
+}
+
+
+void mrp_debug_msg(const char *file, int line, const char *func,
+ const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ mrp_log_msgv(MRP_LOG_DEBUG, file, line, func, format, ap);
+ va_end(ap);
+}
+
+
+int mrp_debug_check(const char *func, const char *file, int line)
+{
+ char buf[2 * PATH_MAX], *base;
+ void *key;
+
+ if (!debug_enabled || rules_on == NULL)
+ return FALSE;
+
+ base = NULL;
+ key = (void *)func;
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ base = strrchr(file, '/');
+ if (base != NULL)
+ base++;
+
+ key = buf;
+
+ snprintf(buf, sizeof(buf), "@%s", file);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ if (base != NULL) {
+ snprintf(buf, sizeof(buf), "@%s", base);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+ }
+
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ if (mrp_htbl_lookup(rules_on, key) != NULL)
+ goto check_suppress;
+
+ if (mrp_htbl_lookup(rules_on, (void *)WILDCARD) == NULL)
+ return FALSE;
+
+
+ check_suppress:
+ if (rules_off == NULL)
+ return TRUE;
+
+ key = (void *)func;
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ key = buf;
+
+ snprintf(buf, sizeof(buf), "@%s", file);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ if (base != NULL) {
+ snprintf(buf, sizeof(buf), "@%s", base);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+ }
+
+ snprintf(buf, sizeof(buf), "%s@%s", func, file);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ snprintf(buf, sizeof(buf), "%s:%d", file, line);
+ if (mrp_htbl_lookup(rules_off, key) != NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DEBUG_H__
+#define __MURPHY_DEBUG_H__
+
+#include <stdio.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug-info.h>
+
+MRP_CDECL_BEGIN
+
+/** Log a debug message if the invoking debug site is enabled. */
+#define mrp_debug(fmt, args...) do { \
+ static int __site_stamp = -1; \
+ static int __site_enabled; \
+ \
+ if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) { \
+ __site_enabled = mrp_debug_check(__FUNCTION__, \
+ __FILE__, __LINE__); \
+ __site_stamp = mrp_debug_stamp; \
+ } \
+ \
+ if (MRP_UNLIKELY(__site_enabled)) \
+ mrp_debug_msg(__LOC__, fmt, ## args); \
+ } while (0)
+
+
+/** mrp_debug variant with explicitly passed site info. */
+#define mrp_debug_at(_file, _line, _func, fmt, args...) do { \
+ static int __site_stamp = -1; \
+ static int __site_enabled; \
+ \
+ if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) { \
+ __site_enabled = mrp_debug_check(_func, _file, _line); \
+ __site_stamp = mrp_debug_stamp; \
+ } \
+ \
+ if (MRP_UNLIKELY(__site_enabled)) \
+ mrp_debug_msg(_file, _line, _func, fmt, ## args); \
+ } while (0)
+
+
+/** Run a block of code if the invoking debug site is enabled. */
+#define mrp_debug_code(...) do { \
+ static int __site_stamp = -1; \
+ static int __site_enabled; \
+ \
+ if (MRP_UNLIKELY(__site_stamp != mrp_debug_stamp)) { \
+ __site_enabled = mrp_debug_check(__FUNCTION__, \
+ __FILE__, __LINE__); \
+ __site_stamp = mrp_debug_stamp; \
+ } \
+ \
+ if (MRP_UNLIKELY(__site_enabled)) { \
+ __VA_ARGS__; \
+ } \
+ } while (0)
+
+/** Global debug configuration stamp, exported for minimum-overhead checking. */
+extern int mrp_debug_stamp;
+
+/** Enable/disable debug messages globally. */
+int mrp_debug_enable(int enabled);
+
+/** Reset all debug configuration to the defaults. */
+void mrp_debug_reset(void);
+
+/** Apply the debug configuration settings given in cmd. */
+int mrp_debug_set_config(const char *cmd);
+
+/** Dump the active debug configuration. */
+int mrp_debug_dump_config(FILE *fp);
+
+/** Low-level log wrapper for debug messages. */
+void mrp_debug_msg(const char *file, int line, const char *func,
+ const char *format, ...) MRP_PRINTF_LIKE(4, 5);
+
+/** Check if the given debug site is enabled. */
+int mrp_debug_check(const char *func, const char *file, int line);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_DEBUG_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)NULL)->sun_path)
+#endif
+
+#define UDP4 "udp4"
+#define UDP4L 4
+#define UDP6 "udp6"
+#define UDP6L 4
+#define UNXD "unxd"
+#define UNXDL 4
+
+
+#define DEFAULT_SIZE 1024 /* default input buffer size */
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ int sock; /* UDP socket */
+ int family; /* socket family */
+ mrp_io_watch_t *iow; /* socket I/O watch */
+ void *ibuf; /* input buffer */
+ size_t isize; /* input buffer size */
+ size_t idata; /* amount of input data */
+} dgrm_t;
+
+
+static void dgrm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data);
+static int dgrm_disconnect(mrp_transport_t *mu);
+static int open_socket(dgrm_t *u, int family);
+
+
+/*
+ * XXX TODO:
+ *
+ * There is an almost verbatim copy of this in stream-transport.c
+ * The only differences are the actual address type specifier
+ * prefixes... Combine these and separate the result out to a
+ * new transport-priv.[hc].
+ */
+
+
+static int parse_address(const char *str, int *familyp, char *nodep,
+ size_t nsize, char **servicep, const char **typep)
+{
+ char *node, *service;
+ const char *type;
+ int family;
+ size_t l, nl;
+
+ node = (char *)str;
+
+ if (!strncmp(node, UDP4":", l=UDP4L+1)) {
+ family = AF_INET;
+ type = UDP4;
+ node += l;
+ }
+ else if (!strncmp(node, UDP6":", l=UDP6L+1)) {
+ family = AF_INET6;
+ type = UDP6;
+ node += l;
+ }
+ else if (!strncmp(node, UNXD":", l=UNXDL+1)) {
+ family = AF_UNIX;
+ type = UNXD;
+ node += l;
+ }
+ else {
+ if (node[0] == '[') family = AF_INET6;
+ else if (node[0] == '/') family = AF_UNIX;
+ else if (node[0] == '@') family = AF_UNIX;
+ else family = AF_UNSPEC;
+
+ type = NULL;
+ }
+
+ switch (family) {
+ case AF_INET:
+ service = strrchr(node, ':');
+ if (service == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node;
+ service++;
+
+ case AF_INET6:
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else
+ nl = service - node;
+ service++;
+ break;
+
+ case AF_UNSPEC:
+ if (!strncmp(node, "tcp:", l=4))
+ node += l;
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+ family = AF_INET6;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else {
+ family = AF_INET;
+ nl = service - node;
+ }
+ service++;
+ break;
+
+ case AF_UNIX:
+ service = NULL;
+ nl = strlen(node);
+ }
+
+ if (nl >= nsize) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ strncpy(nodep, node, nl);
+ nodep[nl] = '\0';
+ *servicep = service;
+ *familyp = family;
+ if (typep != NULL)
+ *typep = type;
+
+ return 0;
+}
+
+
+static socklen_t dgrm_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ struct addrinfo *ai, hints;
+ struct sockaddr_un *un;
+ char node[UNIX_PATH_MAX], *port;
+ socklen_t len;
+
+ mrp_clear(&hints);
+
+ if (parse_address(str, &hints.ai_family, node, sizeof(node),
+ &port, typep) < 0)
+ return 0;
+
+ switch (hints.ai_family) {
+ case AF_UNIX:
+ un = &addr->unx;
+ len = MRP_OFFSET(typeof(*un), sun_path) + strlen(node) + 1;
+
+ if (size < len)
+ errno = ENOMEM;
+ else {
+ un->sun_family = AF_UNIX;
+ strncpy(un->sun_path, node, UNIX_PATH_MAX-1);
+ if (un->sun_path[0] == '@')
+ un->sun_path[0] = '\0';
+ }
+
+ /* When binding the socket, we don't need the null at the end */
+ len--;
+
+ break;
+
+ case AF_INET:
+ case AF_INET6:
+ default:
+ if (getaddrinfo(node, port, &hints, &ai) == 0) {
+ if (ai->ai_addrlen <= size) {
+ memcpy(addr, ai->ai_addr, ai->ai_addrlen);
+ len = ai->ai_addrlen;
+ }
+ else
+ len = 0;
+
+ freeaddrinfo(ai);
+ }
+ else
+ len = 0;
+ }
+
+ return len;
+}
+
+
+static int dgrm_open(mrp_transport_t *mu)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+
+ u->sock = -1;
+ u->family = -1;
+
+ return TRUE;
+}
+
+
+static int dgrm_createfrom(mrp_transport_t *mu, void *conn)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ int on;
+ mrp_io_event_t events;
+
+ u->sock = *(int *)conn;
+
+ if (u->sock >= 0) {
+ if (mu->flags & MRP_TRANSPORT_REUSEADDR) {
+ on = 1;
+ setsockopt(u->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ if (mu->flags & MRP_TRANSPORT_NONBLOCK) {
+ on = 1;
+ fcntl(u->sock, F_SETFL, O_NONBLOCK, on);
+ }
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ u->iow = mrp_add_io_watch(u->ml, u->sock, events, dgrm_recv_cb, u);
+
+ if (u->iow != NULL)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_bind(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+
+ if (u->sock != -1 || !u->connected) {
+ if (open_socket(u, addr->any.sa_family))
+ if (bind(u->sock, &addr->any, addrlen) == 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_listen(mrp_transport_t *mt, int backlog)
+{
+ MRP_UNUSED(mt);
+ MRP_UNUSED(backlog);
+
+ return TRUE; /* can be connected to without listening */
+}
+
+
+static void dgrm_close(mrp_transport_t *mu)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+
+ mrp_del_io_watch(u->iow);
+ u->iow = NULL;
+
+ mrp_free(u->ibuf);
+ u->ibuf = NULL;
+ u->isize = 0;
+ u->idata = 0;
+
+ if (u->sock >= 0){
+ close(u->sock);
+ u->sock = -1;
+ }
+}
+
+
+static void dgrm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ dgrm_t *u = (dgrm_t *)user_data;
+ mrp_transport_t *mu = (mrp_transport_t *)u;
+ mrp_sockaddr_t addr;
+ socklen_t addrlen;
+ uint32_t size;
+ ssize_t n;
+ void *data;
+ int old, error;
+
+ MRP_UNUSED(w);
+
+ if (events & MRP_IO_EVENT_IN) {
+ if (u->idata == u->isize) {
+ if (u->isize != 0) {
+ old = u->isize;
+ u->isize *= 2;
+ }
+ else {
+ old = 0;
+ u->isize = DEFAULT_SIZE;
+ }
+ if (!mrp_reallocz(u->ibuf, old, u->isize)) {
+ error = ENOMEM;
+ fatal_error:
+ closed:
+ dgrm_disconnect(mu);
+
+ if (u->evt.closed != NULL)
+ MRP_TRANSPORT_BUSY(mu, {
+ mu->evt.closed(mu, error, mu->user_data);
+ });
+
+ u->check_destroy(mu);
+ return;
+ }
+ }
+
+ if (recv(fd, &size, sizeof(size), MSG_PEEK) != sizeof(size)) {
+ error = EIO;
+ goto fatal_error;
+ }
+
+ size = ntohl(size);
+
+ if (u->isize < size + sizeof(size)) {
+ old = u->isize;
+ u->isize = size + sizeof(size);
+
+ if (!mrp_reallocz(u->ibuf, old, u->isize)) {
+ error = ENOMEM;
+ goto fatal_error;
+ }
+ }
+
+ addrlen = sizeof(addr);
+ n = recvfrom(fd, u->ibuf, size + sizeof(size), 0, &addr.any, &addrlen);
+
+ if (n != (ssize_t)(size + sizeof(size))) {
+ error = n < 0 ? EIO : EPROTO;
+ goto fatal_error;
+ }
+
+ data = u->ibuf + sizeof(size);
+ error = mu->recv_data(mu, data, size, &addr, addrlen);
+
+ if (error)
+ goto fatal_error;
+
+ if (u->check_destroy(mu))
+ return;
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ error = 0;
+ goto closed;
+ }
+}
+
+
+static int open_socket(dgrm_t *u, int family)
+{
+ mrp_io_event_t events;
+ int on;
+ long nb;
+
+ u->sock = socket(family, SOCK_DGRAM, 0);
+
+ if (u->sock != -1) {
+ if (u->flags & MRP_TRANSPORT_REUSEADDR) {
+ on = 1;
+ setsockopt(u->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ if (u->flags & MRP_TRANSPORT_NONBLOCK) {
+ nb = 1;
+ fcntl(u->sock, F_SETFL, O_NONBLOCK, nb);
+ }
+ if (u->flags & MRP_TRANSPORT_CLOEXEC) {
+ on = 1;
+ fcntl(u->sock, F_SETFL, O_CLOEXEC, on);
+ }
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ u->iow = mrp_add_io_watch(u->ml, u->sock, events, dgrm_recv_cb, u);
+
+ if (u->iow != NULL)
+ return TRUE;
+ else {
+ close(u->sock);
+ u->sock = -1;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_connect(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ int on;
+ long nb;
+
+ if (MRP_UNLIKELY(u->family != -1 && u->family != addr->any.sa_family))
+ return FALSE;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, addr->any.sa_family))
+ return FALSE;
+ }
+
+ if (connect(u->sock, &addr->any, addrlen) == 0) {
+ on = 1;
+ setsockopt(u->sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ nb = 1;
+ fcntl(u->sock, F_SETFL, O_NONBLOCK, nb);
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_disconnect(mrp_transport_t *mu)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct sockaddr none = { .sa_family = AF_UNSPEC, };
+
+
+ if (u->connected) {
+ connect(u->sock, &none, sizeof(none));
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int dgrm_send(mrp_transport_t *mu, mrp_msg_t *msg)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct iovec iov[2];
+ void *buf;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (u->connected) {
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (size >= 0) {
+ len = htonl(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = size;
+
+ n = writev(u->sock, iov, 2);
+ mrp_free(buf);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for dgrm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendto(mrp_transport_t *mu, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct iovec iov[2];
+ void *buf;
+ ssize_t size, n;
+ uint32_t len;
+ struct msghdr hdr;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (size >= 0) {
+ len = htonl(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = size;
+
+ hdr.msg_name = addr;
+ hdr.msg_namelen = addrlen;
+ hdr.msg_iov = iov;
+ hdr.msg_iovlen = MRP_ARRAY_SIZE(iov);
+
+ hdr.msg_control = NULL;
+ hdr.msg_controllen = 0;
+ hdr.msg_flags = 0;
+
+ n = sendmsg(u->sock, &hdr, 0);
+ mrp_free(buf);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send failed",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendraw(mrp_transport_t *mu, void *data, size_t size)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ ssize_t n;
+
+ if (u->connected) {
+ n = write(u->sock, data, size);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for dgrm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendrawto(mrp_transport_t *mu, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ ssize_t n;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ n = sendto(u->sock, data, size, 0, &addr->any, addrlen);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send failed",
+ __FUNCTION__);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int senddatato(mrp_transport_t *mu, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ mrp_data_descr_t *type;
+ ssize_t n;
+ void *buf;
+ size_t size, reserve, len;
+ uint32_t *lenp;
+ uint16_t *tagp;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ reserve = sizeof(*lenp) + sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size > 0) {
+ lenp = buf;
+ len = size - sizeof(*lenp);
+ tagp = buf + sizeof(*lenp);
+ *lenp = htobe32(len);
+ *tagp = htobe16(tag);
+
+ if (u->connected)
+ n = send(u->sock, buf, len + sizeof(*lenp), 0);
+ else
+ n = sendto(u->sock, buf, len + sizeof(*lenp), 0, &addr->any,
+ addrlen);
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)(len + sizeof(*lenp)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send"
+ " needs queuing", __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_senddata(mrp_transport_t *mu, void *data, uint16_t tag)
+{
+ if (mu->connected)
+ return senddatato(mu, data, tag, NULL, 0);
+ else
+ return FALSE;
+}
+
+
+static int dgrm_senddatato(mrp_transport_t *mu, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ return senddatato(mu, data, tag, addr, addrlen);
+}
+
+
+static int sendnativeto(mrp_transport_t *mu, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ mrp_typemap_t *map = u->map;
+ void *buf;
+ size_t size, reserve;
+ uint32_t *lenp;
+ ssize_t n;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ reserve = sizeof(*lenp);
+
+ if (mrp_encode_native(data, type_id, reserve, &buf, &size, map) > 0) {
+ lenp = buf;
+ *lenp = htobe32(size - sizeof(*lenp));
+
+ if (u->connected)
+ n = send(u->sock, buf, size, 0);
+ else
+ n = sendto(u->sock, buf, size, 0, &addr->any, addrlen);
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: dgrm-transport send"
+ " needs queuing", __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendnative(mrp_transport_t *mu, void *data, uint32_t type_id)
+{
+ if (mu->connected)
+ return sendnativeto(mu, data, type_id, NULL, 0);
+ else
+ return FALSE;
+}
+
+
+static int dgrm_sendnativeto(mrp_transport_t *mu, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ return sendnativeto(mu, data, type_id, addr, addrlen);
+}
+
+
+static int sendjsonto(mrp_transport_t *mu, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ dgrm_t *u = (dgrm_t *)mu;
+ struct iovec iov[2];
+ const char *s;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (MRP_UNLIKELY(u->sock == -1)) {
+ if (!open_socket(u, ((struct sockaddr *)addr)->sa_family))
+ return FALSE;
+ }
+
+ if (u->connected && (s = mrp_json_object_to_string(msg)) != NULL) {
+ size = strlen(s);
+ len = htobe32(size);
+
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = (char *)s;
+ iov[1].iov_len = size;
+
+ if (u->connected)
+ n = writev(u->sock, iov, 2);
+ else {
+ struct msghdr mh;
+
+ mh.msg_name = &addr->any;
+ mh.msg_namelen = addrlen;
+ mh.msg_iov = iov;
+ mh.msg_iovlen = MRP_ARRAY_SIZE(iov);
+ mh.msg_control = NULL;
+ mh.msg_controllen = 0;
+ mh.msg_flags = 0;
+
+ n = sendmsg(u->sock, &mh, 0);
+ }
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for dgrm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int dgrm_sendjson(mrp_transport_t *mu, mrp_json_t *msg)
+{
+ if (mu->connected)
+ return sendjsonto(mu, msg, NULL, 0);
+ else
+ return FALSE;
+}
+
+
+static int dgrm_sendjsonto(mrp_transport_t *mu, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ return sendjsonto(mu, msg, addr, addrlen);
+}
+
+
+MRP_REGISTER_TRANSPORT(udp4, UDP4, dgrm_t, dgrm_resolve,
+ dgrm_open, dgrm_createfrom, dgrm_close, NULL,
+ dgrm_bind, dgrm_listen, NULL,
+ dgrm_connect, dgrm_disconnect,
+ dgrm_send, dgrm_sendto,
+ dgrm_sendraw, dgrm_sendrawto,
+ dgrm_senddata, dgrm_senddatato,
+ NULL, NULL,
+ dgrm_sendnative, dgrm_sendnativeto,
+ dgrm_sendjson, dgrm_sendjsonto);
+
+MRP_REGISTER_TRANSPORT(udp6, UDP6, dgrm_t, dgrm_resolve,
+ dgrm_open, dgrm_createfrom, dgrm_close, NULL,
+ dgrm_bind, dgrm_listen, NULL,
+ dgrm_connect, dgrm_disconnect,
+ dgrm_send, dgrm_sendto,
+ dgrm_sendraw, dgrm_sendrawto,
+ dgrm_senddata, dgrm_senddatato,
+ NULL, NULL,
+ dgrm_sendnative, dgrm_sendnativeto,
+ dgrm_sendjson, dgrm_sendjsonto);
+
+MRP_REGISTER_TRANSPORT(unxdgrm, UNXD, dgrm_t, dgrm_resolve,
+ dgrm_open, dgrm_createfrom, dgrm_close, NULL,
+ dgrm_bind, dgrm_listen, NULL,
+ dgrm_connect, dgrm_disconnect,
+ dgrm_send, dgrm_sendto,
+ dgrm_sendraw, dgrm_sendrawto,
+ dgrm_senddata, dgrm_senddatato,
+ NULL, NULL,
+ dgrm_sendnative, dgrm_sendnativeto,
+ dgrm_sendjson, dgrm_sendjsonto);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <errno.h>
+
+#include <Ecore.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+
+typedef struct {
+ int ecore;
+} ecore_glue_t;
+
+
+typedef struct {
+ Ecore_Fd_Handler *ec_io;
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ mrp_io_event_t mask;
+ void *user_data;
+ void *glue_data;
+} io_t;
+
+
+typedef struct {
+ Ecore_Timer *ec_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} tmr_t;
+
+
+typedef struct {
+ Ecore_Timer *ec_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} dfr_t;
+
+
+#define D(fmt, args...) do { \
+ printf("* [%s]: "fmt"\n", __FUNCTION__ , ## args); \
+ } while (0)
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+static void del_io(void *glue_data, void *id);
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_timer(void *glue_data, void *id);
+static void mod_timer(void *glue_data, void *id, unsigned int msecs);
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_defer(void *glue_data, void *id);
+static void mod_defer(void *glue_data, void *id, int enabled);
+
+
+static int io_check_hup(int fd)
+{
+ char buf[1];
+ int saved_errno, n;
+
+ saved_errno = errno;
+ n = recv(fd, buf, 1, MSG_PEEK);
+ errno = saved_errno;
+
+ return (n == 0);
+}
+
+
+static Eina_Bool io_cb(void *user_data, Ecore_Fd_Handler *ec_io)
+{
+ io_t *io = (io_t *)user_data;
+ mrp_io_event_t events = MRP_IO_EVENT_NONE;
+ int fd = ecore_main_fd_handler_fd_get(ec_io);
+
+ if (ecore_main_fd_handler_active_get(ec_io, ECORE_FD_READ))
+ events |= MRP_IO_EVENT_IN;
+ if (ecore_main_fd_handler_active_get(ec_io, ECORE_FD_WRITE))
+ events |= MRP_IO_EVENT_OUT;
+ if (ecore_main_fd_handler_active_get(ec_io, ECORE_FD_ERROR))
+ events |= MRP_IO_EVENT_ERR;
+
+#if 0 /* Pfoof... ecore cannot monitor for HUP. */
+ if (ecore_main_fd_handler_active_get(ec_io, NO_SUCH_ECORE_EVENT))
+ events |= MRP_IO_EVENT_HUP;
+#else
+ if ((io->mask & MRP_IO_EVENT_HUP) && (events & MRP_IO_EVENT_IN))
+ if (io_check_hup(fd))
+ events |= MRP_IO_EVENT_HUP;
+#endif
+
+ io->cb(io->glue_data, io, fd, events, io->user_data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ int mask = 0;
+ io_t *io;
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io != NULL) {
+ if (events & MRP_IO_EVENT_IN) mask |= ECORE_FD_READ;
+ if (events & MRP_IO_EVENT_OUT) mask |= ECORE_FD_WRITE;
+#if 0 /* Pfoof... ecore cannot monitor for HUP. */
+ if (events & MRP_IO_EVENT_HUP) mask |= NO_SUCH_ECORE_EVENT;
+#endif
+ if (events & MRP_IO_EVENT_ERR) mask |= ECORE_FD_ERROR;
+
+ io->mask = events;
+ io->ec_io = ecore_main_fd_handler_add(fd, mask, io_cb, io, NULL, NULL);
+
+ if (io->ec_io != NULL) {
+ io->cb = cb;
+ io->user_data = user_data;
+ io->glue_data = glue_data;
+
+ return io;
+ }
+ else
+ mrp_free(io);
+ }
+
+ return NULL;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ io_t *io = (io_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ ecore_main_fd_handler_del(io->ec_io);
+ mrp_free(io);
+}
+
+
+static Eina_Bool timer_cb(void *user_data)
+{
+ tmr_t *t = (tmr_t *)user_data;
+
+ t->cb(t->glue_data, t, t->user_data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ double interval = (1.0 * msecs) / 1000.0;
+ tmr_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ t->ec_t = ecore_timer_add(interval, timer_cb, t);
+
+ if (t->ec_t != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->glue_data = glue_data;
+
+ return t;
+ }
+ else
+ mrp_free(t);
+ }
+
+ return NULL;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ tmr_t *t = (tmr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ ecore_timer_del(t->ec_t);
+ mrp_free(t);
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ tmr_t *t = (tmr_t *)id;
+ double interval = (1.0 * msecs) / 1000.0;
+
+ MRP_UNUSED(glue_data);
+
+ if (t != NULL) {
+ /*
+ * Notes:
+ * ecore_timer_reset needs to be called after updating the
+ * interval. Otherwise the change will not be effective.
+ *
+ * In practice, since we use mod_timer to update our super-
+ * loop briding timer to latch at the next mrp_mainloop_t
+ * timer moment, this could cause our mainloop to hang
+ * right in the beginning, since the bridging timer has an
+ * initial interval of (uint32_t)-1 (no next event). If we
+ * have no other event sources than timers this would cause
+ * our mainloop to hang indefinitely. If we have other event
+ * sources (I/O or signals), the mainloop would hang till a
+ * non-timer event comes in.
+ */
+ ecore_timer_interval_set(t->ec_t, interval);
+ ecore_timer_reset(t->ec_t);
+ }
+}
+
+
+static Eina_Bool defer_cb(void *user_data)
+{
+ dfr_t *d = (dfr_t *)user_data;
+
+ d->cb(d->glue_data, d, d->user_data);
+
+ return ECORE_CALLBACK_RENEW;
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ dfr_t *d;
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d != NULL) {
+ d->ec_t = ecore_timer_add(0.0, defer_cb, d);
+
+ if (d->ec_t != NULL) {
+ d->cb = cb;
+ d->user_data = user_data;
+ d->glue_data = glue_data;
+
+ return d;
+ }
+ else
+ mrp_free(d);
+ }
+
+ return NULL;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ ecore_timer_del(d->ec_t);
+ mrp_free(d);
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (enabled)
+ ecore_timer_thaw(d->ec_t);
+ else
+ ecore_timer_freeze(d->ec_t);
+}
+
+
+static void unregister(void *data)
+{
+ MRP_UNUSED(data);
+}
+
+
+static mrp_superloop_ops_t ecore_ops = {
+ .add_io = add_io,
+ .del_io = del_io,
+ .add_timer = add_timer,
+ .del_timer = del_timer,
+ .mod_timer = mod_timer,
+ .add_defer = add_defer,
+ .del_defer = del_defer,
+ .mod_defer = mod_defer,
+ .unregister = unregister,
+};
+
+
+static const char *ecore_glue = "murphy-ecore-glue";
+static mrp_mainloop_t *ecore_ml;
+
+
+int mrp_mainloop_register_with_ecore(mrp_mainloop_t *ml)
+{
+ return mrp_set_superloop(ml, &ecore_ops, (void *)ecore_glue);
+}
+
+
+int mrp_mainloop_unregister_from_ecore(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+mrp_mainloop_t *mrp_mainloop_ecore_get(void)
+{
+ if (ecore_ml == NULL) {
+ ecore_init();
+
+ ecore_ml = mrp_mainloop_create();
+
+ if (ecore_ml != NULL) {
+ if (!mrp_mainloop_register_with_ecore(ecore_ml)) {
+ mrp_mainloop_destroy(ecore_ml);
+ ecore_ml = NULL;
+ }
+ }
+ }
+
+ return ecore_ml;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_ECORE_H__
+#define __MURPHY_ECORE_H__
+
+MRP_CDECL_BEGIN
+
+/** Register the given murphy mainloop with the ecore mainloop. */
+int mrp_mainloop_register_with_ecore(mrp_mainloop_t *ml);
+
+/** Unrgister the given murphy mainloop from the ecore mainloop. */
+int mrp_mainloop_unregister_from_ecore(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the ecore mainloop. */
+mrp_mainloop_t *mrp_mainloop_ecore_get(void);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_ECORE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012 - 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+const char *mrp_env_config_key(const char *config, const char *key)
+{
+ const char *beg;
+ int len;
+
+ if (config != NULL) {
+ len = strlen(key);
+
+ beg = config;
+ while (beg != NULL) {
+ beg = strstr(beg, key);
+
+ if (beg != NULL) {
+ if ((beg == config || beg[-1] == ':') &&
+ (beg[len] == '=' || beg[len] == ':' || beg[len] == '\0'))
+ return (beg[len] == '=' ? beg + len + 1 : "");
+ else
+ beg++;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+int32_t mrp_env_config_int32(const char *cfg, const char *key, int32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = mrp_env_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+uint32_t mrp_env_config_uint32(const char *cfg, const char *key,
+ uint32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = mrp_env_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+int mrp_env_config_bool(const char *config, const char *key, bool defval)
+{
+ const char *v;
+
+ v = mrp_env_config_key(config, key);
+
+ if (v != NULL) {
+ if (*v) {
+ if ((!strncasecmp(v, "false", 5) && (v[5] == ':' || !v[5])) ||
+ (!strncasecmp(v, "true" , 4) && (v[4] == ':' || !v[4])))
+ return (v[0] == 't' || v[0] == 'T');
+ if ((!strncasecmp(v, "no" , 2) && (v[2] == ':' || !v[2])) ||
+ (!strncasecmp(v, "yes", 3) && (v[3] == ':' || !v[3])))
+ return (v[0] == 'y' || v[0] == 'Y');
+ if ((!strncasecmp(v, "on" , 2) && (v[2] == ':' || !v[2])) ||
+ (!strncasecmp(v, "off", 3) && (v[3] == ':' || !v[3])))
+ return (v[1] == 'n' || v[1] == 'N');
+ }
+ else if (*v == '\0')
+ return !defval; /* hmm... is this right */
+ }
+
+ return defval;
+}
+
+
+int mrp_env_config_string(const char *cfg, const char *key,
+ const char *defval, char *buf, size_t size)
+{
+ const char *v;
+ char *end;
+ int len;
+
+ v = mrp_env_config_key(cfg, key);
+
+ if (v == NULL)
+ v = defval;
+
+ end = strchr(v, ':');
+
+ if (end != NULL)
+ len = end - v;
+ else
+ len = strlen(v);
+
+ len = snprintf(buf, size, "%*.*s", len, len, v);
+
+ if (len >= (int)size - 1)
+ buf[size - 1] = '\0';
+
+ return len;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 - 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_ENV_H__
+#define __MURPHY_ENV_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/** Extract the value for the given key from the config string. */
+const char *mrp_env_config_key(const char *config, const char *key);
+
+/** Extract an int32 value for key, returning defval if not found. */
+int32_t mrp_env_config_int32(const char *cfg, const char *key, int32_t defval);
+
+/** Extract an uint32 value for key, returning defval if not found. */
+uint32_t mrp_env_config_uint32(const char *cfg, const char *key,
+ uint32_t defval);
+
+/** Extract a boolean value for key, returning defval if not found. */
+bool mrp_env_config_bool(const char *config, const char *key, bool defval);
+
+/** Extract a string value for key to buf (of size), defaulting to defval. */
+int mrp_env_config_string(const char *cfg, const char *key,
+ const char *defval, char *buf, size_t size);
+
+#endif /* __MURPHY_ENV_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+#include <dirent.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/file-utils.h>
+
+
+static const char *translate_glob(const char *pattern, char *glob, size_t size)
+{
+ MRP_UNUSED(glob);
+ MRP_UNUSED(size);
+
+ /* XXX FIXME: translate pattern to glob-like */
+
+ return pattern;
+}
+
+
+static inline mrp_dirent_type_t dirent_type(mode_t mode)
+{
+#define MAP_TYPE(x, y) if (S_IS##x(mode)) return MRP_DIRENT_##y
+
+ MAP_TYPE(REG, REG);
+ MAP_TYPE(DIR, DIR);
+ MAP_TYPE(LNK, LNK);
+ MAP_TYPE(CHR, CHR);
+ MAP_TYPE(BLK, BLK);
+ MAP_TYPE(FIFO, FIFO);
+ MAP_TYPE(SOCK, SOCK);
+
+ return MRP_DIRENT_UNKNOWN;
+
+#undef MAP_TYPE
+}
+
+
+int mrp_scan_dir(const char *path, const char *pattern, mrp_dirent_type_t mask,
+ mrp_scan_dir_cb_t cb, void *user_data)
+{
+ DIR *dp;
+ struct dirent *de;
+ struct stat st;
+ regex_t regexp;
+ const char *prefix;
+ char glob[1024], file[PATH_MAX];
+ size_t size;
+ int stop;
+ mrp_dirent_type_t type;
+
+ if ((dp = opendir(path)) == NULL)
+ return FALSE;
+
+ if (pattern != NULL) {
+ prefix = MRP_PATTERN_GLOB;
+ size = sizeof(MRP_PATTERN_GLOB) - 1;
+
+ if (!strncmp(pattern, prefix, size)) {
+ pattern = translate_glob(pattern + size, glob, sizeof(glob));
+
+ if (pattern == NULL) {
+ closedir(dp);
+ return FALSE;
+ }
+ }
+ else {
+ prefix = MRP_PATTERN_REGEX;
+ size = sizeof(MRP_PATTERN_REGEX) - 1;
+
+ if (!strncmp(pattern, prefix, size))
+ pattern += size;
+ }
+
+ if (regcomp(®exp, pattern, REG_EXTENDED | REG_NOSUB) != 0) {
+ closedir(dp);
+ return FALSE;
+ }
+ }
+
+ stop = FALSE;
+ while ((de = readdir(dp)) != NULL && !stop) {
+ if (pattern != NULL && regexec(®exp, de->d_name, 0, NULL, 0) != 0)
+ continue;
+
+ snprintf(file, sizeof(file), "%s/%s", path, de->d_name);
+
+ if (((mask & MRP_DIRENT_LNK ? lstat : stat))(file, &st) != 0)
+ continue;
+
+ type = dirent_type(st.st_mode);
+ if (!(type & mask))
+ continue;
+
+ stop = !cb(de->d_name, type, user_data);
+ }
+
+
+ closedir(dp);
+ if (pattern != NULL)
+ regfree(®exp);
+
+ return TRUE;
+}
+
+
+int mrp_find_file(const char *file, const char **dirs, int mode, char *buf,
+ size_t size)
+{
+ const char *dir;
+ char path[PATH_MAX];
+ int i;
+
+ if (file[0] != '/') {
+ if (dirs != NULL) {
+ for (dir = dirs[i=0]; dir != NULL; dir = dirs[++i]) {
+ if (snprintf(path, sizeof(path), "%s/%s",
+ dir, file) >= (ssize_t)sizeof(path))
+ continue;
+
+ if (access(path, mode) == 0) {
+ file = path;
+ goto found;
+ }
+ }
+ }
+
+ if (snprintf(path, sizeof(path), "./%s", file) < (ssize_t)sizeof(path)) {
+ if (access(path, mode) == 0) {
+ file = path;
+ goto found;
+ }
+ }
+ }
+ else {
+ if (access(file, mode) == 0)
+ goto found;
+ }
+
+ errno = ENOENT;
+ return -1;
+
+ found:
+ if (buf != NULL && size > 0)
+ snprintf(buf, size, "%s", file);
+
+ return 0;
+}
+
+
+int mrp_mkdir(const char *path, mode_t mode)
+{
+ const char *p;
+ char *q, buf[PATH_MAX];
+ int n, undo[PATH_MAX / 2];
+ struct stat st;
+
+ if (path == NULL || path[0] == '\0') {
+ errno = path ? EINVAL : EFAULT;
+ return -1;
+ }
+
+ /*
+ * Notes:
+ * Our directory creation algorithm logic closely resembles what
+ * 'mkdir -p' does. We simply walk the given path component by
+ * component, testing if each one exist. If an existing one is
+ * not a directory we bail out. Missing ones we try to create with
+ * the given mode, bailing out if we fail.
+ *
+ * Unlike 'mkdir -p' whenever we fail we clean up by removing
+ * all directories we have created (or at least we try).
+ *
+ * Similarly to 'mkdir -p' we don't try to be overly 'smart' about
+ * the path we're handling. Especially we never try to treat '..'
+ * in any special way. This is very much intentional and the idea
+ * is to let the caller try to create a full directory hierarchy
+ * atomically, either succeeeding creating the full hierarchy, or
+ * none of it. To see the consequences of these design choices,
+ * consider what are the possible outcomes of a call like
+ *
+ * mrp_mkdir("/home/kli/bin/../sbin/../scripts/../etc/../doc", 0755);
+ */
+
+ p = path;
+ q = buf;
+ n = 0;
+ while (1) {
+ if (q - buf >= (ptrdiff_t)sizeof(buf) - 1) {
+ errno = ENAMETOOLONG;
+ goto cleanup;
+ }
+
+ if (*p && *p != '/') {
+ *q++ = *p++;
+ continue;
+ }
+
+ *q = '\0';
+
+ mrp_debug("checking/creating '%s'...", buf);
+
+ if (q != buf) {
+ if (stat(buf, &st) < 0) {
+ if (errno != ENOENT)
+ goto cleanup;
+
+ if (mkdir(buf, mode) < 0)
+ goto cleanup;
+
+ undo[n++] = q - buf;
+ }
+ else {
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ goto cleanup;
+ }
+ }
+ }
+
+ while (*p == '/')
+ p++;
+
+ if (!*p)
+ break;
+
+ *q++ = '/';
+ }
+
+ return 0;
+
+ cleanup:
+ while (--n >= 0) {
+ buf[undo[n]] = '\0';
+ mrp_debug("cleaning up '%s'...", buf);
+ rmdir(buf);
+ }
+
+ return -1;
+}
+
+
+char *mrp_normalize_path(char *buf, size_t size, const char *path)
+{
+ const char *p;
+ char *q;
+ int n, back[PATH_MAX / 2];
+
+ if (path == NULL)
+ return NULL;
+
+ if (*path == '\0') {
+ if (size > 0) {
+ *buf = '\0';
+ return buf;
+ }
+ else {
+ overflow:
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+ }
+
+ p = path;
+ q = buf;
+ n = 0;
+
+ while (*p) {
+ if (q - buf + 1 >= (ptrdiff_t)size)
+ goto overflow;
+
+ if (*p == '/') {
+ back[n++] = q - buf;
+ *q++ = *p++;
+
+ skip_slashes:
+ while (*p == '/')
+ p++;
+
+ /*
+ * '.'
+ *
+ * We skip './' including all trailing slashes. Note that
+ * the code is arranged so that whenever we skip trailing
+ * slashes, we automatically check and skip also trailing
+ * './'s too...
+ */
+
+ if (p[0] == '.' && (p[1] == '/' || p[1] == '\0')) {
+ p++;
+ goto skip_slashes;
+ }
+
+ /*
+ * '..'
+ *
+ * We throw away the last incorrectly saved backtracking
+ * point (we saved it for this '../'). Then if we can still
+ * backtrack, we do so. Otherwise (we're at the beginning
+ * already), if the path is absolute, we just ignore the
+ * current '../' (can't go above '/'), otherwise we keep it
+ * for relative pathes.
+ */
+
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
+ n--; /* throw away */
+ if (n > 0) { /* can still backtrack */
+ if (back[n - 1] >= 0) /* previous not a '..' */
+ q = buf + back[n - 1] + 1;
+ }
+ else {
+ if (q > buf && buf[0] == '/') /* for absolute pathes */
+ q = buf + 1; /* reset to start */
+ else { /* for relative pathes */
+ if (q - buf + 4 >= (ptrdiff_t)size)
+ goto overflow;
+
+ q[0] = '.'; /* append this '..' */
+ q[1] = '.';
+ q[2] = '/';
+ q += 3;
+ back[n] = -1; /* block backtracking */
+ }
+ }
+
+ p += 2;
+ goto skip_slashes;
+ }
+ }
+ else
+ *q++ = *p++;
+ }
+
+ /*
+ * finally for other than '/' align trailing slashes
+ */
+
+ if (p > path + 1 && p[-1] != '/')
+ if (q > buf + 1 && q[-1] == '/')
+ q--;
+
+ *q = '\0';
+
+ return buf;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_FILEUTILS_H__
+#define __MURPHY_FILEUTILS_H__
+
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+/*
+ * Routines for scanning a directory for matching entries.
+ */
+
+/** Directory entry types. */
+typedef enum {
+ MRP_DIRENT_UNKNOWN = 0, /* unknown */
+ MRP_DIRENT_NONE = 0x00, /* unknown */
+ MRP_DIRENT_FIFO = 0x01, /* FIFO */
+ MRP_DIRENT_CHR = 0x02, /* character device */
+ MRP_DIRENT_DIR = 0x04, /* directory */
+ MRP_DIRENT_BLK = 0x08, /* block device */
+ MRP_DIRENT_REG = 0x10, /* regular file */
+ MRP_DIRENT_LNK = 0x20, /* symbolic link */
+ MRP_DIRENT_SOCK = 0x40, /* socket */
+ MRP_DIRENT_ANY = 0xff, /* mask of all types */
+} mrp_dirent_type_t;
+
+
+#define MRP_PATTERN_GLOB "glob:" /* a globbing pattern */
+#define MRP_PATTERN_REGEX "regex:" /* a regexp pattern */
+
+
+/** Directory scanning callback type. */
+typedef int (*mrp_scan_dir_cb_t)(const char *entry, mrp_dirent_type_t type,
+ void *user_data);
+
+/** Scan a directory, calling cb with all matching entries. */
+int mrp_scan_dir(const char *path, const char *pattern, mrp_dirent_type_t mask,
+ mrp_scan_dir_cb_t cb, void *user_data);
+
+/** Do an #include-like search for the given file among the given dirs. */
+int mrp_find_file(const char *file, const char **dirs, int mode, char *buf,
+ size_t size);
+
+/** Create a directory, creating leading path as necessary. */
+int mrp_mkdir(const char *path, mode_t mode);
+
+
+/** Parse a path into a normalized form, removing ../'s and ./'s. */
+char *mrp_normalize_path(char *buf, size_t size, const char *path);
+
+#endif /* __MURPHY_FILEUTILS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <endian.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/fragbuf.h>
+
+struct mrp_fragbuf_s {
+ void *data; /* actual data buffer */
+ int size; /* size of the buffer */
+ int used; /* amount of data in the bufer */
+ int framed : 1; /* whether data is framed */
+};
+
+
+static void *fragbuf_ensure(mrp_fragbuf_t *buf, size_t size)
+{
+ int more;
+
+ if (buf->size - buf->used < (int)size) {
+ more = size - (buf->size - buf->used);
+
+ if (mrp_reallocz(buf->data, buf->size, buf->size + more) == NULL)
+ return NULL;
+ else
+ buf->size += more;
+ }
+
+ return buf->data + buf->used;
+}
+
+
+size_t mrp_fragbuf_used(mrp_fragbuf_t *buf)
+{
+ return buf->used;
+}
+
+
+size_t mrp_fragbuf_missing(mrp_fragbuf_t *buf)
+{
+ void *ptr;
+ int offs;
+ uint32_t size;
+
+ if (!buf->framed || !buf->used)
+ return 0;
+
+ /* find the last frame */
+ ptr = buf->data;
+ offs = 0;
+ while (offs < buf->used) {
+ size = be32toh(*(uint32_t *)ptr);
+ offs += sizeof(size) + size;
+ }
+
+ /* get the amount of data missing */
+ return offs - buf->used;
+}
+
+
+int fragbuf_init(mrp_fragbuf_t *buf, int framed, int pre_alloc)
+{
+ buf->data = NULL;
+ buf->size = 0;
+ buf->used = 0;
+ buf->framed = framed;
+
+ if (pre_alloc <= 0 || fragbuf_ensure(buf, pre_alloc))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+mrp_fragbuf_t *mrp_fragbuf_create(int framed, size_t pre_alloc)
+{
+ mrp_fragbuf_t *buf;
+
+ buf = mrp_allocz(sizeof(*buf));
+
+ if (buf != NULL) {
+ if (fragbuf_init(buf, framed, pre_alloc))
+ return buf;
+
+ mrp_free(buf);
+ }
+
+ return NULL;
+}
+
+
+void mrp_fragbuf_reset(mrp_fragbuf_t *buf)
+{
+ if (buf != NULL) {
+ mrp_free(buf->data);
+ buf->data = NULL;
+ buf->size = 0;
+ buf->used = 0;
+ }
+}
+
+void mrp_fragbuf_destroy(mrp_fragbuf_t *buf)
+{
+ if (buf != NULL) {
+ mrp_free(buf->data);
+ mrp_free(buf);
+ }
+}
+
+
+void *mrp_fragbuf_alloc(mrp_fragbuf_t *buf, size_t size)
+{
+ void *ptr;
+
+ ptr = fragbuf_ensure(buf, size);
+
+ if (ptr != NULL)
+ buf->used += size;
+
+ return ptr;
+}
+
+
+int mrp_fragbuf_trim(mrp_fragbuf_t *buf, void *ptr, size_t osize, size_t nsize)
+{
+ size_t diff;
+
+ if (ptr + osize == buf->data + buf->used) { /* looks like the last alloc */
+ if (nsize <= osize) {
+ diff = osize - nsize;
+ buf->used -= diff;
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_fragbuf_push(mrp_fragbuf_t *buf, void *data, size_t size)
+{
+ void *ptr;
+
+ ptr = fragbuf_ensure(buf, size);
+
+ if (ptr != NULL) {
+ memcpy(ptr, data, size);
+ buf->used += size;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_fragbuf_pull(mrp_fragbuf_t *buf, void **datap, size_t *sizep)
+{
+ void *data;
+ uint32_t size;
+
+ if (buf == NULL || buf->used <= 0)
+ return FALSE;
+
+ if (MRP_UNLIKELY(*datap &&
+ (*datap < buf->data || *datap > buf->data + buf->used))) {
+ mrp_log_warning("%s(): *** looks like we're called with an unreset "
+ "datap pointer... ***", __FUNCTION__);
+ }
+
+ /* start of iteration */
+ if (*datap == NULL) {
+ if (!buf->framed) {
+ *datap = buf->data;
+ *sizep = buf->used;
+
+ return TRUE;
+ }
+ else {
+ if (buf->used < (int)sizeof(size))
+ return FALSE;
+
+ size = be32toh(*(uint32_t *)buf->data);
+
+ if (buf->used >= (int)(sizeof(size) + size)) {
+ *datap = buf->data + sizeof(size);
+ *sizep = size;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ }
+ /* continue iteration */
+ else {
+ if (!buf->framed) {
+ data = *datap + *sizep;
+
+ if (buf->data <= data && data < buf->data + buf->used) {
+ memmove(buf->data, data, buf->used - (data - buf->data));
+ buf->used -= (data - buf->data);
+
+ *datap = buf->data;
+ *sizep = buf->used;
+
+ return TRUE;
+ }
+ else {
+ if (data == buf->data + buf->used)
+ buf->used = 0;
+
+ return FALSE;
+ }
+ }
+ else {
+ if (*datap != buf->data + sizeof(size))
+ return FALSE;
+
+ size = be32toh(*(uint32_t *)buf->data);
+
+ if ((int)(size + sizeof(size)) <= buf->used) {
+ memmove(buf->data, buf->data + size + sizeof(size),
+ buf->used - (size + sizeof(size)));
+ buf->used -= size + sizeof(size);
+ }
+ else
+ return FALSE;
+
+ if (buf->used <= (int)sizeof(size))
+ return FALSE;
+
+ size = be32toh(*(uint32_t *)buf->data);
+ data = buf->data + sizeof(size);
+
+ if (buf->used >= (int)(size + sizeof(size))) {
+ *datap = data;
+ *sizep = size;
+
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_FRAGBUF_H__
+#define __MURPHY_FRAGBUF_H__
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * Fragment collector buffers.
+ *
+ * As the name implies, a fragment collector buffer can be used
+ * to collect message fragments and reassemble messages that were
+ * transmitted in arbitrary pieces.
+ *
+ * Messages are expected to be transmitted in frames where each
+ * frame simply consist of a 32-bit message size followed by
+ * the actual message data. On the sending side you can simply
+ * send each message prefixed with its size. On the receiving side
+ * you keep feeding the received chunks of data to a fragment
+ * collector buffer (using mrp_fragbuf_push). After each chunk you
+ * can iterate through the fully reassembled messages (by calling
+ * mrp_fragbuf_pull until it returns FALSE). Messages are removed
+ * automatically from the collector buffer as you iterate through
+ * them.
+ *
+ * You can also create a collector buffer in frameless mode. Such a
+ * buffer will always return immediately all available data as you
+ * iterate through it.
+ */
+
+/** Buffer for collecting fragments of (framed or unframed) message data. */
+typedef struct mrp_fragbuf_s mrp_fragbuf_t;
+
+/** Initialize the given fragment collector buffer. */
+mrp_fragbuf_t *mrp_fragbuf_create(int framed, size_t pre_alloc);
+
+/** Initialize the given data collector buffer. */
+int mrp_fragbuf_init(mrp_fragbuf_t *buf, int framed, size_t pre_alloc);
+
+/** Reset the given data collector buffer. */
+void mrp_fragbuf_reset(mrp_fragbuf_t *buf);
+
+/** Destroy the given data collector buffer, freeing all associated memory. */
+void mrp_fragbuf_destroy(mrp_fragbuf_t *buf);
+
+/** Return the amount of buffer space currently in used in th buffer. */
+size_t mrp_fragbuf_used(mrp_fragbuf_t *buf);
+
+/** Return the amount of bytes missing from the last message. */
+size_t mrp_fragbuf_missing(mrp_fragbuf_t *buf);
+
+/** Allocate a buffer of the given size from the buffer. */
+void *mrp_fragbuf_alloc(mrp_fragbuf_t *buf, size_t size);
+
+/** Trim the last allocation to nsize bytes. */
+int mrp_fragbuf_trim(mrp_fragbuf_t *buf, void *ptr, size_t osize, size_t nsize);
+
+/** Append the given data to the buffer. */
+int mrp_fragbuf_push(mrp_fragbuf_t *buf, void *data, size_t size);
+
+/** Iterate through the given buffer, pulling and freeing assembled messages. */
+int mrp_fragbuf_pull(mrp_fragbuf_t *buf, void **data, size_t *size);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_FRAGBUF_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+
+typedef struct {
+ GMainLoop *gml;
+} glib_glue_t;
+
+
+typedef struct {
+ GIOChannel *gl_ioc;
+ guint gl_iow;
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ mrp_io_event_t mask;
+ void *user_data;
+ void *glue_data;
+} io_t;
+
+
+typedef struct {
+ guint gl_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} tmr_t;
+
+
+typedef struct {
+ guint gl_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} dfr_t;
+
+
+#define D(fmt, args...) do { \
+ printf("* [%s]: "fmt"\n", __FUNCTION__ , ## args); \
+ } while (0)
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+static void del_io(void *glue_data, void *id);
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_timer(void *glue_data, void *id);
+static void mod_timer(void *glue_data, void *id, unsigned int msecs);
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_defer(void *glue_data, void *id);
+static void mod_defer(void *glue_data, void *id, int enabled);
+
+
+static gboolean io_cb(GIOChannel *ioc, GIOCondition cond, gpointer user_data)
+{
+ io_t *io = (io_t *)user_data;
+ mrp_io_event_t events = MRP_IO_EVENT_NONE;
+ int fd = g_io_channel_unix_get_fd(ioc);
+
+ if (cond & G_IO_IN)
+ events |= MRP_IO_EVENT_IN;
+ if (cond & G_IO_OUT)
+ events |= MRP_IO_EVENT_OUT;
+ if (cond & G_IO_ERR)
+ events |= MRP_IO_EVENT_ERR;
+ if (cond & G_IO_HUP)
+ events |= MRP_IO_EVENT_HUP;
+
+ io->cb(io->glue_data, io, fd, events, io->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ GIOCondition mask = 0;
+ GIOChannel *ioc;
+ io_t *io;
+
+ ioc = g_io_channel_unix_new(fd);
+
+ if (ioc == NULL)
+ return NULL;
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io != NULL) {
+ if (events & MRP_IO_EVENT_IN ) mask |= G_IO_IN;
+ if (events & MRP_IO_EVENT_OUT) mask |= G_IO_OUT;
+ if (events & MRP_IO_EVENT_HUP) mask |= G_IO_HUP;
+ if (events & MRP_IO_EVENT_ERR) mask |= G_IO_ERR;
+
+ io->mask = events;
+ io->gl_ioc = ioc;
+ io->gl_iow = g_io_add_watch(ioc, mask, io_cb, io);
+
+ if (io->gl_iow != 0) {
+ io->cb = cb;
+ io->user_data = user_data;
+ io->glue_data = glue_data;
+
+ return io;
+ }
+ else {
+ g_io_channel_unref(ioc);
+ mrp_free(io);
+ }
+ }
+
+ return NULL;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ io_t *io = (io_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ g_source_remove(io->gl_iow);
+ g_io_channel_unref(io->gl_ioc);
+ mrp_free(io);
+}
+
+
+static gboolean timer_cb(gpointer user_data)
+{
+ tmr_t *t = (tmr_t *)user_data;
+
+ t->cb(t->glue_data, t, t->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ tmr_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ t->gl_t = g_timeout_add(msecs, timer_cb, t);
+
+ if (t->gl_t != 0) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->glue_data = glue_data;
+
+ return t;
+ }
+ else
+ mrp_free(t);
+ }
+
+ return NULL;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ tmr_t *t = (tmr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ g_source_remove(t->gl_t);
+ mrp_free(t);
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ tmr_t *t = (tmr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (t != NULL) {
+ g_source_remove(t->gl_t);
+ t->gl_t = g_timeout_add(msecs, timer_cb, t);
+ }
+}
+
+
+static gboolean defer_cb(void *user_data)
+{
+ dfr_t *d = (dfr_t *)user_data;
+
+ d->cb(d->glue_data, d, d->user_data);
+
+ return TRUE;
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ dfr_t *d;
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d != NULL) {
+ d->gl_t = g_timeout_add(0, defer_cb, d);
+
+ if (d->gl_t != 0) {
+ d->cb = cb;
+ d->user_data = user_data;
+ d->glue_data = glue_data;
+
+ return d;
+ }
+ else
+ mrp_free(d);
+ }
+
+ return NULL;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (d->gl_t != 0)
+ g_source_remove(d->gl_t);
+
+ mrp_free(d);
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ dfr_t *d = (dfr_t *)id;
+
+ MRP_UNUSED(glue_data);
+
+ if (enabled && !d->gl_t)
+ d->gl_t = g_timeout_add(0, defer_cb, d);
+ else if (!enabled && d->gl_t) {
+ g_source_remove(d->gl_t);
+ d->gl_t = 0;
+ }
+}
+
+
+static void unregister(void *data)
+{
+ glib_glue_t *glue = (glib_glue_t *)data;
+
+ g_main_loop_unref(glue->gml);
+
+ mrp_free(glue);
+}
+
+
+static mrp_superloop_ops_t glib_ops = {
+ .add_io = add_io,
+ .del_io = del_io,
+ .add_timer = add_timer,
+ .del_timer = del_timer,
+ .mod_timer = mod_timer,
+ .add_defer = add_defer,
+ .del_defer = del_defer,
+ .mod_defer = mod_defer,
+ .unregister = unregister,
+};
+
+
+int mrp_mainloop_register_with_glib(mrp_mainloop_t *ml, GMainLoop *gml)
+{
+ glib_glue_t *glue;
+
+ glue = mrp_allocz(sizeof(*glue));
+
+ if (glue != NULL) {
+ glue->gml = g_main_loop_ref(gml);
+
+ if (mrp_set_superloop(ml, &glib_ops, glue))
+ return TRUE;
+ else {
+ g_main_loop_unref(gml);
+ mrp_free(glue);
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_mainloop_unregister_from_glib(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+mrp_mainloop_t *mrp_mainloop_glib_get(GMainLoop *gml)
+{
+ mrp_mainloop_t *ml;
+
+ if (gml != NULL) {
+ ml = mrp_mainloop_create();
+
+ if (ml != NULL) {
+ if (mrp_mainloop_register_with_glib(ml, gml))
+ return ml;
+ else
+ mrp_mainloop_destroy(ml);
+ }
+ }
+
+ return NULL;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_GLIB_H__
+#define __MURPHY_GLIB_H__
+
+#include <murphy/common/mainloop.h>
+#include <glib.h>
+
+MRP_CDECL_BEGIN
+
+/** Register the given murphy mainloop with the glib mainloop. */
+int mrp_mainloop_register_with_glib(mrp_mainloop_t *ml, GMainLoop *gml);
+
+/** Unrgister the given murphy mainloop from the glib mainloop. */
+int mrp_mainloop_unregister_from_glib(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the glib mainloop. */
+mrp_mainloop_t *mrp_mainloop_glib_get(GMainLoop *gml);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_GLIB_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+
+#include "murphy/common/mm.h"
+#include "murphy/common/list.h"
+#include "murphy/common/hashtbl.h"
+
+#define MIN_NBUCKET 8
+#define MAX_NBUCKET 128
+
+typedef struct { /* a hash bucket */
+ mrp_list_hook_t entries; /* hook to hash table entries */
+ mrp_list_hook_t used; /* hook to list of buckets in use */
+} bucket_t;
+
+typedef struct { /* a hash table entry */
+ mrp_list_hook_t hook; /* hook to bucket chain */
+ void *key; /* key for this entry */
+ void *obj; /* object for this entry */
+} entry_t;
+
+typedef struct { /* iterator state */
+ mrp_list_hook_t *bp, *bn; /* current bucket hook pointers */
+ mrp_list_hook_t *ep, *en; /* current entry hook pointers */
+ entry_t *entry; /* current entry */
+ int verdict; /* remove-from-cb verdict */
+} iter_t;
+
+struct mrp_htbl_s {
+ bucket_t *buckets; /* hash table buckets */
+ size_t nbucket; /* this many of them */
+ mrp_list_hook_t used; /* buckets in use */
+ mrp_htbl_comp_fn_t comp; /* key comparison function */
+ mrp_htbl_hash_fn_t hash; /* key hash function */
+ mrp_htbl_free_fn_t free; /* function to free an entry */
+ iter_t *iter; /* active iterator state */
+};
+
+
+static size_t calc_buckets(size_t nbucket)
+{
+ size_t n;
+
+ if (nbucket < MIN_NBUCKET)
+ nbucket = MIN_NBUCKET;
+ if (nbucket > MAX_NBUCKET)
+ nbucket = MAX_NBUCKET;
+
+ for (n = MIN_NBUCKET; n < nbucket; n <<= 1)
+ ;
+
+ return n;
+}
+
+
+mrp_htbl_t *mrp_htbl_create(mrp_htbl_config_t *cfg)
+{
+ mrp_htbl_t *ht;
+ size_t i, nbucket;
+
+ if (cfg->comp && cfg->hash) {
+ if ((ht = mrp_allocz(sizeof(*ht))) != NULL) {
+ if (cfg->nbucket != 0)
+ nbucket = cfg->nbucket;
+ else {
+ if (cfg->nentry != 0)
+ nbucket = cfg->nentry / 4;
+ else
+ nbucket = 4 * MIN_NBUCKET;
+ }
+
+ ht->nbucket = calc_buckets(nbucket);
+ ht->comp = cfg->comp;
+ ht->hash = cfg->hash;
+ ht->free = cfg->free;
+
+ mrp_list_init(&ht->used);
+
+ ht->buckets = mrp_allocz(sizeof(*ht->buckets) * ht->nbucket);
+ if (ht->buckets != NULL) {
+ for (i = 0; i < ht->nbucket; i++) {
+ mrp_list_init(&ht->buckets[i].entries);
+ mrp_list_init(&ht->buckets[i].used);
+ }
+
+ return ht;
+ }
+ else {
+ mrp_free(ht);
+ ht = NULL;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+void mrp_htbl_destroy(mrp_htbl_t *ht, int free)
+{
+ if (ht != NULL) {
+ if (free)
+ mrp_htbl_reset(ht, free);
+
+ mrp_free(ht->buckets);
+ mrp_free(ht);
+ }
+}
+
+
+static inline void free_entry(mrp_htbl_t *ht, entry_t *entry, int free)
+{
+ if (free && ht->free)
+ ht->free(entry->key, entry->obj);
+ mrp_free(entry);
+}
+
+
+void mrp_htbl_reset(mrp_htbl_t *ht, int free)
+{
+ mrp_list_hook_t *bp, *bn, *ep, *en;
+ bucket_t *bucket;
+ entry_t *entry;
+
+ mrp_list_foreach(&ht->used, bp, bn) {
+ bucket = mrp_list_entry(bp, bucket_t, used);
+
+ mrp_list_foreach(&bucket->entries, ep, en) {
+ entry = mrp_list_entry(ep, entry_t, hook);
+ mrp_list_delete(ep);
+ free_entry(ht, entry, free);
+ }
+
+ mrp_list_delete(&bucket->used);
+ }
+}
+
+
+int mrp_htbl_insert(mrp_htbl_t *ht, void *key, void *object)
+{
+ uint32_t idx = ht->hash(key) & (ht->nbucket - 1);
+ bucket_t *bucket = ht->buckets + idx;
+ int first = mrp_list_empty(&bucket->entries);
+ entry_t *entry;
+
+ if ((entry = mrp_allocz(sizeof(*entry))) != NULL) {
+ entry->key = key;
+ entry->obj = object;
+ mrp_list_append(&bucket->entries, &entry->hook);
+ if (first)
+ mrp_list_append(&ht->used, &bucket->used);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static inline entry_t *lookup(mrp_htbl_t *ht, void *key, bucket_t **bucketp)
+{
+ uint32_t idx = ht->hash(key) & (ht->nbucket - 1);
+ bucket_t *bucket = ht->buckets + idx;
+ mrp_list_hook_t *p, *n;
+ entry_t *entry;
+
+ mrp_list_foreach(&bucket->entries, p, n) {
+ entry = mrp_list_entry(p, entry_t, hook);
+
+ if (!ht->comp(entry->key, key)) {
+ if (bucketp != NULL)
+ *bucketp = bucket;
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+
+void *mrp_htbl_lookup(mrp_htbl_t *ht, void *key)
+{
+ entry_t *entry;
+
+ entry = lookup(ht, key, NULL);
+ if (entry != NULL)
+ return entry->obj;
+ else
+ return NULL;
+}
+
+
+static void delete_from_bucket(mrp_htbl_t *ht, bucket_t *bucket, entry_t *entry)
+{
+ mrp_list_hook_t *eh = &entry->hook;
+
+
+ /*
+ * If there is an iterator active and this entry would
+ * have been the next one to iterate over, we need to
+ * update the iterator to skip to the next entry instead
+ * as this one will be removed. Failing to update the
+ * iterator could crash mrp_htbl_foreach or drive it into
+ * an infinite loop.
+ */
+
+ if (ht->iter != NULL && ht->iter->en == eh)
+ ht->iter->en = eh->next;
+
+ mrp_list_delete(eh);
+
+
+ /*
+ * If the bucket became empty, unlink it from the used list.
+ * If also there is an iterator active and this bucket would
+ * have been the next one to iterate over, we need to
+ * update the iterator to skip to the next bucket instead
+ * as this one just became empty and will be removed from
+ * the used bucket list. Failing to update the iterator
+ * could drive mrp_htbl_foreach into an infinite loop
+ * because of the unexpected hop from the used bucket list
+ * (to a single empty bucket).
+ */
+
+ if (mrp_list_empty(&bucket->entries)) {
+ if (ht->iter != NULL && ht->iter->bn == &bucket->used)
+ ht->iter->bn = bucket->used.next;
+
+ mrp_list_delete(&bucket->used);
+ }
+}
+
+
+void *mrp_htbl_remove(mrp_htbl_t *ht, void *key, int free)
+{
+ bucket_t *bucket;
+ entry_t *entry;
+ void *object;
+
+ /*
+ * We need to check the found entry and its hash-bucket
+ * against any potentially active iterator. Special care
+ * needs to be taken if the entry is being iterated over
+ * or if the bucket becomes empty and it would be the next
+ * bucket to iterate over. The former is taken care of
+ * here while the latter is handled in delete_from_bucket.
+ */
+ if ((entry = lookup(ht, key, &bucket)) != NULL) {
+ delete_from_bucket(ht, bucket, entry);
+ object = entry->obj;
+
+ if (ht->iter != NULL && entry == ht->iter->entry) /* being iterated */
+ ht->iter->verdict = free ? MRP_HTBL_ITER_DELETE : 0;
+ else {
+ free_entry(ht, entry, free);
+ }
+ }
+ else
+ object = NULL;
+
+ return object;
+}
+
+
+int mrp_htbl_foreach(mrp_htbl_t *ht, mrp_htbl_iter_cb_t cb, void *user_data)
+{
+ iter_t iter;
+ bucket_t *bucket;
+ entry_t *entry;
+ int cb_verdict, ht_verdict;
+
+ /*
+ * Now we can only handle a single callback-based iterator.
+ * If there is already one we're busy so just bail out.
+ */
+ if (ht->iter != NULL)
+ return FALSE;
+
+ mrp_clear(&iter);
+ ht->iter = &iter;
+
+ mrp_list_foreach(&ht->used, iter.bp, iter.bn) {
+ bucket = mrp_list_entry(iter.bp, bucket_t, used);
+
+ mrp_list_foreach(&bucket->entries, iter.ep, iter.en) {
+ iter.entry = entry = mrp_list_entry(iter.ep, entry_t, hook);
+ cb_verdict = cb(entry->key, entry->obj, user_data);
+ ht_verdict = iter.verdict;
+
+ /* delete was called from cb (unhashed entry and marked it) */
+ if (ht_verdict & MRP_HTBL_ITER_DELETE) {
+ free_entry(ht, entry, TRUE);
+ }
+ else {
+ /* cb wants us to unhash (safe even if unhashed in remove) */
+ if (cb_verdict & MRP_HTBL_ITER_UNHASH)
+ mrp_list_delete(iter.ep);
+ /* cb want us to free entry (and remove was not called) */
+ if (cb_verdict & MRP_HTBL_ITER_DELETE)
+ free_entry(ht, entry, TRUE);
+
+ /* cb wants to stop iterating */
+ if (!(cb_verdict & MRP_HTBL_ITER_MORE))
+ goto out;
+ }
+ }
+ }
+
+ out:
+ ht->iter = NULL;
+
+ return TRUE;
+}
+
+
+void *mrp_htbl_find(mrp_htbl_t *ht, mrp_htbl_find_cb_t cb, void *user_data)
+{
+ iter_t iter;
+ bucket_t *bucket;
+ entry_t *entry, *found;
+
+ /*
+ * Bail out if there is also an iterator active...
+ */
+ if (ht->iter != NULL)
+ return FALSE;
+
+ mrp_clear(&iter);
+ ht->iter = &iter;
+ found = NULL;
+
+ mrp_list_foreach(&ht->used, iter.bp, iter.bn) {
+ bucket = mrp_list_entry(iter.bp, bucket_t, used);
+
+ mrp_list_foreach(&bucket->entries, iter.ep, iter.en) {
+ entry = mrp_list_entry(iter.ep, entry_t, hook);
+
+ if (cb(entry->key, entry->obj, user_data)) {
+ found = entry->obj;
+ goto out;
+ }
+ }
+ }
+
+ out:
+ ht->iter = NULL;
+
+ return found;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_HASHTBL_H__
+#define __MURPHY_HASHTBL_H__
+
+#include <stdint.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_htbl_s mrp_htbl_t;
+
+/** Prototype for key comparison functions. */
+typedef int (*mrp_htbl_comp_fn_t)(const void *key1, const void *key2);
+
+/** Prototoype for key hash functions. */
+typedef uint32_t (*mrp_htbl_hash_fn_t)(const void *key);
+
+/** Prototype for functions used to free entries. */
+typedef void (*mrp_htbl_free_fn_t)(void *key, void *object);
+
+
+/*
+ * hash table configuration
+ */
+typedef struct {
+ size_t nentry; /* estimated entries */
+ mrp_htbl_comp_fn_t comp; /* comparison function */
+ mrp_htbl_hash_fn_t hash; /* hash function */
+ mrp_htbl_free_fn_t free; /* freeing function */
+ size_t nbucket; /* number of buckets, or 0 */
+} mrp_htbl_config_t;
+
+
+/** Create a new hash table with the given configuration. */
+mrp_htbl_t *mrp_htbl_create(mrp_htbl_config_t *cfg);
+
+/** Destroy a hash table, free all entries unless @free is FALSE. */
+void mrp_htbl_destroy(mrp_htbl_t *ht, int free);
+
+/** Reset a hash table, also free all entries unless @free is FALSE. */
+void mrp_htbl_reset(mrp_htbl_t *ht, int free);
+
+/** Insert the given @key/@object pair to the hash table. */
+int mrp_htbl_insert(mrp_htbl_t *ht, void *key, void *object);
+
+/** Remove and return the object for @key, also free unless @free is FALSE. */
+void *mrp_htbl_remove(mrp_htbl_t *ht, void *key, int free);
+
+/** Look up the object corresponding to @key. */
+void *mrp_htbl_lookup(mrp_htbl_t *ht, void *key);
+
+/** Find the first matching entry in a hash table. */
+typedef int (*mrp_htbl_find_cb_t)(void *key, void *object, void *user_data);
+void *mrp_htbl_find(mrp_htbl_t *ht, mrp_htbl_find_cb_t cb, void *user_data);
+
+
+/*
+ * hash table iterators
+ */
+
+enum {
+ MRP_HTBL_ITER_STOP = 0x0, /* stop iterating */
+ MRP_HTBL_ITER_MORE = 0x1, /* keep iterating */
+ MRP_HTBL_ITER_UNHASH = 0x2, /* unhash without freeing */
+ MRP_HTBL_ITER_DELETE = 0x6, /* unhash and free */
+};
+
+typedef int (*mrp_htbl_iter_cb_t)(void *key, void *object, void *user_data);
+int mrp_htbl_foreach(mrp_htbl_t *ht, mrp_htbl_iter_cb_t cb, void *user_data);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_HASHTBL_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <murphy/common.h>
+
+#define INTERNAL "internal"
+
+typedef struct internal_s internal_t;
+
+struct internal_s {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ char name[MRP_SOCKADDR_SIZE]; /* bound connection name */
+ mrp_sockaddr_t address; /* internal connection name*/
+ bool active;
+ bool bound;
+ bool listening;
+
+ internal_t *endpoint; /* that we are connected to */
+};
+
+typedef struct {
+ void *data;
+ size_t size;
+ internal_t *u;
+ mrp_sockaddr_t *addr;
+ socklen_t addrlen;
+ bool free_data;
+ int offset;
+ bool custom;
+ uint16_t tag;
+
+ mrp_list_hook_t hook;
+} internal_message_t;
+
+
+/* storage for the global data, TODO: refactor away */
+static mrp_htbl_t *servers = NULL;
+static mrp_htbl_t *connections = NULL;
+static mrp_list_hook_t msg_queue;
+static mrp_deferred_t *d;
+static uint32_t cid;
+
+
+static void process_queue(mrp_deferred_t *d, void *user_data)
+{
+ internal_message_t *msg;
+ internal_t *endpoint;
+ mrp_list_hook_t *p, *n;
+
+ MRP_UNUSED(user_data);
+
+ mrp_disable_deferred(d);
+
+ mrp_list_foreach(&msg_queue, p, n) {
+
+ msg = mrp_list_entry(p, typeof(*msg), hook);
+
+ if (!msg) {
+ mrp_log_error("no message!");
+ goto end;
+ }
+
+ if (!msg->u->connected) {
+ if (!msg->addr) {
+ mrp_log_error("connected transport without address!");
+ goto end;
+ }
+
+ /* Find the recipient. Look first from the server table.*/
+ endpoint = mrp_htbl_lookup(servers, msg->addr->data);
+
+ if (!endpoint) {
+
+ /* Look next from the general connections table. */
+ endpoint = mrp_htbl_lookup(connections, msg->addr->data);
+ }
+ }
+ else {
+ endpoint = msg->u->endpoint;
+ }
+
+ if (!endpoint || !endpoint->recv_data) {
+ mrp_log_error("no endpoint matching the address");
+ goto end;
+ }
+
+ /* skip the length word when sending */
+ endpoint->recv_data(
+ (mrp_transport_t *) endpoint, msg->data + msg->offset,
+ msg->size, &msg->u->address, MRP_SOCKADDR_SIZE);
+
+end:
+ if (msg) {
+
+ if (msg->free_data) {
+ if (msg->custom) {
+ /* FIXME: should be mrp_data_free(msg->data, msg->tag); */
+ mrp_free(msg->data);
+ }
+ else
+ mrp_msg_unref(msg->data);
+ }
+
+ mrp_list_delete(&msg->hook);
+ mrp_free(msg);
+ }
+ }
+}
+
+
+static int internal_initialize_table(internal_t *u)
+{
+ mrp_htbl_config_t servers_conf;
+ mrp_htbl_config_t connections_conf;
+
+ MRP_UNUSED(u);
+
+ if (servers && connections && d)
+ return 0; /* already initialized */
+
+ servers_conf.comp = mrp_string_comp;
+ servers_conf.hash = mrp_string_hash;
+ servers_conf.free = NULL;
+ servers_conf.nbucket = 0;
+ servers_conf.nentry = 10;
+
+ servers = mrp_htbl_create(&servers_conf);
+
+ if (!servers)
+ goto error;
+
+ connections_conf.comp = mrp_string_comp;
+ connections_conf.hash = mrp_string_hash;
+ connections_conf.free = NULL;
+ connections_conf.nbucket = 0;
+ connections_conf.nentry = 10;
+
+ connections = mrp_htbl_create(&connections_conf);
+
+ if (!connections)
+ goto error;
+
+ mrp_list_init(&msg_queue);
+
+ cid = 0;
+
+ d = mrp_add_deferred(u->ml, process_queue, NULL);
+
+ if (!d)
+ goto error;
+
+ mrp_disable_deferred(d);
+
+ return 0;
+
+error:
+
+ if (servers)
+ mrp_htbl_destroy(servers, FALSE);
+
+ servers = NULL;
+
+ if (connections)
+ mrp_htbl_destroy(connections, FALSE);
+
+ connections = NULL;
+
+ return -1;
+}
+
+
+static socklen_t internal_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ int len;
+
+ MRP_UNUSED(size);
+
+ if (!str)
+ return 0;
+
+ len = strlen(str);
+
+ if (len <= 9 || len >= MRP_SOCKADDR_SIZE)
+ return 0;
+
+ if (strncmp("internal:", str, 9))
+ return 0;
+
+ if (typep)
+ *typep = INTERNAL;
+
+ memcpy(addr->data, str+9, len-9+1);
+
+ return len-9;
+}
+
+
+static int internal_open(mrp_transport_t *mu)
+{
+ internal_t *u = (internal_t *)mu;
+
+ if (internal_initialize_table(u) < 0)
+ return FALSE;
+
+ memset(u->name, 0, MRP_SOCKADDR_SIZE);
+ memset(u->address.data, 0, MRP_SOCKADDR_SIZE);
+
+ u->active = FALSE;
+
+ snprintf(u->address.data, MRP_SOCKADDR_SIZE, INTERNAL"_%d", cid++);
+
+ mrp_htbl_insert(connections, u->address.data, mu);
+
+ return TRUE;
+}
+
+
+static int internal_bind(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+
+ if (internal_initialize_table(u) < 0)
+ return FALSE;
+
+ memcpy(u->name, addr->data, addrlen+1);
+
+ mrp_htbl_insert(servers, u->name, u);
+
+ u->active = TRUE;
+ u->bound = TRUE;
+
+ return TRUE;
+}
+
+
+static int internal_listen(mrp_transport_t *mu, int backlog)
+{
+ internal_t *u = (internal_t *)mu;
+
+ MRP_UNUSED(backlog);
+
+ if (!u->bound)
+ return FALSE;
+
+ u->listening = TRUE;
+
+ return TRUE;
+}
+
+
+static int internal_accept(mrp_transport_t *mt, mrp_transport_t *mlt)
+{
+ internal_t *t = (internal_t *) mt;
+ internal_t *lt = (internal_t *) mlt;
+ internal_t *client = lt->endpoint;
+
+ t->endpoint = client;
+ client->endpoint = t;
+
+ lt->endpoint = NULL; /* connection process is now over */
+
+ return TRUE;
+}
+
+
+static void remove_messages(internal_t *u)
+{
+ internal_message_t *msg;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&msg_queue, p, n) {
+ msg = mrp_list_entry(p, typeof(*msg), hook);
+
+ if (strcmp(msg->addr->data, u->name) == 0
+ || strcmp(msg->addr->data, u->address.data) == 0) {
+
+ if (msg->free_data) {
+ if (msg->custom) {
+ /* FIXME: should be mrp_data_free(msg->data, msg->tag); */
+ mrp_free(msg->data);
+ }
+ else
+ mrp_msg_unref(msg->data);
+ }
+
+ mrp_list_delete(&msg->hook);
+ mrp_free(msg);
+ }
+ }
+}
+
+
+static void internal_close(mrp_transport_t *mu)
+{
+ internal_t *u = (internal_t *)mu;
+
+ /* Is this client or server? If server, go remove the connection from
+ * servers table. */
+
+ if (u->bound) {
+ /* server listening socket */
+ mrp_htbl_remove(servers, u->name, FALSE);
+ u->bound = FALSE;
+ }
+
+ mrp_htbl_remove(connections, u->address.data, FALSE);
+
+ u->active = FALSE;
+
+ remove_messages(u);
+}
+
+
+static int internal_connect(mrp_transport_t *mu, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ internal_t *host;
+ mrp_transport_t *mt;
+
+ MRP_UNUSED(addrlen);
+
+ /* client connecting */
+
+ if (!servers) {
+ mrp_log_error("no servers available for connecting");
+ return FALSE;
+ }
+
+ host = mrp_htbl_lookup(servers, addr->data);
+
+ if (!host) {
+ mrp_log_error("server '%s' wasn't found", addr->data);
+ return FALSE;
+ }
+
+ mt = (mrp_transport_t *) host;
+
+ host->endpoint = u; /* temporary connection data */
+
+ host->evt.connection(mt, mt->user_data);
+
+ return TRUE;
+}
+
+
+static int internal_disconnect(mrp_transport_t *mu)
+{
+ internal_t *u = (internal_t *)mu;
+
+ if (u->connected) {
+ internal_t *endpoint = u->endpoint;
+
+ if (endpoint) {
+ endpoint->endpoint = NULL;
+ mrp_transport_disconnect((mrp_transport_t *) endpoint);
+ }
+ u->endpoint = NULL;
+ }
+
+ return TRUE;
+}
+
+
+static int internal_sendto(mrp_transport_t *mu, mrp_msg_t *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ void *buf;
+ size_t size;
+ internal_message_t *msg;
+
+ size = mrp_msg_default_encode(data, &buf);
+
+ if (size == 0 || buf == NULL) {
+ return FALSE;
+ }
+
+ msg = mrp_allocz(sizeof(internal_message_t));
+
+ if (!msg)
+ return FALSE;
+
+ msg->addr = addr;
+ msg->addrlen = addrlen;
+ msg->data = buf;
+ msg->free_data = FALSE;
+ msg->offset = 0;
+ msg->size = size;
+ msg->u = u;
+ msg->custom = FALSE;
+
+ mrp_list_init(&msg->hook);
+ mrp_list_append(&msg_queue, &msg->hook);
+
+ mrp_enable_deferred(d);
+
+ return TRUE;
+}
+
+
+static int internal_send(mrp_transport_t *mu, mrp_msg_t *msg)
+{
+ if (!mu->connected) {
+ return FALSE;
+ }
+
+ return internal_sendto(mu, msg, NULL, 0);
+}
+
+
+static int internal_sendrawto(mrp_transport_t *mu, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ internal_message_t *msg;
+
+ msg = mrp_allocz(sizeof(internal_message_t));
+
+ if (!msg)
+ return FALSE;
+
+ msg->addr = addr;
+ msg->addrlen = addrlen;
+ msg->data = data;
+ msg->free_data = FALSE;
+ msg->offset = 0;
+ msg->size = size;
+ msg->u = u;
+ msg->custom = FALSE;
+
+ mrp_list_init(&msg->hook);
+ mrp_list_append(&msg_queue, &msg->hook);
+
+ mrp_enable_deferred(d);
+
+ return TRUE;
+}
+
+
+static int internal_sendraw(mrp_transport_t *mu, void *data, size_t size)
+{
+ if (!mu->connected) {
+ return FALSE;
+ }
+
+ return internal_sendrawto(mu, data, size, NULL, 0);
+}
+
+
+static size_t encode_custom_data(void *data, void **newdata, uint16_t tag)
+{
+ mrp_data_descr_t *type = mrp_msg_find_type(tag);
+ uint32_t *lenp;
+ uint16_t *tagp;
+ size_t reserve, size;
+ int len;
+ void *buf;
+
+ if (type == NULL) {
+ mrp_log_error("type not found!");
+ return 0;
+ }
+
+ reserve = sizeof(*lenp) + sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size == 0) {
+ mrp_log_error("data encoding failed");
+ return 0;
+ }
+
+ /* some format conversion */
+
+ lenp = buf;
+ len = size - sizeof(*lenp);
+ tagp = buf + sizeof(*lenp);
+
+ *tagp = htobe16(tag);
+
+ *newdata = buf;
+
+ return len;
+}
+
+
+static int internal_senddatato(mrp_transport_t *mu, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ internal_t *u = (internal_t *)mu;
+ mrp_data_descr_t *type = mrp_msg_find_type(tag);
+ void *newdata = NULL;
+ size_t size;
+ internal_message_t *msg;
+
+ if (type == NULL)
+ return FALSE;
+
+ msg = mrp_allocz(sizeof(internal_message_t));
+
+ if (!msg)
+ return FALSE;
+
+ size = encode_custom_data(data, &newdata, tag);
+
+ if (!newdata) {
+ mrp_log_error("custom data encoding failed");
+ mrp_free(msg);
+ return FALSE;
+ }
+
+ msg->addr = addr;
+ msg->addrlen = addrlen;
+ msg->data = newdata;
+ msg->free_data = TRUE;
+ msg->offset = 4;
+ msg->size = size;
+ msg->u = u;
+ msg->custom = TRUE;
+ msg->tag = tag;
+
+ mrp_list_init(&msg->hook);
+ mrp_list_append(&msg_queue, &msg->hook);
+
+ mrp_enable_deferred(d);
+
+ return TRUE;
+}
+
+
+static int internal_senddata(mrp_transport_t *mu, void *data, uint16_t tag)
+{
+ if (!mu->connected) {
+ return FALSE;
+ }
+
+ return internal_senddatato(mu, data, tag, NULL, 0);
+}
+
+
+
+
+MRP_REGISTER_TRANSPORT(internal, INTERNAL, internal_t, internal_resolve,
+ internal_open, NULL, internal_close, NULL,
+ internal_bind, internal_listen, internal_accept,
+ internal_connect, internal_disconnect,
+ internal_send, internal_sendto,
+ internal_sendraw, internal_sendrawto,
+ internal_senddata, internal_senddatato,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include "murphy/config.h"
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/json.h>
+
+/** Type for a JSON parser. */
+typedef struct json_tokener mrp_json_parser_t;
+
+static mrp_json_parser_t *parser;
+
+mrp_json_t *mrp_json_create(mrp_json_type_t type, ...)
+{
+ mrp_json_t *o;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ o = json_object_new_string(s);
+ else
+ o = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ o = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ o = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ o = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ o = json_object_new_object();
+ break;
+ case MRP_JSON_ARRAY:
+ o = json_object_new_array();
+ break;
+ default:
+ o = NULL;
+ }
+ va_end(ap);
+
+ return o;
+}
+
+
+mrp_json_t *mrp_json_clone(mrp_json_t *o)
+{
+ if (o != NULL)
+ return mrp_json_string_to_object(mrp_json_object_to_string(o), -1);
+ else
+ return NULL;
+}
+
+
+mrp_json_t *mrp_json_string_to_object(const char *s, int len)
+{
+ if (parser == NULL) {
+ parser = json_tokener_new();
+
+ if (parser == NULL)
+ return NULL;
+ }
+ else
+ json_tokener_reset(parser);
+
+ if (len < 0)
+ len = strlen(s);
+
+ return json_tokener_parse_ex(parser, s, len);
+}
+
+
+const char *mrp_json_object_to_string(mrp_json_t *o)
+{
+ if (o != NULL)
+ return json_object_to_json_string(o);
+ else
+ return "{}";
+}
+
+
+mrp_json_t *mrp_json_ref(mrp_json_t *o)
+{
+ return json_object_get(o);
+}
+
+
+void mrp_json_unref(mrp_json_t *o)
+{
+ json_object_put(o);
+}
+
+
+mrp_json_type_t mrp_json_get_type(mrp_json_t *o)
+{
+ return json_object_get_type(o);
+}
+
+
+int mrp_json_is_type(mrp_json_t *o, mrp_json_type_t type)
+{
+ return json_object_is_type(o, type);
+}
+
+
+void mrp_json_add(mrp_json_t *o, const char *key, mrp_json_t *m)
+{
+ json_object_object_add(o, key, m);
+}
+
+
+mrp_json_t *mrp_json_add_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...)
+{
+ mrp_json_t *m;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ m = json_object_new_string(s);
+ else
+ m = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ m = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ m = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ m = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ m = json_object_new_object();
+ break;
+ case MRP_JSON_ARRAY:
+ m = json_object_new_array();
+ break;
+ default:
+ m = NULL;
+ errno = EINVAL;
+ }
+ va_end(ap);
+
+ if (m != NULL)
+ json_object_object_add(o, key, m);
+
+ return m;
+}
+
+
+mrp_json_t *mrp_json_add_array(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...)
+{
+ va_list ap;
+ void *arr;
+ size_t cnt, i;
+ mrp_json_t *a;
+
+ va_start(ap, type);
+ arr = va_arg(ap, void *);
+ cnt = va_arg(ap, size_t);
+ a = mrp_json_create(MRP_JSON_ARRAY);
+
+ if (a == NULL)
+ goto fail;
+
+ switch (type) {
+ case MRP_JSON_STRING:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_string(a, ((char **)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ case MRP_JSON_INTEGER:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_integer(a, ((int *)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ case MRP_JSON_DOUBLE:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_double(a, ((double *)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ case MRP_JSON_BOOLEAN:
+ for (i = 0; i < cnt; i++) {
+ if (!mrp_json_array_append_boolean(a, ((bool *)arr)[i]))
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+
+ }
+
+ va_end(ap);
+
+ mrp_json_add(o, key, a);
+ return a;
+
+ fail:
+ va_end(ap);
+ mrp_json_unref(a);
+
+ return NULL;
+}
+
+
+mrp_json_t *mrp_json_get(mrp_json_t *o, const char *key)
+{
+ mrp_json_iter_t it;
+ const char *k;
+ mrp_json_t *v;
+
+ mrp_json_foreach_member(o, k, v, it) {
+ if (!strcmp(k, key))
+ return v;
+ }
+
+ return NULL;
+}
+
+
+int mrp_json_get_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...)
+{
+ const char **s;
+ bool *b;
+ int *i;
+ double *d;
+ mrp_json_t *m, **mp;
+ int success;
+ va_list ap;
+
+ success = FALSE;
+ va_start(ap, type);
+
+ m = mrp_json_get(o, key);
+
+ if (m != NULL) {
+ if (json_object_is_type(m, type)) {
+ success = TRUE;
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char **);
+ *s = json_object_get_string(m);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, bool *);
+ *b = json_object_get_boolean(m);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int *);
+ *i = json_object_get_int(m);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double *);
+ *d = json_object_get_double(m);
+ break;
+ case MRP_JSON_OBJECT:
+ mp = va_arg(ap, mrp_json_t **);
+ *mp = m;
+ break;
+ case MRP_JSON_ARRAY:
+ mp = va_arg(ap, mrp_json_t **);
+ *mp = m;
+ break;
+ default:
+ success = FALSE;
+ }
+ }
+ else
+ errno = EINVAL;
+ }
+ else {
+ errno = ENOENT;
+ success = FALSE;
+ }
+
+ va_end(ap);
+
+ return success;
+}
+
+
+void mrp_json_del_member(mrp_json_t *o, const char *key)
+{
+ json_object_object_del(o, key);
+}
+
+
+int mrp_json_array_length(mrp_json_t *a)
+{
+ return json_object_array_length(a);
+}
+
+
+int mrp_json_array_append(mrp_json_t *a, mrp_json_t *v)
+{
+ return json_object_array_add(a, v) == 0;
+}
+
+
+mrp_json_t *mrp_json_array_append_item(mrp_json_t *a, mrp_json_type_t type, ...)
+{
+ mrp_json_t *v;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ v = json_object_new_string(s);
+ else
+ v = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ v = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ v = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ v = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ case MRP_JSON_ARRAY:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ default:
+ v = NULL;
+ errno = EINVAL;
+ }
+ va_end(ap);
+
+ if (v != NULL) {
+ if (json_object_array_add(a, v) == 0)
+ return v;
+ else {
+ mrp_json_unref(v);
+ errno = ENOMEM;
+ }
+ }
+
+ return NULL;
+}
+
+
+int mrp_json_array_set(mrp_json_t *a, int idx, mrp_json_t *v)
+{
+ return json_object_array_put_idx(a, idx, v);
+}
+
+
+int mrp_json_array_set_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...)
+{
+ mrp_json_t *v;
+ const char *s;
+ bool b;
+ int i, l;
+ double d;
+ va_list ap;
+
+ va_start(ap, type);
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char *);
+ l = va_arg(ap, int);
+ if (l < 0)
+ v = json_object_new_string(s);
+ else
+ v = json_object_new_string_len(s, l);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, int);
+ v = json_object_new_boolean(b);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int);
+ v = json_object_new_int(i);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double);
+ v = json_object_new_double(d);
+ break;
+ case MRP_JSON_OBJECT:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ case MRP_JSON_ARRAY:
+ v = va_arg(ap, mrp_json_t *);
+ break;
+ default:
+ v = NULL;
+ errno = EINVAL;
+ }
+ va_end(ap);
+
+ if (v != NULL)
+ return json_object_array_put_idx(a, idx, v);
+ else {
+ errno = ENOMEM;
+ return FALSE;
+ }
+}
+
+
+mrp_json_t *mrp_json_array_get(mrp_json_t *a, int idx)
+{
+ return json_object_array_get_idx(a, idx);
+}
+
+
+int mrp_json_array_get_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...)
+{
+ const char **s;
+ bool *b;
+ int *i;
+ double *d;
+ mrp_json_t *v, **vp;
+ int success;
+ va_list ap;
+
+ success = FALSE;
+ va_start(ap, type);
+
+ v = json_object_array_get_idx(a, idx);
+
+ if (v != NULL) {
+ if (json_object_is_type(v, type)) {
+ success = TRUE;
+ switch (type) {
+ case MRP_JSON_STRING:
+ s = va_arg(ap, const char **);
+ *s = json_object_get_string(v);
+ break;
+ case MRP_JSON_BOOLEAN:
+ b = va_arg(ap, bool *);
+ *b = json_object_get_boolean(v);
+ break;
+ case MRP_JSON_INTEGER:
+ i = va_arg(ap, int *);
+ *i = json_object_get_int(v);
+ break;
+ case MRP_JSON_DOUBLE:
+ d = va_arg(ap, double *);
+ *d = json_object_get_double(v);
+ break;
+ case MRP_JSON_OBJECT:
+ vp = va_arg(ap, mrp_json_t **);
+ *vp = v;
+ break;
+ case MRP_JSON_ARRAY:
+ vp = va_arg(ap, mrp_json_t **);
+ *vp = v;
+ break;
+ default:
+ success = FALSE;
+ errno = EINVAL;
+ }
+ }
+ else
+ errno = EINVAL;
+ }
+ else
+ errno = ENOENT;
+
+ va_end(ap);
+
+ return success;
+}
+
+
+int mrp_json_parse_object(char **strp, int *lenp, mrp_json_t **op)
+{
+ char *str;
+ int len;
+ mrp_json_t *o = NULL;
+ json_tokener *tok = NULL;
+ int res = -1;
+
+ if (strp == NULL || *strp == NULL) {
+ *op = NULL;
+ if (lenp != NULL)
+ *lenp = 0;
+
+ return 0;
+ }
+
+ str = *strp;
+ len = lenp ? *lenp : 0;
+
+ if (len <= 0)
+ len = strlen(str);
+
+ tok = json_tokener_new();
+
+ if (tok != NULL) {
+ o = json_tokener_parse_ex(tok, str, len);
+
+ if (o != NULL) {
+ *strp += tok->char_offset;
+ if (lenp != NULL)
+ *lenp -= tok->char_offset;
+
+ res = 0;
+ }
+ else {
+#ifdef HAVE_JSON_TOKENER_GET_ERROR
+ if (json_tokener_get_error(tok) != json_tokener_success)
+ errno = EINVAL;
+#else
+ if (tok->err != json_tokener_success)
+ errno = EINVAL;
+#endif
+ else
+ res = 0;
+ }
+
+ json_tokener_free(tok);
+ }
+ else
+ errno = ENOMEM;
+
+ *op = o;
+ return res;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_JSON_H__
+#define __MURPHY_JSON_H__
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#include "murphy/config.h"
+
+#ifndef JSON_INCLUDE_PATH_JSONC
+# include <json/json.h>
+# include <json/linkhash.h>
+/* workaround for older broken json-c not exposing json_object_iter */
+# include <json/json_object_private.h>
+#else
+# include <json-c/json.h>
+# include <json-c/linkhash.h>
+/* workaround for older broken json-c not exposing json_object_iter */
+# include <json-c/json_object_private.h>
+#endif
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * We use json-c as the underlying json implementation, However, we do
+ * not want direct json-c dependencies to spread all over the code base
+ * (at least not yet). So we try to define here an envelop layer that
+ * hides json-c underneath.
+ */
+
+/** Type of a JSON object. */
+typedef json_object mrp_json_t;
+
+/** JSON object/member types. */
+typedef enum {
+ MRP_JSON_STRING = json_type_string,
+ MRP_JSON_BOOLEAN = json_type_boolean,
+ MRP_JSON_INTEGER = json_type_int,
+ MRP_JSON_DOUBLE = json_type_double,
+ MRP_JSON_OBJECT = json_type_object,
+ MRP_JSON_ARRAY = json_type_array
+} mrp_json_type_t;
+
+/** Type for a JSON member iterator. */
+typedef json_object_iter mrp_json_iter_t;
+
+/** Create a new JSON object of the given type. */
+mrp_json_t *mrp_json_create(mrp_json_type_t type, ...);
+
+/** Clone the given JSON object, creating a new private copy of it. */
+mrp_json_t *mrp_json_clone(mrp_json_t *o);
+
+/** Deserialize a string to a JSON object. */
+mrp_json_t *mrp_json_string_to_object(const char *str, int len);
+
+/** Serialize a JSON object to a string. */
+const char *mrp_json_object_to_string(mrp_json_t *o);
+
+/** Add a reference to the given JSON object. */
+mrp_json_t *mrp_json_ref(mrp_json_t *o);
+
+/** Remove a reference from the given JSON object, freeing if it was last. */
+void mrp_json_unref(mrp_json_t *o);
+
+/** Get the type of a JSON object. */
+mrp_json_type_t mrp_json_get_type(mrp_json_t *o);
+
+/** Check if a JSON object has the given type. */
+int mrp_json_is_type(mrp_json_t *o, mrp_json_type_t type);
+
+/** Convenience macros to get values of JSON objects of basic types. */
+#define mrp_json_string_value(o) json_object_get_string(o)
+#define mrp_json_integer_value(o) json_object_get_int(o)
+#define mrp_json_double_value(o) json_object_get_double(o)
+#define mrp_json_boolean_value(o) json_object_get_boolean(o)
+
+/** Set a member of a JSON object. */
+void mrp_json_add(mrp_json_t *o, const char *key, mrp_json_t *m);
+
+/** Create a new JSON object and set it as a member of another object. */
+mrp_json_t *mrp_json_add_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...);
+
+/** Convenience macros to add members of various basic types. */
+#define mrp_json_add_string(o, key, s) \
+ mrp_json_add_member(o, key, MRP_JSON_STRING, s, -1)
+
+#define mrp_json_add_string_slice(o, key, s, l) \
+ mrp_json_add_member(o, key, MRP_JSON_STRING, s, l)
+
+#define mrp_json_add_integer(o, key, i) \
+ mrp_json_add_member(o, key, MRP_JSON_INTEGER, i)
+
+#define mrp_json_add_double(o, key, d) \
+ mrp_json_add_member(o, key, MRP_JSON_DOUBLE, d)
+
+#define mrp_json_add_boolean(o, key, b) \
+ mrp_json_add_member(o, key, MRP_JSON_BOOLEAN, (int)b)
+
+/** Add an array member from a native C array of the given type. */
+mrp_json_t *mrp_json_add_array(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...);
+
+/** Convenience macros for adding arrays of various basic types. */
+#define mrp_json_add_string_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_STRING, arr, size)
+
+#define mrp_json_add_int_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_INTEGER, arr, size)
+
+#define mrp_json_add_double_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_DOUBLE, arr, size)
+
+#define mrp_json_add_boolean_array(o, key, arr, size) \
+ mrp_json_add_array(o, key, MRP_JSON_BOOLEAN, arr, size)
+
+/** Get the member of a JSON object as a json object. */
+mrp_json_t *mrp_json_get(mrp_json_t *o, const char *key);
+
+/** Get the member of a JSON object in a type specific format. */
+int mrp_json_get_member(mrp_json_t *o, const char *key,
+ mrp_json_type_t type, ...);
+
+/** Convenience macros to get members of various types. */
+#define mrp_json_get_string(o, key, sptr) \
+ mrp_json_get_member(o, key, MRP_JSON_STRING, sptr)
+
+#define mrp_json_get_integer(o, key, iptr) \
+ mrp_json_get_member(o, key, MRP_JSON_INTEGER, iptr)
+
+#define mrp_json_get_double(o, key, dptr) \
+ mrp_json_get_member(o, key, MRP_JSON_DOUBLE, dptr)
+
+#define mrp_json_get_boolean(o, key, bptr) \
+ mrp_json_get_member(o, key, MRP_JSON_BOOLEAN, bptr)
+
+#define mrp_json_get_array(o, key, aptr) \
+ mrp_json_get_member(o, key, MRP_JSON_ARRAY, aptr)
+
+#define mrp_json_get_object(o, key, optr) \
+ mrp_json_get_member(o, key, MRP_JSON_OBJECT, optr)
+
+/** Delete a member of a JSON object. */
+void mrp_json_del_member(mrp_json_t *o, const char *key);
+
+/** Get the length of a JSON array object. */
+int mrp_json_array_length(mrp_json_t *a);
+
+/** Append a JSON object to an array object. */
+int mrp_json_array_append(mrp_json_t *a, mrp_json_t *v);
+
+/** Create and append a new item to a JSON array object. */
+mrp_json_t *mrp_json_array_append_item(mrp_json_t *a, mrp_json_type_t type,
+ ...);
+
+/** Convenience macros for appending array items of basic types. */
+#define mrp_json_array_append_string(a, s) \
+ mrp_json_array_append_item(a, MRP_JSON_STRING, s, -1)
+
+#define mrp_json_array_append_string_slice(a, s, l) \
+ mrp_json_array_append_item(a, MRP_JSON_STRING, s, l)
+
+
+#define mrp_json_array_append_integer(a, i) \
+ mrp_json_array_append_item(a, MRP_JSON_INTEGER, (int)i)
+
+#define mrp_json_array_append_double(a, d) \
+ mrp_json_array_append_item(a, MRP_JSON_DOUBLE, 1.0*d)
+
+#define mrp_json_array_append_boolean(a, b) \
+ mrp_json_array_append_item(a, MRP_JSON_BOOLEAN, (int)b)
+
+/** Add a JSON object to a given index of an array object. */
+int mrp_json_array_set(mrp_json_t *a, int idx, mrp_json_t *v);
+
+/** Add a JSON object to a given index of an array object. */
+int mrp_json_array_set_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...);
+
+/** Get the object at a given index of a JSON array object. */
+mrp_json_t *mrp_json_array_get(mrp_json_t *a, int idx);
+
+/** Get the element of a JSON array object at an index. */
+int mrp_json_array_get_item(mrp_json_t *a, int idx, mrp_json_type_t type, ...);
+
+/** Convenience macros to get items of certain types from an array. */
+#define mrp_json_array_get_string(a, idx, sptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_STRING, sptr)
+
+#define mrp_json_array_get_integer(a, idx, iptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_INTEGER, iptr)
+
+#define mrp_json_array_get_double(a, idx, dptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_DOUBLE, dptr)
+
+#define mrp_json_array_get_boolean(a, idx, bptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_BOOLEAN, bptr)
+
+#define mrp_json_array_get_array(a, idx, aptr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_ARRAY, aptr)
+
+#define mrp_json_array_get_object(a, idx, optr) \
+ mrp_json_array_get_item(a, idx, MRP_JSON_OBJECT, optr)
+
+/** Iterate through the members of an object. */
+#define mrp_json_foreach_member(o, _k, _v, it) \
+ for (it.entry = json_object_get_object((o))->head; \
+ (it.entry ? \
+ (_k = it.key = it.entry->k, \
+ _v = it.val = (mrp_json_t *)it.entry->v, \
+ it.entry) : 0); \
+ it.entry = it.entry->next)
+
+/** Parse a JSON object from the given string. */
+int mrp_json_parse_object(char **str, int *len, mrp_json_t **op);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_JSON_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ mrp_debug("adding D-BUS watch %p (%s)", dw,
+ dbus_watch_get_enabled(dw) ? "enabled" : "disabled");
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ mrp_debug("event mask for fd %d: %s%s", fd,
+ mask & MRP_IO_EVENT_IN ? "read" : "",
+ mask & MRP_IO_EVENT_OUT ? "write" : "");
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS watch %p...", dw);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ mrp_debug("Toggling D-BUS watch %p...", dw);
+
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("dispatching D-BUS timeout %p...", timer->dt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ mrp_debug("adding D-BUS timeout %p...", dt);
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ mrp_debug("deleting D-BUS timeout %p...", dt);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ mrp_debug("toggling D-BUS timeout %p...", dt);
+
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_debug("waking up mainloop...");
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ mrp_debug("dispatching dbus connection %p...", glue->conn);
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_debug("dispatching status for %p: complete", conn);
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_debug("dispatching status for %p: not complete yet", conn);
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/libdbus.h>
+
+
+#define DBUS_ADMIN_SERVICE "org.freedesktop.DBus"
+#define DBUS_ADMIN_INTERFACE "org.freedesktop.DBus"
+#define DBUS_ADMIN_PATH "/org/freedesktop/DBus"
+#define DBUS_NAME_CHANGED "NameOwnerChanged"
+
+
+struct mrp_dbus_s {
+ char *address; /* bus address */
+ DBusConnection *conn; /* actual D-BUS connection */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_htbl_t *methods; /* method handler table */
+ mrp_htbl_t *signals; /* signal handler table */
+ mrp_list_hook_t name_trackers; /* peer (name) watchers */
+ mrp_list_hook_t calls; /* pending calls */
+ uint32_t call_id; /* next call id */
+ const char *unique_name; /* our unique D-BUS address */
+ int priv; /* whether a private connection */
+ int signal_filter; /* if signal dispatching is set up */
+ int register_fallback; /* if the fallback object is set up */
+ mrp_refcnt_t refcnt; /* reference count */
+};
+
+
+/*
+ * Notes:
+ *
+ * At the moment we administer DBUS method and signal handlers
+ * in a very primitive way (subject to be changed later). For
+ * every bus instance we maintain two hash tables, one for methods
+ * and another for signals. Each method and signal handler is
+ * hashed in only by it's method/signal name to a linked list of
+ * method or signal handlers.
+ *
+ * When dispatching a method, we look up the chain with a matching
+ * method name, or the chain for "" in case a matching chain is
+ * not found, and invoke the handler which best matches the
+ * received message (by looking at the path, interface and name).
+ * Only one such handler is invoked at most.
+ *
+ * For signals we look up both the chain with a matching name and
+ * the chain for "" and invoke all signal handlers that match the
+ * received message (regardless of their return value).
+ */
+
+
+typedef struct {
+ char *member; /* signal/method name */
+ mrp_list_hook_t handlers; /* handlers with matching member */
+} handler_list_t;
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *sender;
+ char *path;
+ char *interface;
+ char *member;
+ mrp_dbus_handler_t handler;
+ void *user_data;
+} handler_t;
+
+#define method_t handler_t
+#define signal_t handler_t
+
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to name tracker list */
+ char *name; /* name to track */
+ mrp_dbus_name_cb_t cb; /* status change callback */
+ void *user_data; /* opaque callback user data */
+ int32_t qid; /* initial query ID */
+} name_tracker_t;
+
+
+typedef struct {
+ mrp_dbus_t *dbus; /* DBUS connection */
+ int32_t id; /* call id */
+ mrp_dbus_reply_cb_t cb; /* completion notification callback */
+ void *user_data; /* opaque callback data */
+ DBusPendingCall *pend; /* pending DBUS call */
+ mrp_list_hook_t hook; /* hook to list of pending calls */
+} call_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml; /* mainloop for bus connection */
+ const char *address; /* address of bus */
+} bus_spec_t;
+
+static mrp_htbl_t *buses;
+
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data);
+static void purge_name_trackers(mrp_dbus_t *dbus);
+static void purge_calls(mrp_dbus_t *dbus);
+static void handler_list_free_cb(void *key, void *entry);
+static void handler_free(handler_t *h);
+static int name_owner_change_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data);
+static void call_free(call_t *call);
+
+
+
+
+static int purge_filters(void *key, void *entry, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)user_data;
+ handler_list_t *l = (handler_list_t *)entry;
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ MRP_UNUSED(key);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_dbus_remove_filter(dbus,
+ h->sender, h->path, h->interface,
+ h->member, NULL);
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+void dbus_disconnect(mrp_dbus_t *dbus)
+{
+ if (dbus) {
+ mrp_htbl_remove(buses, dbus->conn, FALSE);
+
+ if (dbus->signals) {
+ mrp_htbl_foreach(dbus->signals, purge_filters, dbus);
+ mrp_htbl_destroy(dbus->signals, TRUE);
+ }
+ if (dbus->methods)
+ mrp_htbl_destroy(dbus->methods, TRUE);
+
+ if (dbus->conn != NULL) {
+ if (dbus->signal_filter)
+ dbus_connection_remove_filter(dbus->conn, dispatch_signal,
+ dbus);
+ if (dbus->register_fallback)
+ dbus_connection_unregister_object_path(dbus->conn, "/");
+ if (dbus->priv)
+ dbus_connection_close(dbus->conn);
+ dbus_connection_unref(dbus->conn);
+ }
+
+ purge_name_trackers(dbus);
+ purge_calls(dbus);
+
+ mrp_free(dbus->address);
+ dbus->conn = NULL;
+ dbus->ml = NULL;
+
+ mrp_free(dbus);
+ }
+}
+
+
+static int bus_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t bus_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (ptrdiff_t)key;
+ h >>= 2 * sizeof(key);
+
+ return h;
+}
+
+
+static int find_bus_by_spec(void *key, void *object, void *user_data)
+{
+ mrp_dbus_t *dbus = (mrp_dbus_t *)object;
+ bus_spec_t *spec = (bus_spec_t *)user_data;
+
+ MRP_UNUSED(key);
+
+ if (dbus->ml == spec->ml && !strcmp(dbus->address, spec->address))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static mrp_dbus_t *dbus_get(mrp_mainloop_t *ml, const char *address)
+{
+ mrp_htbl_config_t hcfg;
+ bus_spec_t spec;
+
+ if (buses == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = bus_cmp;
+ hcfg.hash = bus_hash;
+ hcfg.free = NULL;
+
+ buses = mrp_htbl_create(&hcfg);
+
+ return NULL;
+ }
+ else {
+ spec.ml = ml;
+ spec.address = address;
+
+ return mrp_htbl_find(buses, find_bus_by_spec, &spec);
+ }
+}
+
+
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ DBusError *errp)
+{
+ static struct DBusObjectPathVTable vtable = {
+ .message_function = dispatch_method
+ };
+
+ mrp_htbl_config_t hcfg;
+ mrp_dbus_t *dbus;
+
+ if ((dbus = dbus_get(ml, address)) != NULL)
+ return mrp_dbus_ref(dbus);
+
+ if ((dbus = mrp_allocz(sizeof(*dbus))) == NULL)
+ return NULL;
+
+ mrp_list_init(&dbus->calls);
+ mrp_list_init(&dbus->name_trackers);
+ mrp_refcnt_init(&dbus->refcnt);
+
+ dbus->ml = ml;
+
+
+ mrp_dbus_error_init(errp);
+
+ /*
+ * connect to the bus
+ */
+
+ if (!strcmp(address, "system"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SYSTEM, errp);
+ else if (!strcmp(address, "session"))
+ dbus->conn = dbus_bus_get(DBUS_BUS_SESSION, errp);
+ else {
+ dbus->conn = dbus_connection_open_private(address, errp);
+ dbus->priv = TRUE;
+
+ if (dbus->conn == NULL || !dbus_bus_register(dbus->conn, errp))
+ goto fail;
+ }
+
+ if (dbus->conn == NULL)
+ goto fail;
+
+ dbus->address = mrp_strdup(address);
+ dbus->unique_name = dbus_bus_get_unique_name(dbus->conn);
+
+ /*
+ * set up with mainloop
+ */
+
+ if (!mrp_dbus_setup_connection(ml, dbus->conn))
+ goto fail;
+
+ /*
+ * set up our message dispatchers and take our name on the bus
+ */
+
+ if (!dbus_connection_add_filter(dbus->conn, dispatch_signal, dbus, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up signal dispatching.");
+ goto fail;
+ }
+ dbus->signal_filter = TRUE;
+
+ if (!dbus_connection_register_fallback(dbus->conn, "/", &vtable, dbus)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to set up method dispatching.");
+ goto fail;
+ }
+ dbus->register_fallback = TRUE;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = handler_list_free_cb;
+
+ if ((dbus->methods = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS method table.");
+ goto fail;
+ }
+
+ if ((dbus->signals = mrp_htbl_create(&hcfg)) == NULL) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to create DBUS signal table.");
+ goto fail;
+ }
+
+
+ /*
+ * install handler for NameOwnerChanged for tracking clients/peers
+ */
+
+ if (!mrp_dbus_add_signal_handler(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name_owner_change_cb, NULL)) {
+ dbus_set_error(errp, DBUS_ERROR_FAILED,
+ "Failed to install NameOwnerChanged handler.");
+ goto fail;
+ }
+
+ /* install a 'safe' filter to avoid receiving all name change signals */
+ mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ DBUS_ADMIN_SERVICE, NULL);
+
+ mrp_list_init(&dbus->name_trackers);
+ dbus->call_id = 1;
+
+ if (mrp_htbl_insert(buses, dbus->conn, dbus))
+ return dbus;
+
+ fail:
+ dbus_disconnect(dbus);
+ return NULL;
+}
+
+
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus)
+{
+ return mrp_ref_obj(dbus, refcnt);
+}
+
+
+int mrp_dbus_unref(mrp_dbus_t *dbus)
+{
+ if (mrp_unref_obj(dbus, refcnt)) {
+ dbus_disconnect(dbus);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, DBusError *error)
+{
+ int flags, status;
+
+ mrp_dbus_error_init(error);
+
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ status = dbus_bus_request_name(dbus->conn, name, flags, error);
+
+ if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ return TRUE;
+ else {
+ if (status == DBUS_REQUEST_NAME_REPLY_EXISTS) {
+ if (error)
+ dbus_error_free(error);
+ dbus_set_error(error, DBUS_ERROR_FAILED, "name already taken");
+ }
+ return FALSE;
+ }
+}
+
+
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, DBusError *error)
+{
+ mrp_dbus_error_init(error);
+
+ if (dbus_bus_release_name(dbus->conn, name, error) != -1)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus)
+{
+ return dbus->unique_name;
+}
+
+static void name_owner_query_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data)
+{
+ name_tracker_t *t = (name_tracker_t *)data;
+ const char *owner;
+ int state;
+
+ if (t->cb != NULL) { /* tracker still active */
+ t->qid = 0;
+ state = dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_RETURN;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &owner,
+ DBUS_TYPE_INVALID))
+ owner = "<unknown>";
+
+ t->cb(dbus, t->name, state, owner, t->user_data);
+ }
+ else /* already requested to delete */
+ mrp_free(t);
+}
+
+
+static int name_owner_change_cb(mrp_dbus_t *dbus, DBusMessage *msg, void *data)
+{
+ const char *name, *prev, *next;
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ MRP_UNUSED(data);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return FALSE;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &prev,
+ DBUS_TYPE_STRING, &next,
+ DBUS_TYPE_INVALID))
+ return FALSE;
+
+ /*
+ * Notes: XXX TODO
+ * In principle t->cb could call mrp_dbus_forget for some other D-BUS
+ * address than name. If that happened to be n (== p->hook.next) this
+ * would result in a crash or memory corruption in the next iteration
+ * of this loop (when handling n). We can easily get around this
+ * problem by
+ *
+ * 1) adminstering in mrp_dbus_t that we're handing a NameOwnerChange
+ * 2) checking for this in mrp_dbus_forget_name and if it is the case
+ * only marking the affected entry for deletion
+ * 3) removing entries marked for deletion in this loop (or just
+ * ignoring them and making another pass in the end removing any
+ * such entry).
+ */
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (!strcmp(name, t->name))
+ t->cb(dbus, name, next && *next, next, t->user_data);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ name_tracker_t *t;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ if ((t->name = mrp_strdup(name)) != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+
+ if (mrp_dbus_install_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL)) {
+ mrp_list_append(&dbus->name_trackers, &t->hook);
+
+ t->qid = mrp_dbus_call(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, "GetNameOwner", 5000,
+ name_owner_query_cb, t,
+ DBUS_TYPE_STRING, &t->name,
+ DBUS_TYPE_INVALID);
+ return TRUE;
+ }
+ else {
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_dbus_remove_filter(dbus,
+ DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ name, NULL);
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ if (t->cb == cb && t->user_data == user_data && !strcmp(t->name,name)) {
+ mrp_list_delete(&t->hook);
+ mrp_free(t->name);
+
+ if (!t->qid)
+ mrp_free(t);
+ else {
+ t->cb = NULL;
+ t->user_data = NULL;
+ t->name = NULL;
+ }
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_name_trackers(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ name_tracker_t *t;
+
+ mrp_list_foreach(&dbus->name_trackers, p, n) {
+ t = mrp_list_entry(p, name_tracker_t, hook);
+
+ mrp_list_delete(p);
+ mrp_dbus_remove_filter(dbus, DBUS_ADMIN_SERVICE, DBUS_ADMIN_PATH,
+ DBUS_ADMIN_SERVICE, DBUS_NAME_CHANGED,
+ t->name, NULL);
+ mrp_free(t->name);
+ mrp_free(t);
+ }
+}
+
+
+static handler_t *handler_alloc(const char *sender, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_t *h;
+
+ if ((h = mrp_allocz(sizeof(*h))) != NULL) {
+ h->sender = mrp_strdup(sender);
+ h->path = mrp_strdup(path);
+ h->interface = mrp_strdup(interface);
+ h->member = mrp_strdup(member);
+
+ if ((path && !h->path) || !h->interface || !h->member) {
+ handler_free(h);
+ return NULL;
+ }
+
+ h->handler = handler;
+ h->user_data = user_data;
+
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static void handler_free(handler_t *h)
+{
+ if (h != NULL) {
+ mrp_free(h->sender);
+ mrp_free(h->path);
+ mrp_free(h->interface);
+ mrp_free(h->member);
+
+ mrp_free(h);
+ }
+}
+
+
+static handler_list_t *handler_list_alloc(const char *member)
+{
+ handler_list_t *l;
+
+ if ((l = mrp_allocz(sizeof(*l))) != NULL) {
+ if ((l->member = mrp_strdup(member)) != NULL)
+ mrp_list_init(&l->handlers);
+ else {
+ mrp_free(l);
+ l = NULL;
+ }
+ }
+
+ return l;
+}
+
+
+static inline void handler_list_free(handler_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+ mrp_list_delete(p);
+ handler_free(h);
+ }
+
+ mrp_free(l->member);
+ mrp_free(l);
+}
+
+
+static void handler_list_free_cb(void *key, void *entry)
+{
+ MRP_UNUSED(key);
+
+ handler_list_free((handler_list_t *)entry);
+}
+
+
+static inline int handler_specificity(handler_t *h)
+{
+ int score = 0;
+
+ if (h->path && *h->path)
+ score |= 0x4;
+ if (h->interface && *h->interface)
+ score |= 0x2;
+ if (h->member && *h->member)
+ score |= 0x1;
+
+ return score;
+}
+
+
+static void handler_list_insert(handler_list_t *l, handler_t *handler)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+ int score;
+
+ score = handler_specificity(handler);
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (score >= handler_specificity(h)) {
+ mrp_list_append(h->hook.prev, &handler->hook); /* add before h */
+ return;
+ }
+ }
+
+ mrp_list_append(&l->handlers, &handler->hook);
+}
+
+
+static handler_t *handler_list_lookup(handler_list_t *l, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (h->handler == handler && user_data == h->user_data &&
+ path && !strcmp(path, h->path) &&
+ interface && !strcmp(interface, h->interface) &&
+ member && !strcmp(member, h->member))
+ return h;
+ }
+
+ return NULL;
+}
+
+
+static handler_t *handler_list_find(handler_list_t *l, const char *path,
+ const char *interface, const char *member)
+{
+#define MATCHES(h, field) (!*field || !*h->field || !strcmp(field, h->field))
+ mrp_list_hook_t *p, *n;
+ handler_t *h;
+
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h, path) && MATCHES(h, interface) && MATCHES(h, member))
+ return h;
+ }
+
+ return NULL;
+#undef MATCHES
+}
+
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL) {
+ if ((methods = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ mrp_htbl_insert(dbus->methods, methods->member, methods);
+ }
+
+ m = handler_alloc(NULL, path, interface, member, handler, user_data);
+ if (m != NULL) {
+ handler_list_insert(methods, m);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data)
+{
+ handler_list_t *methods;
+ handler_t *m;
+
+ if ((methods = mrp_htbl_lookup(dbus->methods, (void *)member)) == NULL)
+ return FALSE;
+
+ m = handler_list_lookup(methods, path, interface, member,
+ handler, user_data);
+ if (m != NULL) {
+ mrp_list_delete(&m->hook);
+ handler_free(m);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL) {
+ if ((signals = handler_list_alloc(member)) == NULL)
+ return FALSE;
+
+ if (!mrp_htbl_insert(dbus->signals, signals->member, signals)) {
+ handler_list_free(signals);
+ return FALSE;
+ }
+ }
+
+ s = handler_alloc(sender, path, interface, member, handler, user_data);
+ if (s != NULL) {
+ handler_list_insert(signals, s);
+ return TRUE;
+ }
+ else {
+ handler_free(s);
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, signals->member, TRUE);
+ return FALSE;
+ }
+}
+
+
+
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data)
+{
+ handler_list_t *signals;
+ handler_t *s;
+
+ MRP_UNUSED(sender);
+
+ if ((signals = mrp_htbl_lookup(dbus->signals, (void *)member)) == NULL)
+ return FALSE;
+
+ s = handler_list_lookup(signals, path, interface, member,
+ handler, user_data);
+ if (s != NULL) {
+ mrp_list_delete(&s->hook);
+ handler_free(s);
+
+ if (mrp_list_empty(&signals->handlers))
+ mrp_htbl_remove(dbus->signals, (void *)member, TRUE);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+
+int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int success;
+
+
+ if (mrp_dbus_add_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data)) {
+ va_start(ap, member);
+ success = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ if (success)
+ return TRUE;
+ else
+ mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler, void *user_data,
+ const char *sender, const char *path,
+ const char *interface, const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ status = mrp_dbus_del_signal_handler(dbus, sender, path, interface, member,
+ handler, user_data);
+ va_start(ap, member);
+ status &= mrp_dbus_remove_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+#define ADD_TAG(tag, value, ...) do { \
+ if (value != NULL) { \
+ l = snprintf(p, n, "%s%s='%s'", p == filter ? "" : ",", \
+ tag, value); \
+ if (l >= n) \
+ do { __VA_ARGS__; } while (0); \
+ n -= l; \
+ p += l; \
+ } \
+ } while (0)
+
+ va_list ap;
+ DBusError error;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal" , return FALSE);
+ ADD_TAG("sender" , sender , return FALSE);
+ ADD_TAG("path" , path , return FALSE);
+ ADD_TAG("interface", interface, return FALSE);
+ ADD_TAG("member" , member , return FALSE);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val, { va_end(ap); return FALSE; });
+ i++;
+ }
+ va_end(ap);
+
+ dbus_error_init(&error);
+ dbus_bus_add_match(dbus->conn, filter, &error);
+
+ if (dbus_error_is_set(&error)) {
+ mrp_log_error("Failed to install filter '%s' (error: %s).", filter,
+ mrp_dbus_errmsg(&error));
+ dbus_error_free(&error);
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+
+}
+
+
+int mrp_dbus_install_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_install_filterv(dbus,
+ sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, va_list args)
+{
+ va_list ap;
+ char filter[1024], *p, argn[16], *val;
+ int n, l, i;
+
+ p = filter;
+ n = sizeof(filter);
+
+ ADD_TAG("type" , "signal" , return FALSE);
+ ADD_TAG("sender" , sender , return FALSE);
+ ADD_TAG("path" , path , return FALSE);
+ ADD_TAG("interface", interface, return FALSE);
+ ADD_TAG("member" , member , return FALSE);
+
+ va_copy(ap, args);
+ i = 0;
+ while ((val = va_arg(ap, char *)) != NULL) {
+ snprintf(argn, sizeof(argn), "arg%d", i);
+ ADD_TAG(argn, val, { va_end(ap); return FALSE; });
+ i++;
+ }
+ va_end(ap);
+
+ dbus_bus_remove_match(dbus->conn, filter, NULL);
+ return TRUE;
+#undef ADD_TAG
+}
+
+
+int mrp_dbus_remove_filter(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, ...)
+{
+ va_list ap;
+ int status;
+
+ va_start(ap, member);
+ status = mrp_dbus_remove_filterv(dbus, sender, path, interface, member, ap);
+ va_end(ap);
+
+ return status;
+}
+
+
+
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ handler_list_t *l;
+ handler_t *h;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->methods, (void *)member)) != NULL) {
+ retry:
+ if ((h = handler_list_find(l, path, interface, member)) != NULL) {
+ if (h->handler(dbus, msg, h->user_data))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+ }
+ else {
+ if ((l = mrp_htbl_lookup(dbus->methods, "")) != NULL)
+ goto retry;
+ }
+
+ mrp_debug("Unhandled method path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+static DBusHandlerResult dispatch_signal(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define MATCHES(h, field) (!*field || !h->field || !*h->field || \
+ !strcmp(field, h->field))
+
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+
+ mrp_dbus_t *dbus = (mrp_dbus_t *)data;
+ mrp_list_hook_t *p, *n;
+ handler_list_t *l;
+ handler_t *h;
+ int retried = FALSE;
+ int handled = FALSE;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ mrp_debug("%s(path='%s', interface='%s', member='%s')...",
+ __FUNCTION__,
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));
+
+ if ((l = mrp_htbl_lookup(dbus->signals, (void *)member)) != NULL) {
+ retry:
+ mrp_list_foreach(&l->handlers, p, n) {
+ h = mrp_list_entry(p, handler_t, hook);
+
+ if (MATCHES(h,path) && MATCHES(h,interface) && MATCHES(h,member)) {
+ h->handler(dbus, msg, h->user_data);
+ handled = TRUE;
+ }
+ }
+ }
+
+ if (!retried) {
+ if ((l = mrp_htbl_lookup(dbus->signals, "")) != NULL) {
+ retried = TRUE;
+ goto retry;
+ }
+ }
+
+ if (!handled)
+ mrp_debug("Unhandled signal path=%s, %s.%s.", SAFESTR(path),
+ SAFESTR(interface), SAFESTR(member));
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+#undef MATCHES
+#undef SAFESTR
+}
+
+
+static void call_reply_cb(DBusPendingCall *pend, void *user_data)
+{
+ call_t *call = (call_t *)user_data;
+ DBusMessage *reply;
+
+ reply = dbus_pending_call_steal_reply(pend);
+
+ call->pend = NULL;
+ mrp_list_delete(&call->hook);
+
+ call->cb(call->dbus, reply, call->user_data);
+
+ dbus_message_unref(reply);
+ dbus_pending_call_unref(pend);
+
+ call_free(call);
+}
+
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, int type, ...)
+{
+ va_list ap;
+ int32_t id;
+ call_t *call;
+ DBusMessage *msg;
+ DBusPendingCall *pend;
+ int success;
+
+ call = NULL;
+ pend = NULL;
+
+ msg = dbus_message_new_method_call(dest, path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (cb == NULL) {
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ dbus_message_unref(msg);
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data, DBusMessage *msg)
+{
+ int32_t id;
+ call_t *call;
+ DBusPendingCall *pend;
+ int method;
+
+ call = NULL;
+ pend = NULL;
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL) {
+ if (cb != NULL)
+ goto fail;
+ else
+ method = FALSE;
+ }
+ else
+ method = TRUE;
+
+ if (cb != NULL) {
+ if ((call = mrp_allocz(sizeof(*call))) != NULL) {
+ mrp_list_init(&call->hook);
+
+ call->dbus = dbus;
+ call->id = dbus->call_id++;
+ call->cb = cb;
+ call->user_data = user_data;
+
+ id = call->id;
+ }
+ else
+ goto fail;
+ }
+ else
+ id = dbus->call_id++;
+
+ if (!dbus_message_set_destination(msg, dest))
+ goto fail;
+ if (!dbus_message_set_path(msg, path))
+ goto fail;
+ if (!dbus_message_set_interface(msg, interface))
+ goto fail;
+ if (!dbus_message_set_member(msg, member))
+ goto fail;
+
+ if (cb == NULL) {
+ if (method)
+ dbus_message_set_no_reply(msg, TRUE);
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+ }
+ else {
+ if (!dbus_connection_send_with_reply(dbus->conn, msg, &pend, timeout))
+ goto fail;
+
+ if (!dbus_pending_call_set_notify(pend, call_reply_cb, call, NULL))
+ goto fail;
+ }
+
+ if (cb != NULL) {
+ mrp_list_append(&dbus->calls, &call->hook);
+ call->pend = pend;
+ }
+
+ return id;
+
+ fail:
+ if (pend != NULL)
+ dbus_pending_call_unref(pend);
+
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ call_free(call);
+
+ return 0;
+}
+
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, DBusMessage *msg)
+{
+ return dbus_connection_send(dbus->conn, msg, NULL);
+}
+
+
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ if (call->id == id) {
+ mrp_list_delete(p);
+
+ dbus_pending_call_cancel(call->pend);
+ dbus_pending_call_unref(call->pend);
+ call->pend = NULL;
+
+ call_free(call);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, DBusMessage *msg, int type, ...)
+{
+ va_list ap;
+ DBusMessage *rpl;
+ int success;
+
+ rpl = dbus_message_new_method_return(msg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, DBusMessage *msg,
+ const char *errname, const char *errmsg, int type, ...)
+{
+ va_list ap;
+ DBusMessage *rpl;
+ int success;
+
+ rpl = dbus_message_new_error(msg, errname, errmsg);
+
+ if (rpl == NULL)
+ return FALSE;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(rpl, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, rpl, NULL))
+ goto fail;
+
+ dbus_message_unref(rpl);
+
+ return TRUE;
+
+ fail:
+ if(rpl != NULL)
+ dbus_message_unref(rpl);
+
+ return FALSE;
+}
+
+
+static void call_free(call_t *call)
+{
+ if (call != NULL)
+ mrp_free(call);
+}
+
+
+static void purge_calls(mrp_dbus_t *dbus)
+{
+ mrp_list_hook_t *p, *n;
+ call_t *call;
+
+ mrp_list_foreach(&dbus->calls, p, n) {
+ call = mrp_list_entry(p, call_t, hook);
+
+ mrp_list_delete(&call->hook);
+
+ if (call->pend != NULL)
+ dbus_pending_call_unref(call->pend);
+
+ mrp_free(call);
+ }
+}
+
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...)
+{
+ va_list ap;
+ DBusMessage *msg;
+ int success;
+
+ msg = dbus_message_new_signal(path, interface, member);
+
+ if (msg == NULL)
+ return 0;
+
+ if (type == DBUS_TYPE_INVALID)
+ success = TRUE;
+ else {
+ va_start(ap, type);
+ success = dbus_message_append_args_valist(msg, type, ap);
+ va_end(ap);
+ }
+
+ if (!success)
+ goto fail;
+
+ if (dest && *dest && !dbus_message_set_destination(msg, dest))
+ goto fail;
+
+ if (!dbus_connection_send(dbus->conn, msg, NULL))
+ goto fail;
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+
+ fail:
+ /*
+ * XXX TODO: Hmm... IIRC, libdbus unrefs messages upon failure. If it
+ * was really so, this would corrupt/crash. Check this from
+ * libdbus code.
+ */
+ if(msg != NULL)
+ dbus_message_unref(msg);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DBUS_H__
+#define __MURPHY_DBUS_H__
+
+#include <murphy/common/mainloop.h>
+#include <dbus/dbus.h>
+
+#define MRP_AF_DBUS 0xDB
+
+/** Our D-BUS (connection) abstraction. */
+struct mrp_dbus_s;
+typedef struct mrp_dbus_s mrp_dbus_t;
+
+/** D-BUS method or signal callback type. */
+typedef int (*mrp_dbus_handler_t)(mrp_dbus_t *, DBusMessage *, void *);
+
+/** Create a new connection to the given bus. */
+mrp_dbus_t *mrp_dbus_connect(mrp_mainloop_t *ml, const char *address,
+ DBusError *errp);
+#define mrp_dbus_get mrp_dbus_connect
+
+
+/** Set up a DBusConnection with a mainloop. */
+int mrp_dbus_setup_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+/** Increase the reference count of the given DBus (connection). */
+mrp_dbus_t *mrp_dbus_ref(mrp_dbus_t *dbus);
+
+/** Decrease the reference count of the given DBus (connection). */
+int mrp_dbus_unref(mrp_dbus_t *dbus);
+
+/** Acquire the given name on the given bus (connection). */
+int mrp_dbus_acquire_name(mrp_dbus_t *dbus, const char *name, DBusError *error);
+
+/** Release the given name on the given bus (connection). */
+int mrp_dbus_release_name(mrp_dbus_t *dbus, const char *name, DBusError *error);
+
+typedef void (*mrp_dbus_name_cb_t)(mrp_dbus_t *, const char *, int,
+ const char *, void *);
+int mrp_dbus_follow_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+int mrp_dbus_forget_name(mrp_dbus_t *dbus, const char *name,
+ mrp_dbus_name_cb_t cb, void *user_data);
+
+int mrp_dbus_export_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+int mrp_dbus_remove_method(mrp_dbus_t *dbus, const char *path,
+ const char *interface, const char *member,
+ mrp_dbus_handler_t handler, void *user_data);
+
+MRP_NULLTERM int mrp_dbus_subscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+MRP_NULLTERM int mrp_dbus_unsubscribe_signal(mrp_dbus_t *dbus,
+ mrp_dbus_handler_t handler,
+ void *user_data,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+MRP_NULLTERM int mrp_dbus_install_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+int mrp_dbus_install_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+MRP_NULLTERM int mrp_dbus_remove_filter(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member, ...);
+
+int mrp_dbus_remove_filterv(mrp_dbus_t *dbus,
+ const char *sender,
+ const char *path,
+ const char *interface,
+ const char *member,
+ va_list ap);
+
+int mrp_dbus_add_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+int mrp_dbus_del_signal_handler(mrp_dbus_t *dbus, const char *sender,
+ const char *path, const char *interface,
+ const char *member, mrp_dbus_handler_t handler,
+ void *user_data);
+
+typedef void (*mrp_dbus_reply_cb_t)(mrp_dbus_t *dbus, DBusMessage *reply,
+ void *user_data);
+
+int32_t mrp_dbus_call(mrp_dbus_t *dbus, const char *dest,
+ const char *path, const char *interface,
+ const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ int dbus_type, ...);
+int mrp_dbus_call_cancel(mrp_dbus_t *dbus, int32_t id);
+
+int mrp_dbus_reply(mrp_dbus_t *dbus, DBusMessage *msg, int type, ...);
+
+int mrp_dbus_reply_error(mrp_dbus_t *dbus, DBusMessage *msg,
+ const char *errname, const char *errmsg,
+ int type, ...);
+
+int mrp_dbus_signal(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int type, ...);
+
+int32_t mrp_dbus_send(mrp_dbus_t *dbus, const char *dest, const char *path,
+ const char *interface, const char *member, int timeout,
+ mrp_dbus_reply_cb_t cb, void *user_data,
+ DBusMessage *msg);
+
+int mrp_dbus_send_msg(mrp_dbus_t *dbus, DBusMessage *msg);
+
+const char *mrp_dbus_get_unique_name(mrp_dbus_t *dbus);
+
+static inline void mrp_dbus_error_init(DBusError *error)
+{
+ /*
+ * Prevent libdbus error messages for NULL DBusError's...
+ */
+ if (error != NULL)
+ dbus_error_init(error);
+}
+
+
+static inline const char *mrp_dbus_errmsg(DBusError *err)
+{
+ if (err && dbus_error_is_set(err))
+ return err->message;
+ else
+ return "unknown DBUS error";
+}
+
+
+#endif /* __MURPHY_DBUS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LIST_H__
+#define __MURPHY_LIST_H__
+
+#include <murphy/common/macros.h>
+
+
+MRP_CDECL_BEGIN
+
+
+/** \file
+ * A simple doubly-linked circular list implementation, obviously inspired
+ * by the linux kernel.
+ */
+
+
+/** A list hook. Used both a list head and to hook up objects to the list. */
+typedef struct mrp_list_hook_s mrp_list_hook_t;
+struct mrp_list_hook_s {
+ mrp_list_hook_t *prev;
+ mrp_list_hook_t *next;
+};
+
+/** Macro to initialize a list to be empty. */
+#define MRP_LIST_INIT(list) { .prev = &(list), .next = &(list) }
+
+/** Macro to define a list and initialize it to be empty. */
+#define MRP_LIST_HOOK(list) mrp_list_hook_t list = MRP_LIST_INIT(list)
+
+/** Initialize a list to be empty. */
+static inline void mrp_list_init(mrp_list_hook_t *list)
+{
+ list->prev = list->next = list;
+}
+
+/** Check if a list is empty. */
+static inline int mrp_list_empty(mrp_list_hook_t *list)
+{
+ if (list->next == list->prev) {
+ if (list->next == list)
+ return TRUE;
+
+#ifdef __MURPHY_LIST_ALLOW_NULL
+ if (!list->next)
+ return TRUE;
+#endif
+ }
+
+ return FALSE;
+}
+
+/** Append a new item to a list (add it after the last item). */
+static inline void mrp_list_append(mrp_list_hook_t *list, mrp_list_hook_t *item)
+{
+ if (mrp_list_empty(list)) {
+ list->next = list->prev = item;
+ item->next = item->prev = list;
+ }
+ else {
+ mrp_list_hook_t *prev = list->prev;
+
+ prev->next = item;
+ item->prev = prev;
+ item->next = list;
+ list->prev = item;
+ }
+}
+
+/** Prepend a new item to a list (add it before the first item). */
+static inline void mrp_list_prepend(mrp_list_hook_t *list,
+ mrp_list_hook_t *item)
+{
+ if (mrp_list_empty(list)) {
+ list->next = list->prev = item;
+ item->next = item->prev = list;
+ }
+ else {
+ mrp_list_hook_t *next = list->next;
+
+ list->next = item;
+ item->prev = list;
+ item->next = next;
+ next->prev = item;
+ }
+}
+
+/** Insert a new item to the list before a given item. */
+static inline void mrp_list_insert_before(mrp_list_hook_t *next,
+ mrp_list_hook_t *item)
+{
+ mrp_list_append(next, item);
+}
+
+/** Insert a new item to the list after a given item. */
+static inline void mrp_list_insert_after(mrp_list_hook_t *prev,
+ mrp_list_hook_t *item)
+{
+ mrp_list_prepend(prev, item);
+}
+
+/** Delete the given item from the list. */
+static inline void mrp_list_delete(mrp_list_hook_t *item)
+{
+ mrp_list_hook_t *prev, *next;
+
+ if (!mrp_list_empty(item)) {
+ prev = item->prev;
+ next = item->next;
+
+ prev->next = next;
+ next->prev = prev;
+
+ item->prev = item->next = item;
+ }
+}
+
+/** Reattach a list to a new hook. Initialize old hook to be empty. */
+static inline void mrp_list_move(mrp_list_hook_t *new_hook,
+ mrp_list_hook_t *old_hook)
+{
+ *new_hook = *old_hook;
+
+ new_hook->next->prev = new_hook;
+ new_hook->prev->next = new_hook;
+
+ mrp_list_init(old_hook);
+}
+
+
+/** Update a list when the address of a hook has changed (eg. by realloc). */
+static inline void mrp_list_update_address(mrp_list_hook_t *new_addr,
+ mrp_list_hook_t *old_addr)
+{
+ mrp_list_hook_t *prev, *next;
+ ptrdiff_t diff;
+
+ diff = new_addr - old_addr;
+ prev = new_addr->prev;
+ next = new_addr->next;
+
+ prev->next += diff;
+ next->prev += diff;
+}
+
+
+/** Macro to iterate through a list (current item safe to remove). */
+#define mrp_list_foreach(list, p, n) \
+ if ((list)->next != NULL) \
+ for (p = (list)->next, n = p->next; p != (list); p = n, n = n->next)
+
+/** Macro to iterate through a list backwards (current item safe to remove). */
+#define mrp_list_foreach_back(list, p, n) \
+ if ((list)->prev != NULL) \
+ for (p = (list)->prev, n = p->prev; p != (list); p = n, n = n->prev)
+
+/** Macro to get a pointer to a embedding structure from a list pointer. */
+#ifndef __cplusplus
+# define PTR_ARITH_TYPE void
+#else
+# define PTR_ARITH_TYPE char
+#endif
+
+#define mrp_list_entry(ptr, type, member) \
+ (type *)(((PTR_ARITH_TYPE *)(ptr)) - MRP_OFFSET(type, member))
+
+
+MRP_CDECL_END
+
+
+#endif /* __MURPHY_LIST_H__ */
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+
+typedef struct {
+ mrp_list_hook_t hook;
+ char *name;
+ mrp_logger_t logger;
+ void *data;
+ int builtin;
+} log_target_t;
+
+static log_target_t stderr_target;
+static log_target_t stdout_target;
+static log_target_t syslog_target;
+static log_target_t file_target;
+
+static MRP_LIST_HOOK(log_targets);
+static int log_mask = MRP_LOG_MASK_ERROR;
+static log_target_t *log_target = NULL;
+
+
+mrp_log_mask_t mrp_log_parse_levels(const char *levels)
+{
+ const char *p;
+ mrp_log_mask_t mask;
+
+ mask = 0;
+
+ if (levels == NULL) {
+ if (mask == 0)
+ mask = 1;
+ else {
+ mask <<= 1;
+ mask |= 1;
+ }
+ }
+ else {
+ p = levels;
+ while (p && *p) {
+# define MATCHES(s, l) (!strcmp(s, l) || \
+ !strncmp(s, l",", sizeof(l",") - 1))
+
+ if (MATCHES(p, "info"))
+ mask |= MRP_LOG_MASK_INFO;
+ else if (MATCHES(p, "error"))
+ mask |= MRP_LOG_MASK_ERROR;
+ else if (MATCHES(p, "warning"))
+ mask |= MRP_LOG_MASK_WARNING;
+ else if (MATCHES(p, "none") || MATCHES(p, "off"))
+ mask = 0;
+ else
+ return -1;
+
+ if ((p = strchr(p, ',')) != NULL)
+ p += 1;
+
+# undef MATCHES
+ }
+ }
+
+ return mask;
+}
+
+
+const char *mrp_log_parse_target(const char *target)
+{
+ return target;
+}
+
+
+const char *mrp_log_dump_mask(mrp_log_mask_t mask, char *buf, size_t size)
+{
+ char *p, *t;
+ int n, l;
+
+ if (!mask)
+ return "none";
+
+ p = buf;
+ l = size;
+
+ t = "";
+ *p = '\0';
+
+ if (mask & MRP_LOG_MASK_INFO) {
+ n = snprintf(p, l, "info");
+ p += n;
+ l -= n;
+ t = ",";
+ }
+ if (mask & MRP_LOG_MASK_WARNING) {
+ n = snprintf(p, l, "%swarning", t);
+ p += n;
+ l -= n;
+ t = ",";
+ }
+ if (mask & MRP_LOG_MASK_ERROR) {
+ n = snprintf(p, l, "%serror", t);
+ p += n;
+ l -= n;
+ t = ",";
+ }
+
+ return buf;
+}
+
+
+mrp_log_mask_t mrp_log_enable(mrp_log_mask_t enabled)
+{
+ mrp_log_mask_t old_mask = log_mask;
+
+ log_mask |= enabled;
+
+ return old_mask;
+}
+
+
+mrp_log_mask_t mrp_log_disable(mrp_log_mask_t disabled)
+{
+ mrp_log_mask_t old_mask = log_mask;
+
+ log_mask &= ~disabled;
+
+ return old_mask;
+}
+
+
+mrp_log_mask_t mrp_log_set_mask(mrp_log_mask_t enabled)
+{
+ mrp_log_mask_t old_mask = log_mask;
+
+ log_mask = enabled;
+
+ return old_mask;
+}
+
+
+static log_target_t *find_target(const char *name)
+{
+ log_target_t *t;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&log_targets, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (t->name == name || !strcmp(t->name, name))
+ return t;
+ }
+
+ return NULL;
+}
+
+
+int mrp_log_set_target(const char *name)
+{
+ log_target_t *target;
+ const char *path;
+
+ if (!strncmp(name, "file:", 5)) {
+ path = name + 5;
+ name = "file";
+ }
+ else
+ path = NULL;
+
+ target = find_target(name);
+
+ if (target == NULL || (target == &file_target && path == NULL))
+ return FALSE;
+
+ /* close files opened by us, if any */
+ if (log_target == &file_target) {
+ if (file_target.data != NULL) {
+ fclose(file_target.data);
+ file_target.data = NULL;
+ }
+ }
+
+ log_target = target;
+
+ /* open any new files if we have to */
+ if (target == &file_target) {
+ target->data = fopen(path, "a");
+
+ if (target->data == NULL) {
+ log_target = &syslog_target;
+
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+const char *mrp_log_get_target(void)
+{
+ return log_target->name;
+}
+
+
+int mrp_log_get_targets(const char **targets, size_t size)
+{
+ mrp_list_hook_t *p, *n;
+ log_target_t *t;
+ int cnt;
+
+ cnt = 0;
+ mrp_list_foreach(&log_targets, p, n) {
+ if (cnt == (int)size)
+ break;
+
+ t = mrp_list_entry(p, typeof(*t), hook);
+ targets[cnt++] = t->name;
+ }
+
+ return cnt;
+}
+
+
+int mrp_log_register_target(const char *name, mrp_logger_t logger, void *data)
+{
+ log_target_t *target;
+
+ if (find_target(name) != NULL)
+ return FALSE;
+
+ target = mrp_allocz(sizeof(*target));
+
+ mrp_list_init(&target->hook);
+ target->name = mrp_strdup(name);
+ target->logger = logger;
+ target->data = data;
+
+ if (target->name != NULL) {
+ mrp_list_append(&log_targets, &target->hook);
+
+ return TRUE;
+ }
+ else {
+ mrp_free(target);
+
+ return FALSE;
+ }
+}
+
+
+int mrp_log_unregister_target(const char *name)
+{
+ log_target_t *target;
+
+ target = find_target(name);
+
+ if (target == NULL || target->builtin)
+ return FALSE;
+
+ if (log_target == target)
+ log_target = &stderr_target;
+
+ mrp_list_delete(&target->hook);
+ mrp_free(target->name);
+ mrp_free(target);
+
+ return TRUE;
+}
+
+
+static void log_msgv(void *data, mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format,
+ va_list ap)
+{
+ FILE *fp = data;
+ int lvl;
+ const char *prefix;
+ char prfx[2*1024];
+
+ if (!(log_mask & (1 << level)))
+ return;
+
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+
+ switch (level) {
+ case MRP_LOG_ERROR: lvl = LOG_ERR; prefix = "E: "; break;
+ case MRP_LOG_WARNING: lvl = LOG_WARNING; prefix = "W: "; break;
+ case MRP_LOG_INFO: lvl = LOG_INFO; prefix = "I: "; break;
+ case MRP_LOG_DEBUG: lvl = LOG_INFO;
+ snprintf(prfx, sizeof(prfx) - 1, "D: [%s] ", func);
+ prfx[sizeof(prfx)-1] = '\0';
+ prefix = prfx;
+ break;
+ default:
+ return;
+ }
+
+ if (fp == NULL)
+ vsyslog(lvl, format, ap);
+ else {
+ fputs(prefix, fp);
+ vfprintf(fp, format, ap); fputs("\n", fp);
+ fflush(fp);
+ }
+}
+
+
+void mrp_log_msgv(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format,
+ va_list ap)
+{
+ static int busy = 0;
+ mrp_logger_t logger = log_target->logger;
+ void *data = log_target->data;
+
+ if (MRP_UNLIKELY(busy != 0))
+ return;
+
+ if (!(log_mask & (1 << level)))
+ return;
+
+ busy++;
+ logger(data, level, file, line, func, format, ap);
+ busy--;
+}
+
+
+void mrp_log_msg(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, ...)
+{
+ va_list ap;
+
+ if (!(log_mask & (1 << level)))
+ return;
+
+ va_start(ap, format);
+ mrp_log_msgv(level, file, line, func, format, ap);
+ va_end(ap);
+}
+
+
+/*
+ * workaround for not being able to initialize log_fp to stderr
+ */
+
+static __attribute__((constructor)) void set_default_logging(void)
+{
+ mrp_list_init(&stderr_target.hook);
+ stderr_target.name = "stderr";
+ stderr_target.logger = log_msgv;
+ stderr_target.data = stderr;
+ stderr_target.builtin = TRUE;
+
+ mrp_list_init(&stdout_target.hook);
+ stdout_target.name = "stdout";
+ stdout_target.logger = log_msgv;
+ stdout_target.data = stdout;
+ stdout_target.builtin = TRUE;
+
+ mrp_list_init(&syslog_target.hook);
+ syslog_target.name = "syslog";
+ syslog_target.logger = log_msgv;
+ syslog_target.data = NULL;
+ syslog_target.builtin = TRUE;
+
+ mrp_list_init(&file_target.hook);
+ file_target.name = "file";
+ file_target.logger = log_msgv;
+ file_target.data = NULL;
+ file_target.builtin = TRUE;
+
+ mrp_list_prepend(&log_targets, &file_target.hook);
+ mrp_list_prepend(&log_targets, &syslog_target.hook);
+ mrp_list_prepend(&log_targets, &stderr_target.hook);
+ mrp_list_prepend(&log_targets, &stdout_target.hook);
+
+ log_target = &stderr_target;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LOG_H__
+#define __MURPHY_LOG_H__
+
+/** \file
+ * Logging functions and macros.
+ */
+
+#include <stdarg.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_LOG_NAME_ERROR "error" /**< name for error level */
+#define MRP_LOG_NAME_WARNING "warning" /**< name for warning level */
+#define MRP_LOG_NAME_INFO "info" /**< name for info level */
+#define MRP_LOG_NAME_DEBUG "debug" /**< name for debug level */
+
+
+/**
+ * Logging levels.
+ */
+typedef enum {
+ MRP_LOG_ERROR = 0, /**< error log level */
+ MRP_LOG_WARNING, /**< warning log level */
+ MRP_LOG_INFO, /**< info log level */
+ MRP_LOG_DEBUG, /**< debug log level */
+} mrp_log_level_t;
+
+
+/**
+ * Logging masks.
+ */
+typedef enum {
+ MRP_LOG_MASK_ERROR = 0x01, /**< error logging mask */
+ MRP_LOG_MASK_WARNING = 0x02, /**< warning logging mask */
+ MRP_LOG_MASK_INFO = 0x04, /**< info logging mask */
+ MRP_LOG_MASK_DEBUG = 0x08, /**< debug logging mask */
+} mrp_log_mask_t;
+
+#define MRP_LOG_MASK(level) (1 << ((level)-1)) /**< mask of level */
+#define MRP_LOG_UPTO(level) ((1 << (level+1))-1) /**< mask up to level */
+
+
+/** Parse a string of comma-separated log level names to a log mask. */
+mrp_log_mask_t mrp_log_parse_levels(const char *levels);
+
+/** Write the given log mask as a string to the given buffer. */
+const char *mrp_log_dump_mask(mrp_log_mask_t mask, char *buf, size_t size);
+
+/** Clear current logging level and enable levels in mask. */
+mrp_log_mask_t mrp_log_set_mask(mrp_log_mask_t mask);
+
+/** Enable logging for levels in mask. */
+mrp_log_mask_t mrp_log_enable(mrp_log_mask_t mask);
+
+/** Disable logging for levels in mask. */
+mrp_log_mask_t mrp_log_disable(mrp_log_mask_t mask);
+
+/** Get the current logging level mask. */
+#define mrp_log_get_mask() mrp_log_disable(0)
+
+/**
+ * Logging target names.
+ */
+#define MRP_LOG_NAME_STDOUT "stdout"
+#define MRP_LOG_NAME_STDERR "stderr"
+#define MRP_LOG_NAME_SYSLOG "syslog"
+
+/**
+ * Logging targets.
+ */
+#define MRP_LOG_TO_STDOUT "stdout"
+#define MRP_LOG_TO_STDERR "stderr"
+#define MRP_LOG_TO_SYSLOG "syslog"
+#define MRP_LOG_TO_FILE(path) ((const char *)(path))
+
+
+/** Parse a log target name to MRP_LOG_TO_*. */
+const char *mrp_log_parse_target(const char *target);
+
+/** Set logging target. */
+int mrp_log_set_target(const char *target);
+
+/** Get the current log target. */
+const char *mrp_log_get_target(void);
+
+/** Get all available logging targets. */
+int mrp_log_get_targets(const char **targets, size_t size);
+
+/** Log an error. */
+#define mrp_log_error(fmt, args...) \
+ mrp_log_msg(MRP_LOG_ERROR, __LOC__, fmt , ## args)
+
+/** Log a warning. */
+#define mrp_log_warning(fmt, args...) \
+ mrp_log_msg(MRP_LOG_WARNING, __LOC__, fmt , ## args)
+
+/** Log an informational message. */
+#define mrp_log_info(fmt, args...) \
+ mrp_log_msg(MRP_LOG_INFO, __LOC__, fmt , ## args)
+
+/** Generic logging function. */
+void mrp_log_msg(mrp_log_level_t level,
+ const char *file, int line, const char *func,
+ const char *format, ...) MRP_PRINTF_LIKE(5, 6);
+
+/** Generic logging function for easy wrapping. */
+void mrp_log_msgv(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, va_list ap);
+
+/** Type for custom logging functions. */
+typedef void (*mrp_logger_t)(void *user_data,
+ mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format,
+ va_list ap);
+
+/** Register a new logging target. */
+int mrp_log_register_target(const char *name, mrp_logger_t logger,
+ void *user_data);
+
+/** Unregister the given logging target. */
+int mrp_log_unregister_target(const char *name);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_LOG_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MACROS_H__
+#define __MURPHY_MACROS_H__
+
+#include <stddef.h>
+
+#ifndef FALSE
+# define FALSE 0
+# define TRUE (!FALSE)
+#endif
+
+#ifdef __cplusplus
+# define typeof(expr) decltype(expr)
+#endif
+
+/** Align ptr to multiple of align. */
+#define MRP_ALIGN(ptr, align) (((ptr) + ((align)-1)) & ~((align)-1))
+
+/** Get the offset of the given member in a struct/union of the given type. */
+#define MRP_OFFSET(type, member) ((ptrdiff_t)(&(((type *)0)->member)))
+
+/** Determine the dimension of the given array. */
+#define MRP_ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
+
+#ifndef __GNUC__
+# define __FUNCTION__ __func__
+#endif
+
+#ifdef __GNUC__
+ /** MAX that evalutes its arguments only once. */
+# define MRP_MAX(a, b) ({ \
+ typeof(a) _a = (a); \
+ typeof(b) _b = (b); \
+ _a > _b ? _a : _b; \
+ })
+
+ /** MIN that evalutes its arguments only once. */
+# define MRP_MIN(a, b) ({ \
+ typeof(a) _a = (a), _b = (b); \
+ _a < _b ? _a : _b; \
+ })
+
+ /** Likeliness branch-prediction hint for the compiler. */
+# define MRP_LIKELY(cond) __builtin_expect((cond), 1)
+
+ /** Unlikeliness branch-prediction hint for the compiler. */
+# define MRP_UNLIKELY(cond) __builtin_expect((cond), 0)
+
+ /** Prevent symbol from being exported (overriden by linker scripts). */
+# define MRP_HIDDEN __attribute__ ((visibility ("hidden")))
+
+ /** Request a symbol to be exported (overriden by linker scripts). */
+# define MRP_EXPORT __attribute__ ((visibility ("default")))
+
+ /** Ask the compiler to check for the presence of a NULL-sentinel. */
+# define MRP_NULLTERM __attribute__((sentinel))
+
+ /** Ask for printf-like format string checks of calls to this function. */
+# define MRP_PRINTF_LIKE(format_idx, first_arg_idx) \
+ __attribute__ ((format (printf, format_idx, first_arg_idx)))
+
+ /** Mark a function to be called before main is entered. */
+# define MRP_INIT __attribute__((constructor(65535)))
+# define MRP_INIT_AT(prio) __attribute__ ((constructor(prio)))
+
+ /** Mark a function to be called after main returns, or exit is called. */
+# define MRP_EXIT __attribute__ ((destructor(65535)))
+# define MRP_EXIT_AT(prio) __attribute__ ((destructor(prio)))
+
+/** Mark a variable unused. */
+# define MRP_UNUSED(var) (void)var
+#else /* ! __GNUC__ */
+# define MRP_LIKELY(cond) (cond)
+# define MRP_UNLIKELY(cond) (cond)
+# define MRP_HIDDEN
+# define MRP_EXPORT
+# define MRP_NULLTERM
+# define MRP_PRINTF_LIKE(format_idx, first_arg_idx)
+# define __FUNCTION__ __func__
+# define MRP_INIT
+# define MRP_INIT_AT
+# define MRP_EXIT
+# define MRP_EXIT_AT
+# define MRP_UNUSED(var)
+#endif
+
+/** Macro that can be used to pass the location of its usage. */
+# define __LOC__ __FILE__, __LINE__, __FUNCTION__
+
+/** Assertions. */
+#ifndef NDEBUG
+# define MRP_ASSERT(expr, fmt, args...) do { \
+ if (!(expr)) { \
+ printf("assertion '%s' failed at %s@%s:%d: "fmt"\n", #expr, \
+ __FUNCTION__, __FILE__, __LINE__, ## args); \
+ abort(); \
+ } \
+ } while (0)
+#else
+# define MRP_ASSERT(expr, msg) do { } while (0)
+#endif
+
+/** Create a version integer from a (major, minor, micro) tuple. */
+#define MRP_VERSION_INT(maj, min, mic) \
+ ((((maj) & 0xff) << 16) | (((min) & 0xff) << 8) | ((mic) & 0xff))
+
+/** Create a version string from a (const) (major, minor, micro) tuple. */
+#define MRP_VERSION_STRING(maj, min, mic) #maj"."#min"."#mic
+
+/** Extract major version from a version integer. */
+#define MRP_VERSION_MAJOR(ver) (((ver) >> 16) & 0xff)
+
+/** Extract minor version from a version integer. */
+#define MRP_VERSION_MINOR(ver) (((ver) >> 8) & 0xff)
+
+/** Extract micro version from a version integer. */
+#define MRP_VERSION_MICRO(ver) ((ver) & 0xff)
+
+/** Macro to stringify a macro argument. */
+#define MRP_STRINGIFY(arg) #arg
+
+/** C++-compatibility macros. */
+#ifdef __cplusplus
+# define MRP_CDECL_BEGIN extern "C" {
+# define MRP_CDECL_END }
+#else
+# define MRP_CDECL_BEGIN
+# define MRP_CDECL_END
+#endif
+
+#endif /* __MURPHY_MACROS_H__ */
+
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <time.h>
+#include <signal.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <sys/epoll.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/json.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/mainloop.h>
+
+#define USECS_PER_SEC (1000 * 1000)
+#define USECS_PER_MSEC (1000)
+#define NSECS_PER_USEC (1000)
+
+/*
+ * I/O watches
+ */
+
+struct mrp_io_watch_s {
+ mrp_list_hook_t hook; /* to list of watches */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ int fd; /* file descriptor to watch */
+ mrp_io_event_t events; /* events of interest */
+ mrp_io_watch_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+ struct pollfd *pollfd; /* associated pollfd */
+ mrp_list_hook_t slave; /* watches with the same fd */
+ int wrhup; /* EPOLLHUPs delivered */
+};
+
+#define is_master(w) !mrp_list_empty(&(w)->hook)
+#define is_slave(w) !mrp_list_empty(&(w)->slave)
+
+
+/*
+ * timers
+ */
+
+struct mrp_timer_s {
+ mrp_list_hook_t hook; /* to list of timers */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ unsigned int msecs; /* timer interval */
+ uint64_t expire; /* next expiration time */
+ mrp_timer_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+};
+
+
+/*
+ * deferred callbacks
+ */
+
+struct mrp_deferred_s {
+ mrp_list_hook_t hook; /* to list of cbs */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ mrp_deferred_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+ int inactive : 1;
+};
+
+
+/*
+ * signal handlers
+ */
+
+struct mrp_sighandler_s {
+ mrp_list_hook_t hook; /* to list of handlers */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ int signum; /* signal number */
+ mrp_sighandler_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+};
+
+
+/*
+ * wakeup notifications
+ */
+
+struct mrp_wakeup_s {
+ mrp_list_hook_t hook; /* to list of wakeup cbs */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* mainloop */
+ mrp_wakeup_event_t events; /* wakeup event mask */
+ uint64_t lpf; /* wakeup at most this often */
+ uint64_t next; /* next wakeup time */
+ mrp_timer_t *timer; /* forced interval timer */
+ mrp_wakeup_cb_t cb; /* user callback */
+ void *user_data; /* opaque user data */
+};
+
+#define mark_deleted(o) do { \
+ (o)->cb = NULL; \
+ mrp_list_append(&(o)->ml->deleted, &(o)->deleted); \
+ } while (0)
+
+#define is_deleted(o) ((o)->cb == NULL)
+
+
+/*
+ * any of the above data structures linked to the list of deleted items
+ *
+ * When deleted, the above data structures are first unlinked from their
+ * native list and linked to the special list of deleted entries. At an
+ * appropriate point upon every iteration of the main loop this list is
+ * checked and all entries are freed. This structure is used to get a
+ * pointer to the real structure that we need free. For this to work link
+ * hooks in all of the above structures need to be kept at the same offset
+ * as it is in deleted_t.
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* unfreed deleted items */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+} deleted_t;
+
+
+/*
+ * file descriptor table
+ *
+ * We do not want to associate direct pointers to related data structures
+ * with epoll. We might get delivered pending events for deleted fds (at
+ * least for unix domain sockets this seems to be the case) and with direct
+ * pointers we'd get delivered a dangling pointer together with the event.
+ * Instead we keep these structures in an fd table and use the fd to look
+ * up the associated data structure for events. We ignore events for which
+ * no data structure is found. In the fd table we keep a fixed size direct
+ * table for a small amount of fds (we expect to be using at most in the
+ * vast majority of cases) and we hash in the rest.
+ */
+
+#define FDTBL_SIZE 64
+
+typedef struct {
+ void *t[FDTBL_SIZE];
+ mrp_htbl_t *h;
+} fdtbl_t;
+
+
+/*
+ * external mainloops
+ */
+
+struct mrp_subloop_s {
+ mrp_list_hook_t hook; /* to list of subloops */
+ mrp_list_hook_t deleted; /* to list of pending delete */
+ int (*free)(void *ptr); /* cb to free memory */
+ mrp_mainloop_t *ml; /* main loop */
+ mrp_subloop_ops_t *cb; /* subloop glue callbacks */
+ void *user_data; /* opaque subloop data */
+ int epollfd; /* epollfd for this subloop */
+ struct epoll_event *events; /* epoll event buffer */
+ int nevent; /* epoll event buffer size */
+ fdtbl_t *fdtbl; /* file descriptor table */
+ mrp_io_watch_t *w; /* watch for epollfd */
+ struct pollfd *pollfds; /* pollfds for this subloop */
+ int npollfd; /* number of pollfds */
+ int pending; /* pending events */
+ int poll; /* need to poll for events */
+};
+
+
+/*
+ * event busses
+ */
+
+struct mrp_event_bus_s {
+ char *name; /* bus name */
+ mrp_list_hook_t hook; /* to list of busses */
+ mrp_mainloop_t *ml; /* associated mainloop */
+ mrp_list_hook_t watches; /* event watches on this bus */
+ int busy; /* whether pumping events */
+ int dead;
+};
+
+
+/*
+ * event watches
+ */
+
+struct mrp_event_watch_s {
+ mrp_list_hook_t hook; /* to list of event watches */
+ mrp_event_bus_t *bus; /* associated event bus */
+ mrp_event_mask_t mask; /* mask of watched events */
+ mrp_event_watch_cb_t cb; /* notification callback */
+ void *user_data; /* opaque user data */
+ int dead : 1; /* marked for deletion */
+};
+
+
+/*
+ * pending events
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to event queue */
+ mrp_event_bus_t *bus; /* bus for this event */
+ uint32_t id; /* event id */
+ int format; /* attached data format */
+ void *data; /* attached data */
+} pending_event_t;
+
+
+/*
+ * main loop
+ */
+
+struct mrp_mainloop_s {
+ int epollfd; /* our epoll descriptor */
+ struct epoll_event *events; /* epoll event buffer */
+ int nevent; /* epoll event buffer size */
+ fdtbl_t *fdtbl; /* file descriptor table */
+
+ mrp_list_hook_t iowatches; /* list of I/O watches */
+ int niowatch; /* number of I/O watches */
+ mrp_io_event_t iomode; /* default event trigger mode */
+
+ mrp_list_hook_t timers; /* list of timers */
+ mrp_timer_t *next_timer; /* next expiring timer */
+
+ mrp_list_hook_t deferred; /* list of deferred cbs */
+ mrp_list_hook_t inactive_deferred; /* inactive defferred cbs */
+
+ mrp_list_hook_t wakeups; /* list of wakeup cbs */
+
+ int poll_timeout; /* next poll timeout */
+ int poll_result; /* return value from poll */
+
+ int sigfd; /* signal polling fd */
+ sigset_t sigmask; /* signal mask */
+ mrp_io_watch_t *sigwatch; /* sigfd I/O watch */
+ mrp_list_hook_t sighandlers; /* signal handlers */
+
+ mrp_list_hook_t subloops; /* external main loops */
+
+ mrp_list_hook_t deleted; /* unfreed deleted items */
+ int quit; /* TRUE if _quit called */
+ int exit_code; /* returned from _run */
+
+ mrp_superloop_ops_t *super_ops; /* superloop options */
+ void *super_data; /* superloop glue data */
+ void *iow; /* superloop epollfd watch */
+ void *timer; /* superloop timer */
+ void *work; /* superloop deferred work */
+
+ mrp_list_hook_t busses; /* known event busses */
+ mrp_list_hook_t eventq; /* pending events */
+ mrp_deferred_t *eventd; /* deferred event pump cb */
+};
+
+
+static mrp_event_def_t *events; /* registered events */
+static int nevent; /* number of events */
+static MRP_LIST_HOOK (ewatches); /* global, synchronous 'bus' */
+
+
+static void dump_pollfds(const char *prefix, struct pollfd *fds, int nfd);
+static void adjust_superloop_timer(mrp_mainloop_t *ml);
+static size_t poll_events(void *id, mrp_mainloop_t *ml, void **bufp);
+static void pump_events(mrp_deferred_t *d, void *user_data);
+
+/*
+ * fd table manipulation
+ */
+
+static int fd_cmp(const void *key1, const void *key2)
+{
+ return key2 - key1;
+}
+
+
+static uint32_t fd_hash(const void *key)
+{
+ uint32_t h;
+
+ h = (uint32_t)(ptrdiff_t)key;
+
+ return h;
+}
+
+
+
+static fdtbl_t *fdtbl_create(void)
+{
+ fdtbl_t *ft;
+ mrp_htbl_config_t hcfg;
+
+ if ((ft = mrp_allocz(sizeof(*ft))) != NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = fd_cmp;
+ hcfg.hash = fd_hash;
+ hcfg.free = NULL;
+ hcfg.nbucket = 16;
+
+ ft->h = mrp_htbl_create(&hcfg);
+
+ if (ft->h != NULL)
+ return ft;
+ else
+ mrp_free(ft);
+ }
+
+ return NULL;
+}
+
+
+static void fdtbl_destroy(fdtbl_t *ft)
+{
+ if (ft != NULL) {
+ mrp_htbl_destroy(ft->h, FALSE);
+ mrp_free(ft);
+ }
+}
+
+
+static void *fdtbl_lookup(fdtbl_t *ft, int fd)
+{
+ if (fd >= 0 && ft != NULL) {
+ if (fd < FDTBL_SIZE)
+ return ft->t[fd];
+ else
+ return mrp_htbl_lookup(ft->h, (void *)(ptrdiff_t)fd);
+ }
+
+ return NULL;
+}
+
+
+static int fdtbl_insert(fdtbl_t *ft, int fd, void *ptr)
+{
+ if (fd >= 0 && ft != NULL) {
+ if (fd < FDTBL_SIZE) {
+ if (ft->t[fd] == NULL) {
+ ft->t[fd] = ptr;
+ return 0;
+ }
+ else
+ errno = EEXIST;
+ }
+ else {
+ if (mrp_htbl_insert(ft->h, (void *)(ptrdiff_t)fd, ptr))
+ return 0;
+ else
+ errno = EEXIST;
+ }
+ }
+ else
+ errno = EINVAL;
+
+ return -1;
+}
+
+
+static void fdtbl_remove(fdtbl_t *ft, int fd)
+{
+ if (fd >= 0 && ft != NULL) {
+ if (fd < FDTBL_SIZE)
+ ft->t[fd] = NULL;
+ else
+ mrp_htbl_remove(ft->h, (void *)(ptrdiff_t)fd, FALSE);
+ }
+}
+
+
+/*
+ * I/O watches
+ */
+
+static uint32_t epoll_event_mask(mrp_io_watch_t *master, mrp_io_watch_t *ignore)
+{
+ mrp_io_watch_t *w;
+ mrp_list_hook_t *p, *n;
+ uint32_t mask;
+
+ mask = (master != ignore ?
+ master->events : master->events & MRP_IO_TRIGGER_EDGE);
+
+ mrp_list_foreach(&master->slave, p, n) {
+ w = mrp_list_entry(p, typeof(*w), slave);
+
+ if (w != ignore)
+ mask |= w->events;
+ }
+
+ mrp_debug("epoll event mask for I/O watch %p: %d", master, mask);
+
+ return mask;
+}
+
+
+static int epoll_add_slave(mrp_io_watch_t *master, mrp_io_watch_t *slave)
+{
+ mrp_mainloop_t *ml = master->ml;
+ struct epoll_event evt;
+
+ evt.events = epoll_event_mask(master, NULL) | slave->events;
+ evt.data.u64 = 0;
+ evt.data.fd = master->fd;
+
+ if (epoll_ctl(ml->epollfd, EPOLL_CTL_MOD, master->fd, &evt) == 0) {
+ mrp_list_append(&master->slave, &slave->slave);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+static int epoll_add(mrp_io_watch_t *w)
+{
+ mrp_mainloop_t *ml = w->ml;
+ mrp_io_watch_t *master;
+ struct epoll_event evt;
+
+ if (fdtbl_insert(ml->fdtbl, w->fd, w) == 0) {
+ evt.events = w->events;
+ evt.data.u64 = 0; /* init full union for valgrind... */
+ evt.data.fd = w->fd;
+
+ if (epoll_ctl(ml->epollfd, EPOLL_CTL_ADD, w->fd, &evt) == 0) {
+ mrp_list_append(&ml->iowatches, &w->hook);
+ ml->niowatch++;
+
+ return 0;
+ }
+ else
+ fdtbl_remove(ml->fdtbl, w->fd);
+ }
+ else {
+ if (errno == EEXIST) {
+ master = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (master != NULL)
+ return epoll_add_slave(master, w);
+ }
+ }
+
+ return -1;
+}
+
+
+static int epoll_del(mrp_io_watch_t *w)
+{
+ mrp_mainloop_t *ml = w->ml;
+ mrp_io_watch_t *master;
+ struct epoll_event evt;
+ int status;
+
+ if (is_master(w))
+ master = w;
+ else
+ master = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (master != NULL) {
+ evt.events = epoll_event_mask(master, w);
+ evt.data.u64 = 0; /* init full union for valgrind... */
+ evt.data.fd = w->fd;
+
+ if ((evt.events & MRP_IO_EVENT_ALL) == 0) {
+ fdtbl_remove(ml->fdtbl, w->fd);
+ status = epoll_ctl(ml->epollfd, EPOLL_CTL_DEL, w->fd, &evt);
+
+ if (status == 0 || (errno == EBADF || errno == ENOENT))
+ ml->niowatch--;
+ }
+ else
+ status = epoll_ctl(ml->epollfd, EPOLL_CTL_MOD, w->fd, &evt);
+
+ if (status == 0 || (errno == EBADF || errno == ENOENT))
+ return 0;
+ else
+ mrp_log_error("Failed to update epoll for deleted I/O watch %p "
+ "(fd %d, %d: %s).", w, w->fd, errno, strerror(errno));
+ }
+ else {
+ mrp_log_error("Failed to find master for deleted I/O watch %p "
+ "(fd %d).", w, w->fd);
+ errno = EINVAL;
+ }
+
+ return -1;
+}
+
+
+static int free_io_watch(void *ptr)
+{
+ mrp_io_watch_t *w = (mrp_io_watch_t *)ptr;
+ mrp_mainloop_t *ml = w->ml;
+ mrp_io_watch_t *master;
+
+ master = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (master == w) {
+ fdtbl_remove(ml->fdtbl, w->fd);
+
+ if (!mrp_list_empty(&w->slave)) {
+ /* relink first slave as new master to mainloop */
+ master = mrp_list_entry(w->slave.next, typeof(*master), slave);
+ mrp_list_append(&ml->iowatches, &master->hook);
+
+ fdtbl_insert(ml->fdtbl, master->fd, master);
+ }
+ }
+
+ mrp_list_delete(&w->slave);
+ mrp_free(w);
+
+ return TRUE;
+}
+
+
+mrp_io_watch_t *mrp_add_io_watch(mrp_mainloop_t *ml, int fd,
+ mrp_io_event_t events,
+ mrp_io_watch_cb_t cb, void *user_data)
+{
+ mrp_io_watch_t *w;
+
+ if (fd < 0 || cb == NULL)
+ return NULL;
+
+ if ((w = mrp_allocz(sizeof(*w))) != NULL) {
+ mrp_list_init(&w->hook);
+ mrp_list_init(&w->deleted);
+ mrp_list_init(&w->slave);
+ w->ml = ml;
+ w->fd = fd;
+ w->events = events & MRP_IO_EVENT_ALL;
+
+ switch (events & MRP_IO_TRIGGER_MASK) {
+ case 0:
+ if (ml->iomode == MRP_IO_TRIGGER_EDGE)
+ w->events |= MRP_IO_TRIGGER_EDGE;
+ break;
+ case MRP_IO_TRIGGER_EDGE:
+ w->events |= MRP_IO_TRIGGER_EDGE;
+ break;
+ case MRP_IO_TRIGGER_LEVEL:
+ break;
+ default:
+ mrp_log_warning("Invalid I/O event trigger mode 0x%x.",
+ events & MRP_IO_TRIGGER_MASK);
+ break;
+ }
+
+ w->cb = cb;
+ w->user_data = user_data;
+ w->free = free_io_watch;
+
+ if (epoll_add(w) != 0) {
+ mrp_free(w);
+ w = NULL;
+ }
+ else
+ mrp_debug("added I/O watch %p (fd %d, events 0x%x)", w, w->fd, w->events);
+ }
+
+ return w;
+}
+
+
+void mrp_del_io_watch(mrp_io_watch_t *w)
+{
+ /*
+ * Notes: It is not safe to free the watch here as there might be
+ * a delivered but unprocessed epoll event with a pointer
+ * to the watch. We just mark it deleted and take care of
+ * the actual deletion in the dispatching loop.
+ */
+
+ if (w != NULL && !is_deleted(w)) {
+ mrp_debug("marking I/O watch %p (fd %d) deleted", w, w->fd);
+
+ mark_deleted(w);
+ w->events = 0;
+
+ epoll_del(w);
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_io_watch_mainloop(mrp_io_watch_t *w)
+{
+ return w ? w->ml : NULL;
+}
+
+
+int mrp_set_io_event_mode(mrp_mainloop_t *ml, mrp_io_event_t mode)
+{
+ if (mode == MRP_IO_TRIGGER_LEVEL || mode == MRP_IO_TRIGGER_EDGE) {
+ ml->iomode = mode;
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Invalid I/O event mode 0x%x.", mode);
+ return FALSE;
+ }
+}
+
+
+mrp_io_event_t mrp_get_io_event_mode(mrp_mainloop_t *ml)
+{
+ return ml->iomode ? ml->iomode : MRP_IO_TRIGGER_LEVEL;
+}
+
+
+/*
+ * timers
+ */
+
+static uint64_t time_now(void)
+{
+ struct timespec ts;
+ uint64_t now;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ now = ts.tv_sec * USECS_PER_SEC;
+ now += ts.tv_nsec / NSECS_PER_USEC;
+
+ return now;
+}
+
+
+static inline int usecs_to_msecs(uint64_t usecs)
+{
+ int msecs;
+
+ msecs = (usecs + USECS_PER_MSEC - 1) / USECS_PER_MSEC;
+
+ return msecs;
+}
+
+
+static void insert_timer(mrp_timer_t *t)
+{
+ mrp_mainloop_t *ml = t->ml;
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t1, *next;
+ int inserted;
+
+ /*
+ * Notes:
+ * If there is ever a need to run a large number of
+ * simultaneous timers, we need to change this to a
+ * self-balancing data structure, eg. an red-black tree.
+ */
+
+ inserted = FALSE;
+ next = NULL;
+ mrp_list_foreach(&ml->timers, p, n) {
+ t1 = mrp_list_entry(p, mrp_timer_t, hook);
+
+ if (!is_deleted(t1)) {
+ if (t->expire <= t1->expire) {
+ mrp_list_prepend(p->prev, &t->hook);
+ inserted = TRUE;
+ break;
+ }
+ if (next == NULL)
+ next = t1;
+ }
+ }
+
+ if (!inserted)
+ mrp_list_append(&ml->timers, &t->hook);
+
+ if (next)
+ ml->next_timer = next;
+ else {
+ ml->next_timer = t;
+ adjust_superloop_timer(ml);
+ }
+}
+
+
+static inline void rearm_timer(mrp_timer_t *t)
+{
+ mrp_list_delete(&t->hook);
+ t->expire = time_now() + t->msecs * USECS_PER_MSEC;
+ insert_timer(t);
+}
+
+
+static mrp_timer_t *find_next_timer(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t = NULL;
+
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (!is_deleted(t))
+ break;
+ else
+ t = NULL;
+ }
+
+ ml->next_timer = t;
+ return t;
+}
+
+
+static int free_timer(void *ptr)
+{
+ mrp_timer_t *t = (mrp_timer_t *)ptr;
+
+ mrp_free(t);
+
+ return TRUE;
+}
+
+
+
+mrp_timer_t *mrp_add_timer(mrp_mainloop_t *ml, unsigned int msecs,
+ mrp_timer_cb_t cb, void *user_data)
+{
+ mrp_timer_t *t;
+
+ if (cb == NULL)
+ return NULL;
+
+ if ((t = mrp_allocz(sizeof(*t))) != NULL) {
+ mrp_list_init(&t->hook);
+ mrp_list_init(&t->deleted);
+ t->ml = ml;
+ t->expire = time_now() + msecs * USECS_PER_MSEC;
+ t->msecs = msecs;
+ t->cb = cb;
+ t->user_data = user_data;
+ t->free = free_timer;
+
+ insert_timer(t);
+ }
+
+ return t;
+}
+
+
+void mrp_mod_timer(mrp_timer_t *t, unsigned int msecs)
+{
+ if (t != NULL && !is_deleted(t)) {
+ if (msecs != MRP_TIMER_RESTART)
+ t->msecs = msecs;
+
+ rearm_timer(t);
+ }
+}
+
+
+void mrp_del_timer(mrp_timer_t *t)
+{
+ /*
+ * Notes: It is not safe to simply free this entry here as we might
+ * be dispatching with this entry being the next to process.
+ * We check for this and if it is not the case we relink this
+ * to the list of deleted items which will be then processed
+ * at end of the mainloop iteration. Otherwise we only mark the
+ * this entry for deletion and the rest will be taken care of in
+ * dispatch_timers().
+ */
+
+ if (t != NULL && !is_deleted(t)) {
+ mrp_debug("marking timer %p deleted", t);
+
+ mark_deleted(t);
+
+ if (t->ml->next_timer == t) {
+ find_next_timer(t->ml);
+ adjust_superloop_timer(t->ml);
+ }
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_timer_mainloop(mrp_timer_t *t)
+{
+ return t ? t->ml : NULL;
+}
+
+
+/*
+ * deferred/idle callbacks
+ */
+
+mrp_deferred_t *mrp_add_deferred(mrp_mainloop_t *ml, mrp_deferred_cb_t cb,
+ void *user_data)
+{
+ mrp_deferred_t *d;
+
+ if (cb == NULL)
+ return NULL;
+
+ if ((d = mrp_allocz(sizeof(*d))) != NULL) {
+ mrp_list_init(&d->hook);
+ mrp_list_init(&d->deleted);
+ d->ml = ml;
+ d->cb = cb;
+ d->user_data = user_data;
+
+ mrp_list_append(&ml->deferred, &d->hook);
+ adjust_superloop_timer(ml);
+ }
+
+ return d;
+}
+
+
+void mrp_del_deferred(mrp_deferred_t *d)
+{
+ /*
+ * Notes: It is not safe to simply free this entry here as we might
+ * be dispatching with this entry being the next to process.
+ * We just mark this here deleted and take care of the rest
+ * in the dispatching loop.
+ */
+
+ if (d != NULL && !is_deleted(d)) {
+ mrp_debug("marking deferred %p deleted", d);
+ mark_deleted(d);
+ }
+}
+
+
+void mrp_disable_deferred(mrp_deferred_t *d)
+{
+ if (d != NULL)
+ d->inactive = TRUE;
+}
+
+
+static inline void disable_deferred(mrp_deferred_t *d)
+{
+ if (MRP_LIKELY(d->inactive)) {
+ mrp_list_delete(&d->hook);
+ mrp_list_append(&d->ml->inactive_deferred, &d->hook);
+ }
+
+}
+
+
+void mrp_enable_deferred(mrp_deferred_t *d)
+{
+ if (d != NULL) {
+ if (!is_deleted(d)) {
+ d->inactive = FALSE;
+ mrp_list_delete(&d->hook);
+ mrp_list_append(&d->ml->deferred, &d->hook);
+ }
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_deferred_mainloop(mrp_deferred_t *d)
+{
+ return d ? d->ml : NULL;
+}
+
+
+/*
+ * signal notifications
+ */
+
+static void dispatch_signals(mrp_io_watch_t *w, int fd,
+ mrp_io_event_t events, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_io_watch_mainloop(w);
+ struct signalfd_siginfo sig;
+ mrp_list_hook_t *p, *n;
+ mrp_sighandler_t *h;
+ int signum;
+
+ MRP_UNUSED(events);
+ MRP_UNUSED(user_data);
+
+ while (read(fd, &sig, sizeof(sig)) > 0) {
+ signum = sig.ssi_signo;
+
+ mrp_list_foreach(&ml->sighandlers, p, n) {
+ h = mrp_list_entry(p, typeof(*h), hook);
+
+ if (!is_deleted(h)) {
+ if (h->signum == signum)
+ h->cb(h, signum, h->user_data);
+ }
+ }
+ }
+}
+
+
+static int setup_sighandlers(mrp_mainloop_t *ml)
+{
+ if (ml->sigfd == -1) {
+ sigemptyset(&ml->sigmask);
+
+ ml->sigfd = signalfd(-1, &ml->sigmask, SFD_NONBLOCK | SFD_CLOEXEC);
+
+ if (ml->sigfd == -1)
+ return FALSE;
+
+ ml->sigwatch = mrp_add_io_watch(ml, ml->sigfd, MRP_IO_EVENT_IN,
+ dispatch_signals, NULL);
+
+ if (ml->sigwatch == NULL) {
+ close(ml->sigfd);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+mrp_sighandler_t *mrp_add_sighandler(mrp_mainloop_t *ml, int signum,
+ mrp_sighandler_cb_t cb, void *user_data)
+{
+ mrp_sighandler_t *s;
+
+ if (cb == NULL || ml->sigfd == -1)
+ return NULL;
+
+ if ((s = mrp_allocz(sizeof(*s))) != NULL) {
+ mrp_list_init(&s->hook);
+ mrp_list_init(&s->deleted);
+ s->ml = ml;
+ s->signum = signum;
+ s->cb = cb;
+ s->user_data = user_data;
+
+ mrp_list_append(&ml->sighandlers, &s->hook);
+ sigaddset(&ml->sigmask, s->signum);
+ signalfd(ml->sigfd, &ml->sigmask, SFD_NONBLOCK|SFD_CLOEXEC);
+ sigprocmask(SIG_BLOCK, &ml->sigmask, NULL);
+ }
+
+ return s;
+}
+
+
+static void recalc_sigmask(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_sighandler_t *h;
+
+ sigprocmask(SIG_UNBLOCK, &ml->sigmask, NULL);
+ sigemptyset(&ml->sigmask);
+
+ mrp_list_foreach(&ml->sighandlers, p, n) {
+ h = mrp_list_entry(p, typeof(*h), hook);
+ if (!is_deleted(h))
+ sigaddset(&ml->sigmask, h->signum);
+ }
+
+ sigprocmask(SIG_BLOCK, &ml->sigmask, NULL);
+}
+
+
+void mrp_del_sighandler(mrp_sighandler_t *h)
+{
+ if (h != NULL && !is_deleted(h)) {
+ mrp_debug("marking sighandler %p deleted", h);
+
+ mark_deleted(h);
+ recalc_sigmask(h->ml);
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_sighandler_mainloop(mrp_sighandler_t *h)
+{
+ return h ? h->ml : NULL;
+}
+
+
+/*
+ * wakeup notifications
+ */
+
+static void wakeup_cb(mrp_wakeup_t *w, mrp_wakeup_event_t event, uint64_t now)
+{
+ if (w->next > now) {
+ mrp_debug("skipping wakeup %p because of low-pass filter", w);
+ return;
+ }
+
+ w->cb(w, event, w->user_data);
+
+ if (w->lpf != MRP_WAKEUP_NOLIMIT)
+ w->next = now + w->lpf;
+
+ if (w->timer != NULL)
+ mrp_mod_timer(w->timer, MRP_TIMER_RESTART);
+}
+
+
+static void forced_wakeup_cb(mrp_timer_t *t, void *user_data)
+{
+ mrp_wakeup_t *w = (mrp_wakeup_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ if (is_deleted(w))
+ return;
+
+ mrp_debug("dispatching forced wakeup cb %p", w);
+
+ wakeup_cb(w, MRP_WAKEUP_EVENT_LIMIT, time_now());
+}
+
+
+mrp_wakeup_t *mrp_add_wakeup(mrp_mainloop_t *ml, mrp_wakeup_event_t events,
+ unsigned int lpf_msecs, unsigned int force_msecs,
+ mrp_wakeup_cb_t cb, void *user_data)
+{
+ mrp_wakeup_t *w;
+
+ if (cb == NULL)
+ return NULL;
+
+ if (lpf_msecs > force_msecs && force_msecs != MRP_WAKEUP_NOLIMIT)
+ return NULL;
+
+ if ((w = mrp_allocz(sizeof(*w))) != NULL) {
+ mrp_list_init(&w->hook);
+ mrp_list_init(&w->deleted);
+ w->ml = ml;
+ w->events = events;
+ w->cb = cb;
+ w->user_data = user_data;
+
+ w->lpf = lpf_msecs * USECS_PER_MSEC;
+
+ if (lpf_msecs != MRP_WAKEUP_NOLIMIT)
+ w->next = time_now() + w->lpf;
+
+ if (force_msecs != MRP_WAKEUP_NOLIMIT) {
+ w->timer = mrp_add_timer(ml, force_msecs, forced_wakeup_cb, w);
+
+ if (w->timer == NULL) {
+ mrp_free(w);
+ return NULL;
+ }
+ }
+
+ mrp_list_append(&ml->wakeups, &w->hook);
+ }
+
+ return w;
+}
+
+
+void mrp_del_wakeup(mrp_wakeup_t *w)
+{
+ /*
+ * Notes: It is not safe to simply free this entry here as we might
+ * be dispatching with this entry being the next to process.
+ * We just mark this here deleted and take care of the rest
+ * in the dispatching loop.
+ */
+
+ if (w != NULL && !is_deleted(w)) {
+ mrp_debug("marking wakeup %p deleted", w);
+ mark_deleted(w);
+ }
+}
+
+
+mrp_mainloop_t *mrp_get_wakeup_mainloop(mrp_wakeup_t *w)
+{
+ return w ? w->ml : NULL;
+}
+
+
+/*
+ * external mainloops we pump
+ */
+
+static int free_subloop(void *ptr)
+{
+ mrp_subloop_t *sl = (mrp_subloop_t *)ptr;
+
+ mrp_debug("freeing subloop %p", sl);
+
+ mrp_free(sl->pollfds);
+ mrp_free(sl->events);
+ mrp_free(sl);
+
+ return TRUE;
+}
+
+
+static void subloop_event_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ mrp_subloop_t *sl = (mrp_subloop_t *)user_data;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(fd);
+ MRP_UNUSED(events);
+
+ mrp_debug("subloop %p has events, setting poll to TRUE", sl);
+
+ sl->poll = TRUE;
+}
+
+
+mrp_subloop_t *mrp_add_subloop(mrp_mainloop_t *ml, mrp_subloop_ops_t *ops,
+ void *user_data)
+{
+ mrp_subloop_t *sl;
+
+ if (ops == NULL || user_data == NULL)
+ return NULL;
+
+ if ((sl = mrp_allocz(sizeof(*sl))) != NULL) {
+ mrp_list_init(&sl->hook);
+ mrp_list_init(&sl->deleted);
+ sl->free = free_subloop;
+ sl->ml = ml;
+ sl->cb = ops;
+ sl->user_data = user_data;
+ sl->epollfd = epoll_create1(EPOLL_CLOEXEC);
+ sl->fdtbl = fdtbl_create();
+
+ if (sl->epollfd >= 0 && sl->fdtbl != NULL) {
+ sl->w = mrp_add_io_watch(ml, sl->epollfd, MRP_IO_EVENT_IN,
+ subloop_event_cb, sl);
+
+ if (sl->w != NULL)
+ mrp_list_append(&ml->subloops, &sl->hook);
+ else
+ goto fail;
+ }
+ else {
+ fail:
+ close(sl->epollfd);
+ fdtbl_destroy(sl->fdtbl);
+ mrp_free(sl);
+ sl = NULL;
+ }
+ }
+
+ return sl;
+}
+
+
+void mrp_del_subloop(mrp_subloop_t *sl)
+{
+ struct epoll_event dummy;
+ int i;
+
+ /*
+ * Notes: It is not safe to free the loop here as there might be
+ * a delivered but unprocessed epoll event with a pointers
+ * to the loops pollfds. However, since we do not dispatch
+ * loops by traversing the list of loops, it is safe to relink
+ * it to the list of data structures to be deleted at the
+ * end of the next main loop iteration. So we just remove the
+ * pollfds from epoll, mark this as deleted and relink it.
+ */
+
+ if (sl != NULL && !is_deleted(sl)) {
+ mrp_debug("deactivating and marking subloop %p deleted", sl);
+
+ mrp_del_io_watch(sl->w);
+
+ /* XXX TODO: Why ? close(sl->epollfd) should be enough... */
+ for (i = 0; i < sl->npollfd; i++)
+ epoll_ctl(sl->epollfd, EPOLL_CTL_DEL, sl->pollfds[i].fd, &dummy);
+
+ close(sl->epollfd);
+ sl->epollfd = -1;
+ fdtbl_destroy(sl->fdtbl);
+ sl->fdtbl = NULL;
+
+ mark_deleted(sl);
+ }
+}
+
+
+/*
+ * external mainloop that pumps us
+ */
+
+
+static void super_io_cb(void *super_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_superloop_ops_t *ops = ml->super_ops;
+
+ MRP_UNUSED(super_data);
+ MRP_UNUSED(id);
+ MRP_UNUSED(fd);
+ MRP_UNUSED(events);
+
+ ops->mod_defer(ml->super_data, ml->work, TRUE);
+}
+
+
+static void super_timer_cb(void *super_data, void *id, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_superloop_ops_t *ops = ml->super_ops;
+
+ MRP_UNUSED(super_data);
+ MRP_UNUSED(id);
+
+ ops->mod_defer(ml->super_data, ml->work, TRUE);
+}
+
+
+static void super_work_cb(void *super_data, void *id, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_superloop_ops_t *ops = ml->super_ops;
+ unsigned int timeout;
+
+ MRP_UNUSED(super_data);
+ MRP_UNUSED(id);
+
+ mrp_mainloop_poll(ml, FALSE);
+ mrp_mainloop_dispatch(ml);
+
+ if (!ml->quit) {
+ mrp_mainloop_prepare(ml);
+
+ /*
+ * Notes:
+ *
+ * Some mainloop abstractions (eg. the one in PulseAudio)
+ * have deferred callbacks that starve all other event
+ * processing until no more deferred callbacks are pending.
+ * For this reason, we cannot map our deferred callbacks
+ * directly to superloop deferred callbacks (in some cases
+ * this could starve the superloop indefinitely). Hence, if
+ * we have enabled deferred callbacks, we arm our timer with
+ * 0 timeout to let the superloop do one round of its event
+ * processing.
+ */
+
+ timeout = mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+ ops->mod_timer(ml->super_data, ml->timer, timeout);
+ ops->mod_defer(ml->super_data, ml->work, FALSE);
+ }
+ else {
+ ops->del_io(ml->super_data, ml->iow);
+ ops->del_timer(ml->super_data, ml->timer);
+ ops->del_defer(ml->super_data, ml->work);
+
+ ml->iow = NULL;
+ ml->timer = NULL;
+ ml->work = NULL;
+ }
+}
+
+
+static void adjust_superloop_timer(mrp_mainloop_t *ml)
+{
+ mrp_superloop_ops_t *ops = ml->super_ops;
+ unsigned int timeout;
+
+ if (ops == NULL)
+ return;
+
+ mrp_mainloop_prepare(ml);
+ timeout = mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+ ops->mod_timer(ml->super_data, ml->timer, timeout);
+}
+
+
+int mrp_set_superloop(mrp_mainloop_t *ml, mrp_superloop_ops_t *ops,
+ void *loop_data)
+{
+ mrp_io_event_t events;
+ int timeout;
+
+ if (ml->super_ops == NULL) {
+ if (ops->poll_io != NULL)
+ ops->poll_events = poll_events;
+
+ ml->super_ops = ops;
+ ml->super_data = loop_data;
+
+ mrp_mainloop_prepare(ml);
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_OUT | MRP_IO_EVENT_HUP;
+ ml->iow = ops->add_io(ml->super_data, ml->epollfd, events,
+ super_io_cb, ml);
+ ml->work = ops->add_defer(ml->super_data, super_work_cb, ml);
+
+ /*
+ * Notes:
+ *
+ * Some mainloop abstractions (eg. the one in PulseAudio)
+ * have deferred callbacks that starve all other event
+ * processing until no more deferred callbacks are pending.
+ * For this reason, we cannot map our deferred callbacks
+ * directly to superloop deferred callbacks (in some cases
+ * this could starve the superloop indefinitely). Hence, if
+ * we have enabled deferred callbacks, we arm our timer with
+ * 0 timeout to let the superloop do one round of its event
+ * processing.
+ */
+
+ timeout = mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+ ml->timer = ops->add_timer(ml->super_data, timeout, super_timer_cb, ml);
+
+ if (ml->iow != NULL && ml->timer != NULL && ml->work != NULL)
+ return TRUE;
+ else
+ mrp_clear_superloop(ml);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_clear_superloop(mrp_mainloop_t *ml)
+{
+ mrp_superloop_ops_t *ops = ml->super_ops;
+ void *data = ml->super_data;
+
+ if (ops != NULL) {
+ if (ml->iow != NULL) {
+ ops->del_io(data, ml->iow);
+ ml->iow = NULL;
+ }
+
+ if (ml->work != NULL) {
+ ops->del_defer(data, ml->work);
+ ml->work = NULL;
+ }
+
+ if (ml->timer != NULL) {
+ ops->del_timer(data, ml->timer);
+ ml->timer = NULL;
+ }
+
+ ml->super_ops = NULL;
+ ml->super_data = NULL;
+
+ ops->unregister(data);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_mainloop_unregister(mrp_mainloop_t *ml)
+{
+ return mrp_clear_superloop(ml);
+}
+
+
+/*
+ * mainloop
+ */
+
+static void purge_io_watches(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n, *sp, *sn;
+ mrp_io_watch_t *w, *s;
+
+ mrp_list_foreach(&ml->iowatches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+ mrp_list_delete(&w->hook);
+ mrp_list_delete(&w->deleted);
+
+ mrp_list_foreach(&w->slave, sp, sn) {
+ s = mrp_list_entry(sp, typeof(*s), slave);
+ mrp_list_delete(&s->slave);
+ mrp_free(s);
+ }
+
+ mrp_free(w);
+ }
+}
+
+
+static void purge_timers(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t;
+
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+ mrp_list_delete(&t->hook);
+ mrp_list_delete(&t->deleted);
+ mrp_free(t);
+ }
+}
+
+
+static void purge_deferred(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_deferred_t *d;
+
+ mrp_list_foreach(&ml->deferred, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ mrp_list_delete(&d->hook);
+ mrp_list_delete(&d->deleted);
+ mrp_free(d);
+ }
+
+ mrp_list_foreach(&ml->inactive_deferred, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ mrp_list_delete(&d->hook);
+ mrp_list_delete(&d->deleted);
+ mrp_free(d);
+ }
+}
+
+
+static void purge_sighandlers(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_sighandler_t *s;
+
+ mrp_list_foreach(&ml->sighandlers, p, n) {
+ s = mrp_list_entry(p, typeof(*s), hook);
+ mrp_list_delete(&s->hook);
+ mrp_list_delete(&s->deleted);
+ mrp_free(s);
+ }
+}
+
+
+static void purge_wakeups(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_wakeup_t *w;
+
+ mrp_list_foreach(&ml->wakeups, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+ mrp_list_delete(&w->hook);
+ mrp_list_delete(&w->deleted);
+ mrp_free(w);
+ }
+}
+
+
+static void purge_deleted(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ deleted_t *d;
+
+ mrp_list_foreach(&ml->deleted, p, n) {
+ d = mrp_list_entry(p, typeof(*d), deleted);
+ mrp_list_delete(&d->deleted);
+ mrp_list_delete(&d->hook);
+ if (d->free == NULL) {
+ mrp_debug("purging deleted object %p", d);
+ mrp_free(d);
+ }
+ else {
+ mrp_debug("purging deleted object %p (free cb: %p)", d, d->free);
+ if (!d->free(d)) {
+ mrp_log_error("Failed to free purged item %p.", d);
+ mrp_list_prepend(p, &d->deleted);
+ }
+ }
+ }
+}
+
+
+static void purge_subloops(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_subloop_t *sl;
+
+ mrp_list_foreach(&ml->subloops, p, n) {
+ sl = mrp_list_entry(p, typeof(*sl), hook);
+ mrp_list_delete(&sl->hook);
+ mrp_list_delete(&sl->deleted);
+ free_subloop(sl);
+ }
+}
+
+
+mrp_mainloop_t *mrp_mainloop_create(void)
+{
+ mrp_mainloop_t *ml;
+
+ if ((ml = mrp_allocz(sizeof(*ml))) != NULL) {
+ ml->epollfd = epoll_create1(EPOLL_CLOEXEC);
+ ml->sigfd = -1;
+ ml->fdtbl = fdtbl_create();
+
+ if (ml->epollfd >= 0 && ml->fdtbl != NULL) {
+ mrp_list_init(&ml->iowatches);
+ mrp_list_init(&ml->timers);
+ mrp_list_init(&ml->deferred);
+ mrp_list_init(&ml->inactive_deferred);
+ mrp_list_init(&ml->sighandlers);
+ mrp_list_init(&ml->wakeups);
+ mrp_list_init(&ml->deleted);
+ mrp_list_init(&ml->subloops);
+ mrp_list_init(&ml->busses);
+ mrp_list_init(&ml->eventq);
+
+ ml->eventd = mrp_add_deferred(ml, pump_events, ml);
+ if (ml->eventd == NULL)
+ goto fail;
+ mrp_disable_deferred(ml->eventd);
+
+ if (!setup_sighandlers(ml))
+ goto fail;
+ }
+ else {
+ fail:
+ close(ml->epollfd);
+ fdtbl_destroy(ml->fdtbl);
+ mrp_free(ml);
+ ml = NULL;
+ }
+ }
+
+
+
+ return ml;
+}
+
+
+void mrp_mainloop_destroy(mrp_mainloop_t *ml)
+{
+ if (ml != NULL) {
+ mrp_clear_superloop(ml);
+ purge_io_watches(ml);
+ purge_timers(ml);
+ purge_deferred(ml);
+ purge_sighandlers(ml);
+ purge_wakeups(ml);
+ purge_subloops(ml);
+ purge_deleted(ml);
+
+ close(ml->sigfd);
+ close(ml->epollfd);
+ fdtbl_destroy(ml->fdtbl);
+
+ mrp_free(ml->events);
+ mrp_free(ml);
+ }
+}
+
+
+static int prepare_subloop(mrp_subloop_t *sl)
+{
+ /*
+ * Notes:
+ *
+ * If we have a relatively large number of file descriptors to
+ * poll but typically only a small fraction of them has pending
+ * events per mainloop iteration epoll has significant advantages
+ * over poll. This is the main reason why our mainloop uses epoll.
+ * However, there is a considerable amount of pain one needs to
+ * go through to integrate an external poll-based (sub-)mainloop
+ * (e.g. glib's GMainLoop) with an epoll-based mainloop. I mean,
+ * just look at the code below !
+ *
+ * If it eventually turns out that we typically only have a small
+ * number of file descriptors while at the same time we practically
+ * always need to pump GMainLoop, it is probably a good idea to
+ * bite the bullet and change our mainloop to be poll-based as well.
+ * But let's not go there yet...
+ */
+
+
+ struct epoll_event evt;
+ struct pollfd *fds, *pollfds;
+ int timeout;
+ int nfd, npollfd, n, i;
+ int nmatch;
+ int fd, idx;
+
+ MRP_UNUSED(dump_pollfds);
+
+ mrp_debug("preparing subloop %p", sl);
+
+ pollfds = sl->pollfds;
+ npollfd = sl->npollfd;
+
+ if (sl->cb->prepare(sl->user_data)) {
+ mrp_debug("subloop %p prepare reported ready, dispatching it", sl);
+ sl->cb->dispatch(sl->user_data);
+ }
+ sl->poll = FALSE;
+
+ nfd = npollfd;
+ fds = nfd ? mrp_allocz(nfd * sizeof(*fds)) : NULL;
+
+ MRP_ASSERT(nfd == 0 || fds != NULL, "failed to allocate pollfd's");
+
+ while ((n = sl->cb->query(sl->user_data, fds, nfd, &timeout)) > nfd) {
+ fds = mrp_reallocz(fds, nfd, n);
+ nfd = n;
+ MRP_ASSERT(fds != NULL, "failed to allocate pollfd's");
+ }
+ nfd = n;
+
+
+#if 0
+ printf("-------------------------\n");
+ dump_pollfds("old: ", sl->pollfds, sl->npollfd);
+ dump_pollfds("new: ", fds, nfd);
+ printf("-------------------------\n");
+#endif
+
+
+ /*
+ * skip over the identical portion of the old and new pollfd's
+ */
+
+ for (i = nmatch = 0; i < npollfd && i < n; i++, nmatch++) {
+ if (fds[i].fd != pollfds[i].fd ||
+ fds[i].events != pollfds[i].events)
+ break;
+ else
+ fds[i].revents = pollfds[i].revents = 0;
+ }
+
+
+ if (nmatch == npollfd && npollfd == nfd) {
+ mrp_free(fds);
+ goto out;
+ }
+
+
+ /*
+ * replace file descriptors with the new set (remove old, add new)
+ */
+
+ for (i = 0; i < npollfd; i++) {
+ fd = pollfds[i].fd;
+ fdtbl_remove(sl->fdtbl, fd);
+ if (epoll_ctl(sl->epollfd, EPOLL_CTL_DEL, fd, &evt) < 0) {
+ if (errno != EBADF && errno != ENOENT)
+ mrp_log_error("Failed to delete subloop fd %d from epoll "
+ "(%d: %s)", fd, errno, strerror(errno));
+ }
+ }
+
+ for (i = 0; i < nfd; i++) {
+ fd = fds[i].fd;
+ idx = i + 1;
+
+ evt.events = fds[i].events;
+ evt.data.u64 = 0; /* init full union for valgrind... */
+ evt.data.fd = fd;
+
+ if (fdtbl_insert(sl->fdtbl, fd, (void *)(ptrdiff_t)idx) == 0) {
+ if (epoll_ctl(sl->epollfd, EPOLL_CTL_ADD, fd, &evt) != 0) {
+ mrp_log_error("Failed to add subloop fd %d to epoll "
+ "(%d: %s)", fd, errno, strerror(errno));
+ }
+ }
+ else {
+ mrp_log_error("Failed to add subloop fd %d to fd table "
+ "(%d: %s)", fd, errno, strerror(errno));
+ }
+
+ fds[i].revents = 0;
+ }
+
+ mrp_free(sl->pollfds);
+ sl->pollfds = fds;
+ sl->npollfd = nfd;
+
+
+ /*
+ * resize event buffer if needed
+ */
+
+ if (sl->nevent < nfd) {
+ sl->nevent = nfd;
+ sl->events = mrp_realloc(sl->events, sl->nevent * sizeof(*sl->events));
+
+ MRP_ASSERT(sl->events != NULL || sl->nevent == 0,
+ "can't allocate epoll event buffer");
+ }
+
+ out:
+ mrp_debug("subloop %p: fds: %d, timeout: %d, poll: %s",
+ sl, sl->npollfd, timeout, sl->poll ? "TRUE" : "FALSE");
+
+ return timeout;
+}
+
+
+static int prepare_subloops(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_subloop_t *sl;
+ int ext_timeout, min_timeout;
+
+ min_timeout = INT_MAX;
+
+ mrp_list_foreach(&ml->subloops, p, n) {
+ sl = mrp_list_entry(p, typeof(*sl), hook);
+
+ if (!is_deleted(sl)) {
+ ext_timeout = prepare_subloop(sl);
+ min_timeout = MRP_MIN(min_timeout, ext_timeout);
+ }
+ else
+ mrp_debug("skipping deleted subloop %p", sl);
+ }
+
+ return min_timeout;
+}
+
+
+#if 0
+static inline void dump_timers(mrp_mainloop_t *ml)
+{
+ mrp_timer_t *t;
+ mrp_list_hook_t *p, *n;
+ int i;
+ mrp_timer_t *next = NULL;
+
+ mrp_debug("timer dump:");
+ i = 0;
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ mrp_debug(" #%d: %p, @%u, next %llu (%s)", i, t, t->msecs, t->expire,
+ is_deleted(t) ? "DEAD" : "alive");
+
+ if (!is_deleted(t) && next == NULL)
+ next = t;
+
+ i++;
+ }
+
+ mrp_debug("next timer: %p", ml->next_timer);
+ mrp_debug("poll timer: %d", ml->poll_timeout);
+
+ if (next != NULL && ml->next_timer != NULL &&
+ !is_deleted(ml->next_timer) && next != ml->next_timer) {
+ mrp_debug("*** BUG ml->next_timer is not the nearest !!! ***");
+ if (getenv("__MURPHY_TIMER_CHECK_ABORT") != NULL)
+ abort();
+ }
+}
+#endif
+
+
+int mrp_mainloop_prepare(mrp_mainloop_t *ml)
+{
+ mrp_timer_t *next_timer;
+ int timeout, ext_timeout;
+ uint64_t now;
+
+ if (!mrp_list_empty(&ml->deferred)) {
+ timeout = 0;
+ }
+ else {
+ next_timer = ml->next_timer;
+
+ if (next_timer == NULL)
+ timeout = -1;
+ else {
+ now = time_now();
+ if (MRP_UNLIKELY(next_timer->expire <= now))
+ timeout = 0;
+ else
+ timeout = usecs_to_msecs(next_timer->expire - now);
+ }
+ }
+
+ ext_timeout = prepare_subloops(ml);
+
+ if (ext_timeout != -1 && timeout != -1)
+ ml->poll_timeout = MRP_MIN(timeout, ext_timeout);
+ else if (ext_timeout == -1)
+ ml->poll_timeout = timeout;
+ else
+ ml->poll_timeout = ext_timeout;
+
+ if (ml->nevent < ml->niowatch) {
+ ml->nevent = ml->niowatch;
+ ml->events = mrp_realloc(ml->events, ml->nevent * sizeof(*ml->events));
+
+ MRP_ASSERT(ml->events != NULL, "can't allocate epoll event buffer");
+ }
+
+ mrp_debug("mainloop %p prepared: %d I/O watches, timeout %d", ml,
+ ml->niowatch, ml->poll_timeout);
+
+ return TRUE;
+}
+
+
+static size_t poll_events(void *id, mrp_mainloop_t *ml, void **bufp)
+{
+ void *buf;
+ int n;
+
+ if (MRP_UNLIKELY(id != ml->iow)) {
+ mrp_log_error("superloop polling with invalid I/O watch (%p != %p)",
+ id, ml->iow);
+ *bufp = NULL;
+ return 0;
+ }
+
+ buf = mrp_allocz(ml->nevent * sizeof(ml->events[0]));
+
+ if (buf != NULL) {
+ n = epoll_wait(ml->epollfd, buf, ml->nevent, 0);
+
+ if (n < 0)
+ n = 0;
+ }
+ else
+ n = 0;
+
+ *bufp = buf;
+ return n * sizeof(ml->events[0]);
+}
+
+
+int mrp_mainloop_poll(mrp_mainloop_t *ml, int may_block)
+{
+ int n, timeout;
+
+ timeout = may_block && mrp_list_empty(&ml->deferred) ? ml->poll_timeout : 0;
+
+ if (ml->nevent > 0) {
+ if (ml->super_ops == NULL || ml->super_ops->poll_io == NULL) {
+ mrp_debug("polling %d descriptors with timeout %d",
+ ml->nevent, timeout);
+
+ n = epoll_wait(ml->epollfd, ml->events, ml->nevent, timeout);
+
+ if (n < 0 && errno == EINTR)
+ n = 0;
+ }
+ else {
+ mrp_superloop_ops_t *super_ops = ml->super_ops;
+ void *super_data = ml->super_data;
+ void *id = ml->iow;
+ void *buf = ml->events;
+ size_t size = ml->nevent * sizeof(ml->events[0]);
+
+ size = super_ops->poll_io(super_data, id, buf, size);
+ n = size / sizeof(ml->events[0]);
+
+ MRP_ASSERT(n * sizeof(ml->events[0]) == size,
+ "superloop passed us a partial epoll_event");
+ }
+
+ mrp_debug("mainloop %p has %d/%d I/O events waiting", ml, n,
+ ml->nevent);
+
+ ml->poll_result = n;
+ }
+ else {
+ /*
+ * Notes: Practically we should never branch here because
+ * we always have at least ml->sigfd registered for epoll.
+ */
+ if (timeout > 0)
+ usleep(timeout * USECS_PER_MSEC);
+
+ ml->poll_result = 0;
+ }
+
+ return TRUE;
+}
+
+
+static int poll_subloop(mrp_subloop_t *sl)
+{
+ struct epoll_event *e;
+ struct pollfd *pfd;
+ int fd, idx, n, i;
+
+ if (sl->poll) {
+ n = epoll_wait(sl->epollfd, sl->events, sl->nevent, 0);
+
+ if (n < 0 && errno == EINTR)
+ n = 0;
+
+ for (i = 0, e = sl->events; i < n; i++, e++) {
+ fd = e->data.fd;
+ idx = ((int)(ptrdiff_t)fdtbl_lookup(sl->fdtbl, fd)) - 1;
+
+ if (0 <= idx && idx < sl->npollfd) {
+ pfd = sl->pollfds + idx;
+ pfd->revents = e->events;
+ }
+ }
+
+ mrp_debug("subloop %p has %d fds ready", sl, sl->npollfd);
+
+ return n;
+ }
+ else {
+ mrp_debug("subloop %p has poll flag off", sl);
+
+ return 0;
+ }
+}
+
+
+static void dispatch_wakeup(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_wakeup_t *w;
+ mrp_wakeup_event_t event;
+ uint64_t now;
+
+ if (ml->poll_timeout == 0) {
+ mrp_debug("skipping wakeup callbacks (poll timeout was 0)");
+ return;
+ }
+
+ if (ml->poll_result == 0) {
+ mrp_debug("woken up by timeout");
+ event = MRP_WAKEUP_EVENT_TIMER;
+ }
+ else {
+ mrp_debug("woken up by I/O (or signal)");
+ event = MRP_WAKEUP_EVENT_IO;
+ }
+
+ now = time_now();
+
+ mrp_list_foreach(&ml->wakeups, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+
+ if (!(w->events & event))
+ continue;
+
+ if (!is_deleted(w)) {
+ mrp_debug("dispatching wakeup cb %p", w);
+ wakeup_cb(w, event, now);
+ }
+ else
+ mrp_debug("skipping deleted wakeup cb %p", w);
+
+ if (ml->quit)
+ break;
+ }
+}
+
+
+static void dispatch_deferred(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_deferred_t *d;
+
+ mrp_list_foreach(&ml->deferred, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+
+ if (!is_deleted(d) && !d->inactive) {
+ mrp_debug("dispatching active deferred cb %p", d);
+ d->cb(d, d->user_data);
+ }
+ else
+ mrp_debug("skipping %s deferred cb %p",
+ is_deleted(d) ? "deleted" : "inactive", d);
+
+ if (!is_deleted(d) && d->inactive)
+ disable_deferred(d);
+
+ if (ml->quit)
+ break;
+ }
+}
+
+
+static void dispatch_timers(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_timer_t *t;
+ uint64_t now;
+
+ now = time_now();
+
+ mrp_list_foreach(&ml->timers, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (!is_deleted(t)) {
+ if (t->expire <= now) {
+ mrp_debug("dispatching expired timer %p", t);
+
+ t->cb(t, t->user_data);
+
+ if (!is_deleted(t))
+ rearm_timer(t);
+ }
+ else
+ break;
+ }
+ else
+ mrp_debug("skipping deleted timer %p", t);
+
+ if (ml->quit)
+ break;
+ }
+}
+
+
+static void dispatch_subloops(mrp_mainloop_t *ml)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_subloop_t *sl;
+
+ mrp_list_foreach(&ml->subloops, p, n) {
+ sl = mrp_list_entry(p, typeof(*sl), hook);
+
+ if (!is_deleted(sl)) {
+ poll_subloop(sl);
+
+ if (sl->cb->check(sl->user_data, sl->pollfds,
+ sl->npollfd)) {
+ mrp_debug("dispatching subloop %p", sl);
+ sl->cb->dispatch(sl->user_data);
+ }
+ else
+ mrp_debug("skipping subloop %p, check said no", sl);
+ }
+ }
+}
+
+
+static void dispatch_slaves(mrp_io_watch_t *w, struct epoll_event *e)
+{
+ mrp_io_watch_t *s;
+ mrp_list_hook_t *p, *n;
+ mrp_io_event_t events;
+
+ events = e->events & ~(MRP_IO_EVENT_INOUT & w->events);
+
+ mrp_list_foreach(&w->slave, p, n) {
+ if (events == MRP_IO_EVENT_NONE)
+ break;
+
+ s = mrp_list_entry(p, typeof(*s), slave);
+
+ if (!is_deleted(s)) {
+ mrp_debug("dispatching slave I/O watch %p (fd %d)", s, s->fd);
+ s->cb(s, s->fd, events, s->user_data);
+ }
+ else
+ mrp_debug("skipping slave I/O watch %p (fd %d)", s, s->fd);
+
+ events &= ~(MRP_IO_EVENT_INOUT & s->events);
+ }
+}
+
+
+static void dispatch_poll_events(mrp_mainloop_t *ml)
+{
+ struct epoll_event *e;
+ mrp_io_watch_t *w, *tblw;
+ int i, fd;
+
+ for (i = 0, e = ml->events; i < ml->poll_result; i++, e++) {
+ fd = e->data.fd;
+ w = fdtbl_lookup(ml->fdtbl, fd);
+
+ if (w == NULL) {
+ mrp_debug("ignoring event for deleted fd %d", fd);
+ continue;
+ }
+
+ if (!is_deleted(w)) {
+ mrp_debug("dispatching I/O watch %p (fd %d)", w, fd);
+ w->cb(w, w->fd, e->events, w->user_data);
+ }
+ else
+ mrp_debug("skipping deleted I/O watch %p (fd %d)", w, fd);
+
+ if (!mrp_list_empty(&w->slave))
+ dispatch_slaves(w, e);
+
+ if (e->events & EPOLLRDHUP) {
+ tblw = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (tblw == w) {
+ mrp_debug("forcibly stop polling fd %d for watch %p", w->fd, w);
+ epoll_del(w);
+ }
+ else if (tblw != NULL)
+ mrp_debug("don't stop polling reused fd %d of watch %p",
+ w->fd, w);
+ }
+ else {
+ if ((e->events & EPOLLHUP) && !is_deleted(w)) {
+ /*
+ * Notes:
+ *
+ * If the user does not react to EPOLLHUPs delivered
+ * we stop monitoring the fd to avoid sitting in an
+ * infinite busy loop just delivering more EPOLLHUP
+ * notifications...
+ */
+
+ if (w->wrhup++ > 5) {
+ tblw = fdtbl_lookup(ml->fdtbl, w->fd);
+
+ if (tblw == w) {
+ mrp_debug("forcibly stop polling fd %d for watch %p",
+ w->fd, w);
+ epoll_del(w);
+ }
+ else if (tblw != NULL)
+ mrp_debug("don't stop polling reused fd %d of watch %p",
+ w->fd, w);
+ }
+ }
+ }
+
+ if (ml->quit)
+ break;
+ }
+
+ if (ml->quit)
+ return;
+
+ dispatch_subloops(ml);
+
+ mrp_debug("done dispatching poll events");
+}
+
+
+int mrp_mainloop_dispatch(mrp_mainloop_t *ml)
+{
+ dispatch_wakeup(ml);
+
+ if (ml->quit)
+ goto quit;
+
+ dispatch_deferred(ml);
+
+ if (ml->quit)
+ goto quit;
+
+ dispatch_timers(ml);
+
+ if (ml->quit)
+ goto quit;
+
+ dispatch_poll_events(ml);
+
+ quit:
+ purge_deleted(ml);
+
+ return !ml->quit;
+}
+
+
+int mrp_mainloop_iterate(mrp_mainloop_t *ml)
+{
+ return
+ mrp_mainloop_prepare(ml) &&
+ mrp_mainloop_poll(ml, TRUE) &&
+ mrp_mainloop_dispatch(ml) &&
+ !ml->quit;
+}
+
+
+int mrp_mainloop_run(mrp_mainloop_t *ml)
+{
+ while (mrp_mainloop_iterate(ml))
+ ;
+
+ return ml->exit_code;
+}
+
+
+void mrp_mainloop_quit(mrp_mainloop_t *ml, int exit_code)
+{
+ ml->exit_code = exit_code;
+ ml->quit = TRUE;
+}
+
+
+/*
+ * debugging routines
+ */
+
+
+static void dump_pollfds(const char *prefix, struct pollfd *fds, int nfd)
+{
+ char *t;
+ int i;
+
+ printf("%s (%d): ", prefix, nfd);
+ for (i = 0, t = ""; i < nfd; i++, t = ", ")
+ printf("%s%d/0x%x", t, fds[i].fd, fds[i].events);
+ printf("\n");
+}
+
+
+/*
+ * event bus and events
+ */
+
+static inline void *ref_event_data(void *data, int format)
+{
+ switch (format & MRP_EVENT_FORMAT_MASK) {
+ case MRP_EVENT_FORMAT_JSON:
+ return mrp_json_ref((mrp_json_t *)data);
+ case MRP_EVENT_FORMAT_MSG:
+ return mrp_msg_ref((mrp_msg_t *)data);
+ default:
+ return data;
+ }
+}
+
+
+static inline void unref_event_data(void *data, int format)
+{
+ switch (format & MRP_EVENT_FORMAT_MASK) {
+ case MRP_EVENT_FORMAT_JSON:
+ mrp_json_unref((mrp_json_t *)data);
+ break;
+ case MRP_EVENT_FORMAT_MSG:
+ mrp_msg_unref((mrp_msg_t *)data);
+ break;
+ default:
+ break;
+ }
+}
+
+
+mrp_event_bus_t *mrp_event_bus_get(mrp_mainloop_t *ml, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_event_bus_t *bus;
+
+ if (name == NULL || !strcmp(name, MRP_GLOBAL_BUS_NAME))
+ return MRP_GLOBAL_BUS;
+
+ mrp_list_foreach(&ml->busses, p, n) {
+ bus = mrp_list_entry(p, typeof(*bus), hook);
+
+ if (!strcmp(bus->name, name))
+ return bus;
+ }
+
+ bus = mrp_allocz(sizeof(*bus));
+
+ if (bus == NULL)
+ return NULL;
+
+ bus->name = mrp_strdup(name);
+
+ if (bus->name == NULL) {
+ mrp_free(bus);
+ return NULL;
+ }
+
+ mrp_list_init(&bus->hook);
+ mrp_list_init(&bus->watches);
+ bus->ml = ml;
+
+ mrp_list_append(&ml->busses, &bus->hook);
+
+ return bus;
+}
+
+
+uint32_t mrp_event_id(const char *name)
+{
+ mrp_event_def_t *e;
+ int i;
+
+ if (events != NULL)
+ for (i = 0, e = events; i < nevent; i++, e++)
+ if (!strcmp(e->name, name))
+ return e->id;
+
+ if (!mrp_reallocz(events, nevent, nevent + 1))
+ return 0;
+
+ e = events + nevent;
+
+ e->id = nevent;
+ e->name = mrp_strdup(name);
+
+ if (e->name == NULL) {
+ mrp_reallocz(events, nevent + 1, nevent);
+ return 0;
+ }
+
+ nevent++;
+
+ return e->id;
+}
+
+
+const char *mrp_event_name(uint32_t id)
+{
+ if ((int)id < nevent)
+ return events[id].name;
+ else
+ return MRP_EVENT_UNKNOWN_NAME;
+}
+
+
+char *mrp_event_dump_mask(mrp_event_mask_t *mask, char *buf, size_t size)
+{
+ char *p, *t;
+ int l, n, id;
+
+ p = buf;
+ l = (int)size;
+ t = "";
+
+ MRP_MASK_FOREACH_SET(mask, id, 1) {
+ n = snprintf(p, l, "%s%s", t, mrp_event_name(id));
+ t = "|";
+
+ if (n >= l)
+ return "<insufficient mask dump buffer>";
+
+ p += n;
+ l -= n;
+ }
+
+ return buf;
+}
+
+
+mrp_event_watch_t *mrp_event_add_watch(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_watch_cb_t cb, void *user_data)
+{
+ mrp_list_hook_t *watches = bus ? &bus->watches : &ewatches;
+ mrp_event_watch_t *w;
+
+ w = mrp_allocz(sizeof(*w));
+
+ if (w == NULL)
+ return NULL;
+
+ mrp_list_init(&w->hook);
+ mrp_mask_init(&w->mask);
+ w->bus = bus;
+ w->cb = cb;
+ w->user_data = user_data;
+
+ if (!mrp_mask_set(&w->mask, id)) {
+ mrp_free(w);
+ return NULL;
+ }
+
+ mrp_list_append(watches, &w->hook);
+
+ mrp_debug("added event watch %p for event %d (%s) on bus %s", w, id,
+ mrp_event_name(id), bus ? bus->name : MRP_GLOBAL_BUS_NAME);
+
+ return w;
+}
+
+
+mrp_event_watch_t *mrp_event_add_watch_mask(mrp_event_bus_t *bus,
+ mrp_event_mask_t *mask,
+ mrp_event_watch_cb_t cb,
+ void *user_data)
+{
+ mrp_list_hook_t *watches = bus ? &bus->watches : &ewatches;
+ mrp_event_watch_t *w;
+ char events[512];
+
+ w = mrp_allocz(sizeof(*w));
+
+ if (w == NULL)
+ return NULL;
+
+ mrp_list_init(&w->hook);
+ mrp_mask_init(&w->mask);
+ w->bus = bus;
+ w->cb = cb;
+ w->user_data = user_data;
+
+ if (!mrp_mask_copy(&w->mask, mask)) {
+ mrp_free(w);
+ return NULL;
+ }
+
+ mrp_list_append(watches, &w->hook);
+
+ mrp_debug("added event watch %p for events <%s> on bus %s", w,
+ mrp_event_dump_mask(&w->mask, events, sizeof(events)),
+ bus ? bus->name : MRP_GLOBAL_BUS_NAME);
+
+ return w;
+}
+
+
+void mrp_event_del_watch(mrp_event_watch_t *w)
+{
+ if (w == NULL)
+ return;
+
+ if (w->bus != NULL && w->bus->busy) {
+ w->dead = TRUE;
+ w->bus->dead++;
+ return;
+ }
+
+ mrp_list_delete(&w->hook);
+ mrp_mask_reset(&w->mask);
+ mrp_free(w);
+}
+
+
+void bus_purge_dead(mrp_event_bus_t *bus)
+{
+ mrp_event_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ if (!bus->dead)
+ return;
+
+ mrp_list_foreach(&bus->watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+
+ if (!w->dead)
+ continue;
+
+ mrp_list_delete(&w->hook);
+ mrp_mask_reset(&w->mask);
+ mrp_free(w);
+ }
+
+ bus->dead = 0;
+}
+
+
+static int queue_event(mrp_event_bus_t *bus, uint32_t id, void *data,
+ mrp_event_flag_t flags)
+{
+ pending_event_t *e;
+
+ e = mrp_allocz(sizeof(*e));
+
+ if (e == NULL)
+ return -1;
+
+ mrp_list_init(&e->hook);
+ e->bus = bus;
+ e->id = id;
+ e->format = flags & MRP_EVENT_FORMAT_MASK;
+ e->data = ref_event_data(data, e->format);
+ mrp_list_append(&bus->ml->eventq, &e->hook);
+
+ mrp_enable_deferred(bus->ml->eventd);
+
+ return 0;
+}
+
+
+static int emit_event(mrp_event_bus_t *bus, uint32_t id, void *data,
+ mrp_event_flag_t flags)
+{
+ mrp_list_hook_t *watches;
+ mrp_event_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ if (bus)
+ watches = &bus->watches;
+ else {
+ if (!(flags & MRP_EVENT_SYNCHRONOUS)) {
+ errno = EINVAL;
+ return -1;
+ }
+ watches = &ewatches;
+ }
+
+ if (bus)
+ bus->busy++;
+
+ mrp_debug("emitting event 0x%x (%s) on bus <%s>", id, mrp_event_name(id),
+ bus ? bus->name : MRP_GLOBAL_BUS_NAME);
+
+ mrp_list_foreach(watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+
+ if (w->dead)
+ continue;
+
+ if (mrp_mask_test(&w->mask, id))
+ w->cb(w, id, flags & MRP_EVENT_FORMAT_MASK, data, w->user_data);
+ }
+
+ if (bus) {
+ bus->busy--;
+
+ if (!bus->busy)
+ bus_purge_dead(bus);
+ }
+
+ return 0;
+}
+
+
+static void pump_events(mrp_deferred_t *d, void *user_data)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *)user_data;
+ mrp_list_hook_t *p, *n;
+ pending_event_t *e;
+
+ pump:
+ mrp_list_foreach(&ml->eventq, p, n) {
+ e = mrp_list_entry(p, typeof(*e), hook);
+
+ emit_event(e->bus, e->id, e->data, e->format);
+
+ mrp_list_delete(&e->hook);
+ unref_event_data(e->data, e->format);
+
+ mrp_free(e);
+ }
+
+ if (!mrp_list_empty(&ml->eventq))
+ goto pump;
+
+ mrp_disable_deferred(d);
+}
+
+
+int mrp_emit_event(mrp_event_bus_t *bus, uint32_t id, mrp_event_flag_t flags,
+ void *data)
+{
+ int status;
+
+ if (flags & MRP_EVENT_SYNCHRONOUS) {
+ ref_event_data(data, flags);
+ status = emit_event(bus, id, data, flags);
+ unref_event_data(data, flags);
+
+ return status;
+ }
+ else {
+ if (bus != NULL)
+ return queue_event(bus, id, data, flags);
+
+ errno = EOPNOTSUPP;
+ return -1;
+ }
+}
+
+
+int _mrp_event_emit_msg(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_flag_t flags, ...)
+{
+ mrp_msg_t *msg;
+ uint16_t tag;
+ va_list ap;
+ int status;
+
+ va_start(ap, flags);
+ tag = va_arg(ap, unsigned int);
+ msg = tag ? mrp_msg_createv(tag, ap) : NULL;
+ va_end(ap);
+
+ flags &= ~MRP_EVENT_FORMAT_MASK;
+ status = mrp_emit_event(bus, id, flags | MRP_EVENT_FORMAT_MSG, msg);
+ mrp_msg_unref(msg);
+
+ return status;
+}
+
+
+MRP_INIT static void init_events(void)
+{
+ MRP_ASSERT(mrp_event_id(MRP_EVENT_UNKNOWN_NAME) == MRP_EVENT_UNKNOWN,
+ "reserved id 0x%x for builtin event <%s> already taken",
+ MRP_EVENT_UNKNOWN, MRP_EVENT_UNKNOWN_NAME);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MAINLOOP_H__
+#define __MURPHY_MAINLOOP_H__
+
+#include <signal.h>
+#include <stdint.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_mainloop_s mrp_mainloop_t;
+
+/*
+ * I/O watches
+ */
+
+/** I/O events */
+typedef enum {
+ MRP_IO_EVENT_NONE = 0x0,
+ MRP_IO_EVENT_IN = EPOLLIN,
+ MRP_IO_EVENT_PRI = EPOLLPRI,
+ MRP_IO_EVENT_OUT = EPOLLOUT,
+ MRP_IO_EVENT_RDHUP = EPOLLRDHUP,
+ MRP_IO_EVENT_WRHUP = EPOLLHUP,
+ MRP_IO_EVENT_HUP = EPOLLRDHUP|EPOLLHUP,
+ MRP_IO_EVENT_ERR = EPOLLERR,
+ MRP_IO_EVENT_INOUT = EPOLLIN|EPOLLOUT,
+ MRP_IO_EVENT_ALL = EPOLLIN|EPOLLPRI|EPOLLOUT|EPOLLRDHUP|EPOLLERR,
+ /* event trigger modes */
+ MRP_IO_TRIGGER_LEVEL = 0x1U << 25,
+ MRP_IO_TRIGGER_EDGE = EPOLLET,
+ MRP_IO_TRIGGER_MASK = MRP_IO_TRIGGER_LEVEL|MRP_IO_TRIGGER_EDGE
+} mrp_io_event_t;
+
+typedef struct mrp_io_watch_s mrp_io_watch_t;
+
+/** I/O watch notification callback type. */
+typedef void (*mrp_io_watch_cb_t)(mrp_io_watch_t *w, int fd,
+ mrp_io_event_t events, void *user_data);
+/** Register a new file descriptor to watch. */
+mrp_io_watch_t *mrp_add_io_watch(mrp_mainloop_t *ml, int fd,
+ mrp_io_event_t events,
+ mrp_io_watch_cb_t cb, void *user_data);
+/** Unregister an I/O watch. */
+void mrp_del_io_watch(mrp_io_watch_t *watch);
+
+/** Get the mainloop of an I/O watch. */
+mrp_mainloop_t *mrp_get_io_watch_mainloop(mrp_io_watch_t *watch);
+
+/** Set the default event trigger mode for the given mainloop. */
+int mrp_set_io_event_mode(mrp_mainloop_t *ml, mrp_io_event_t mode);
+
+/** Get the default event trigger mode of the given mainloop. */
+mrp_io_event_t mrp_get_io_event_mode(mrp_mainloop_t *ml);
+
+/*
+ * timers
+ */
+
+typedef struct mrp_timer_s mrp_timer_t;
+
+/** Timer notification callback type. */
+typedef void (*mrp_timer_cb_t)(mrp_timer_t *t, void *user_data);
+
+/** Add a new timer. */
+mrp_timer_t *mrp_add_timer(mrp_mainloop_t *ml, unsigned int msecs,
+ mrp_timer_cb_t cb, void *user_data);
+/** Modify the timeout or rearm the given timer. */
+#define MRP_TIMER_RESTART (unsigned int)-1
+void mrp_mod_timer(mrp_timer_t *t, unsigned int msecs);
+
+/** Delete a timer. */
+void mrp_del_timer(mrp_timer_t *t);
+
+/** Get the mainloop of a timer. */
+mrp_mainloop_t *mrp_get_timer_mainloop(mrp_timer_t *t);
+
+
+/*
+ * deferred callbacks
+ */
+
+typedef struct mrp_deferred_s mrp_deferred_t;
+
+/** Deferred callback notification callback type. */
+typedef void (*mrp_deferred_cb_t)(mrp_deferred_t *d, void *user_data);
+
+/** Add a deferred callback. */
+mrp_deferred_t *mrp_add_deferred(mrp_mainloop_t *ml, mrp_deferred_cb_t cb,
+ void *user_data);
+/** Remove a deferred callback. */
+void mrp_del_deferred(mrp_deferred_t *d);
+
+/** Disable a deferred callback. */
+void mrp_disable_deferred(mrp_deferred_t *d);
+
+/** Enable a deferred callback. */
+void mrp_enable_deferred(mrp_deferred_t *d);
+
+/** Get the mainloop of a deferred callback. */
+mrp_mainloop_t *mrp_get_deferred_mainloop(mrp_deferred_t *d);
+
+
+/*
+ * signals
+ */
+
+typedef struct mrp_sighandler_s mrp_sighandler_t;
+
+/* Signal handler callback type. */
+typedef void (*mrp_sighandler_cb_t)(mrp_sighandler_t *h, int signum,
+ void *user_data);
+/** Register a signal handler. */
+mrp_sighandler_t *mrp_add_sighandler(mrp_mainloop_t *ml, int signum,
+ mrp_sighandler_cb_t cb, void *user_data);
+/** Unregister a signal handler. */
+void mrp_del_sighandler(mrp_sighandler_t *h);
+
+/** Get the mainloop of a signal handler. */
+mrp_mainloop_t *mrp_get_sighandler_mainloop(mrp_sighandler_t *h);
+
+
+/*
+ * wakeup callbacks
+ */
+
+/** I/O events */
+typedef enum {
+ MRP_WAKEUP_EVENT_NONE = 0x0, /* no event */
+ MRP_WAKEUP_EVENT_TIMER = 0x1, /* woken up by timeout */
+ MRP_WAKEUP_EVENT_IO = 0x2, /* woken up by I/O (or signal) */
+ MRP_WAKEUP_EVENT_ANY = 0x3, /* mask of all selectable events */
+ MRP_WAKEUP_EVENT_LIMIT = 0x4, /* woken up by forced trigger */
+} mrp_wakeup_event_t;
+
+typedef struct mrp_wakeup_s mrp_wakeup_t;
+
+/** Wakeup callback notification type. */
+typedef void (*mrp_wakeup_cb_t)(mrp_wakeup_t *w, mrp_wakeup_event_t event,
+ void *user_data);
+
+/** Add a wakeup callback for the specified events. lpf_msecs and
+ * force_msecs specifiy two limiting intervals in milliseconds.
+ * lpf_msecs is a low-pass filtering interval. It is guaranteed that
+ * the wakeup callback will not be invoked more ofter than this.
+ * force_msecs is a forced trigger interval. It is guaranteed that
+ * the wakeup callback will be triggered at least this often. You can
+ * MRP_WAKEUP_NOLIMIT to omit either or both limiting intervals. */
+#define MRP_WAKEUP_NOLIMIT ((unsigned int)0)
+mrp_wakeup_t *mrp_add_wakeup(mrp_mainloop_t *ml, mrp_wakeup_event_t events,
+ unsigned int lpf_msecs, unsigned int force_msecs,
+ mrp_wakeup_cb_t cb, void *user_data);
+
+/** Remove a wakeup callback. */
+void mrp_del_wakeup(mrp_wakeup_t *w);
+
+/** Get the mainloop of a wakeup callback. */
+mrp_mainloop_t *mrp_get_wakeup_mainloop(mrp_wakeup_t *w);
+
+
+/*
+ * subloops - external mainloops pumped by this mainloop
+ */
+
+typedef struct mrp_subloop_s mrp_subloop_t;
+
+typedef struct {
+ int (*prepare)(void *user_data);
+ int (*query)(void *user_data, struct pollfd *fds, int nfd, int *timeout);
+ int (*check)(void *user_data, struct pollfd *fds, int nfd);
+ void (*dispatch)(void *user_data);
+} mrp_subloop_ops_t;
+
+
+/** Register an external mainloop to be pumped by the given mainloop. */
+mrp_subloop_t *mrp_add_subloop(mrp_mainloop_t *ml, mrp_subloop_ops_t *ops,
+ void *user_data);
+
+/** Stop pumping a registered external mainloop. */
+void mrp_del_subloop(mrp_subloop_t *sl);
+
+
+/*
+ * superloops - external mainloop to pump murphy mainloops
+ */
+
+typedef struct {
+ void *(*add_io)(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+ void (*del_io)(void *glue_data, void *id);
+
+ void *(*add_timer)(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+ void (*del_timer)(void *glue_data, void *id);
+ void (*mod_timer)(void *glue_data, void *id, unsigned int msecs);
+
+ void *(*add_defer)(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+ void (*del_defer)(void *glue_data, void *id);
+ void (*mod_defer)(void *glue_data, void *id, int enabled);
+ void (*unregister)(void *glue_data);
+
+ /*
+ * Notes:
+ *
+ * This is a band-aid attempt to get our mainloop run under the
+ * threaded event loop of xwalk which has strict limitations about
+ * what (type of) thread can access which functionality of the
+ * event loop. In particular, the I/O watch equivalent is limited
+ * for the I/O thread. In the current media element resource infra
+ * integration code of xwalk, the related code is run in the UI
+ * thread. This makes interacting from there with the daemon using
+ * the stock resource libraries (in particular, pumping the mainloop)
+ * non-straightforward.
+ *
+ * The superloop glue code is supposed to use poll_events to trigger
+ * a nonblocking epoll_wait on our epoll fd to retrieve (and cache)
+ * pending epoll events from the kernel. poll_io is then used by us
+ * to retrieve pending epoll events from the glue code. The glue code
+ * needs to take care of any necessary locking to protect itself/us
+ * from potentially concurrent invocations of poll_io and poll_events
+ * from different threads.
+ *
+ * The superloop abstraction now became really really ugly. poll_io
+ * and poll_events implicitly assume/know that add_io/del_io is only
+ * used for adding a single superloop I/O watch for our epoll fd.
+ * The I/O watch id in the functions below is passed around only for
+ * doublechecing this. Probably we should change the I/O watch part
+ * of the superloop API to be actually less generic and only usable
+ * for the epoll fd to avoid further confusion.
+ */
+ size_t (*poll_events)(void *id, mrp_mainloop_t *ml, void **events);
+ size_t (*poll_io)(void *glue_data, void *id, void *buf, size_t size);
+} mrp_superloop_ops_t;
+
+
+/** Set a superloop to pump the given mainloop. */
+int mrp_set_superloop(mrp_mainloop_t *ml, mrp_superloop_ops_t *ops,
+ void *loop_data);
+
+/** Clear the superloop that pumps the given mainloop. */
+int mrp_clear_superloop(mrp_mainloop_t *ml);
+
+/** Unregister a mainloop from its superloop if it has one. */
+int mrp_mainloop_unregister(mrp_mainloop_t *ml);
+
+/*
+ * mainloop
+ */
+
+/** Create a new mainloop. */
+mrp_mainloop_t *mrp_mainloop_create(void);
+
+/** Destroy an existing mainloop, free all I/O watches, timers, etc. */
+void mrp_mainloop_destroy(mrp_mainloop_t *ml);
+
+/** Keep iterating the mainloop until mrp_mainloop_quit is called. */
+int mrp_mainloop_run(mrp_mainloop_t *ml);
+
+/** Prepare the mainloop for polling. */
+int mrp_mainloop_prepare(mrp_mainloop_t *ml);
+
+/** Poll the mainloop. */
+int mrp_mainloop_poll(mrp_mainloop_t *ml, int may_block);
+
+/** Dispatch pending events of the mainloop. */
+int mrp_mainloop_dispatch(mrp_mainloop_t *ml);
+
+/** Run a single prepare-poll-dispatch cycle of the mainloop. */
+int mrp_mainloop_iterate(mrp_mainloop_t *ml);
+
+/** Quit the mainloop. */
+void mrp_mainloop_quit(mrp_mainloop_t *ml, int exit_code);
+
+/*
+ * event bus and and events
+ */
+
+#include <murphy/common/mask.h>
+#include <murphy/common/msg.h>
+
+#define MRP_GLOBAL_BUS NULL /* global synchronous bus */
+#define MRP_GLOBAL_BUS_NAME "global" /* global synchronous bus name */
+
+/** event flags */
+typedef enum {
+ MRP_EVENT_ASYNCHRONOUS = 0x00, /* deliver asynchronously */
+ MRP_EVENT_SYNCHRONOUS = 0x01, /* deliver synchronously */
+ MRP_EVENT_FORMAT_JSON = 0x01 << 1, /* attached data is JSON */
+ MRP_EVENT_FORMAT_MSG = 0x02 << 1, /* attached data is mrp_msg_t */
+ MRP_EVENT_FORMAT_CUSTOM = 0x03 << 1, /* attached data is of custom format */
+ MRP_EVENT_FORMAT_MASK = 0x03 << 1,
+} mrp_event_flag_t;
+
+/** Reserved event id for unknown events. */
+#define MRP_EVENT_UNKNOWN 0
+
+/** Reserved name for unknown events. */
+#define MRP_EVENT_UNKNOWN_NAME "unknown"
+
+/**
+ * Event definition for registering event(name)s.
+ */
+typedef struct {
+ char *name; /* event name */
+ uint32_t id; /* event id assigned by murphy */
+} mrp_event_def_t;
+
+/** Opaque event watch type. */
+typedef struct mrp_event_watch_s mrp_event_watch_t;
+
+/** Event watch notification callback type. */
+typedef void (*mrp_event_watch_cb_t)(mrp_event_watch_t *w, uint32_t id,
+ int format, void *data, void *user_data);
+
+/** Opaque event bus type. */
+typedef struct mrp_event_bus_s mrp_event_bus_t;
+
+/** Event mask type (a bitmask of event ids). */
+typedef mrp_mask_t mrp_event_mask_t;
+
+/** Look up (or create) the event bus with the given name. */
+mrp_event_bus_t *mrp_event_bus_get(mrp_mainloop_t *ml, const char *name);
+#define mrp_event_bus_create mrp_event_bus_get
+
+/** Look up (or register) the id of the given event. */
+uint32_t mrp_event_id(const char *name);
+#define mrp_event_register mrp_event_id
+
+/** Look up the name of the given event. */
+const char *mrp_event_name(uint32_t id);
+
+/** Dump the names of the events set in mask. */
+char *mrp_event_dump_mask(mrp_event_mask_t *mask, char *buf, size_t size);
+
+/** Register an event watch for a single event on the given bus. */
+mrp_event_watch_t *mrp_event_add_watch(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_watch_cb_t cb, void *user_data);
+
+/** Register an event watch for a number of events on the given bus. */
+mrp_event_watch_t *mrp_event_add_watch_mask(mrp_event_bus_t *bus,
+ mrp_event_mask_t *mask,
+ mrp_event_watch_cb_t cb,
+ void *user_data);
+
+/** Unregister the given event watch. */
+void mrp_event_del_watch(mrp_event_watch_t *w);
+
+/** Emit the given event with the data attached on the given bus. */
+int mrp_event_emit(mrp_event_bus_t *bus, uint32_t id, mrp_event_flag_t flags,
+ void *data);
+
+/** Convenience macro to emit an event with JSON data attached. */
+#define mrp_event_emit_json(bus, id, flags, data) \
+ mrp_event_emit((bus), (id), (flags) | MRP_EVENT_FORMAT_JSON, (data))
+
+/** Convenience macro to emit an event with mrp_msg_t data attached. */
+#define mrp_event_emit_msg(bus, id, flags, ...) \
+ _mrp_event_emit_msg((bus), (id), (flags), __VA_ARGS__, MRP_MSG_END)
+
+/** Emit an event with mrp_msg_t data attached. */
+int _mrp_event_emit_msg(mrp_event_bus_t *bus, uint32_t id,
+ mrp_event_flag_t flags, ...);
+
+/** Convenience macro to emit an event with custom data attached. */
+#define mrp_event_emit_custom(bus, id, data, flags) \
+ mrp_event_emit((bus), (id), (data), (flags) | MRP_EVENT_FORMAT_CUSTOM)
+
+/** Convenience macros for autoregistering a table of events on startup. */
+#define MRP_EVENT(_name, _id) [_id] = { .name = _name, .id = 0 }
+#define MRP_REGISTER_EVENTS(_event_table, ...) \
+ static mrp_event_def_t _event_table[] = { \
+ __VA_ARGS__, \
+ { .name = NULL } \
+ }; \
+ \
+ MRP_INIT static void register_##_event_table(void) \
+ { \
+ mrp_event_def_t *e; \
+ \
+ for (e = _event_table; e->name != NULL; e++) { \
+ e->id = mrp_event_register(e->name); \
+ \
+ if (e->id != MRP_EVENT_UNKNOWN) \
+ mrp_log_info("Event '%s' registered as 0x%x.", \
+ e->name, e->id); \
+ else \
+ mrp_log_error("Failed to register event '%s'.", \
+ e->name); \
+ } \
+ } \
+ struct __mrp_allow_trailing_semicolon
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MAINLOOP_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MASK_H__
+#define __MURPHY_MASK_H__
+
+#include <stdint.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+
+
+MRP_CDECL_BEGIN
+
+/** Type used to store bits in bitmasks. */
+typedef uint64_t _mask_t;
+
+
+/**
+ * trivial representation of a bitmask of arbitrary size
+ */
+
+typedef struct {
+ int nbit; /* number of bits in this mask */
+ union {
+ _mask_t bits; /* bits for nbit <= 64 */
+ _mask_t *bitp; /* bits for nbit > 64 */
+ };
+} mrp_mask_t;
+
+
+/** Macro to intialize a bitmask to empty. */
+#define MRP_MASK_EMPTY { .nbit = 64, .bits = 0 }
+#define MRP_MASK_INIT(m) do { (m)->nbit = 64; (m)->bits = 0; } while (0)
+
+/** Macro to declare a bitmask variable and initialize it. */
+#define MRP_MASK(m) mrp_mask_t m = MRP_MASK_EMPTY
+
+/* Various bit-fiddling macros. */
+#define MRP_MASK_BIT(bit) (1ULL << (bit))
+#define MRP_MASK_UPTO(bit) ((1ULL << (bit)) - 1)
+#define MRP_MASK_BELOW(bit) (MRP_MASK_UPTO(bit) >> 1)
+
+
+#define _BITS_PER_WORD ((int)(sizeof(_mask_t) * 8))
+#define _WORD(bit) ((bit) / _BITS_PER_WORD)
+#define _BIT(bit) ((bit) & (_BITS_PER_WORD - 1))
+#define _MASK(bit) (0x1ULL << (bit))
+
+
+/** Initialize the given mask. */
+static inline void mrp_mask_init(mrp_mask_t *m)
+{
+#ifndef __cplusplus
+ *m = (mrp_mask_t)MRP_MASK_EMPTY;
+#else
+ MRP_MASK_INIT(m);
+#endif
+}
+
+
+/** Reset the given mask. */
+static inline void mrp_mask_reset(mrp_mask_t *m)
+{
+ if (m->nbit > _BITS_PER_WORD)
+ mrp_free(m->bitp);
+
+ mrp_mask_init(m);
+}
+
+
+/** Ensure the given mask to accomodate the given number of bits. */
+static inline mrp_mask_t *mrp_mask_ensure(mrp_mask_t *m, int bits)
+{
+ _mask_t w;
+ int o, n;
+
+ if (bits <= m->nbit)
+ return m;
+
+ if (m->nbit == _BITS_PER_WORD) {
+ w = m->bits;
+ n = (bits + _BITS_PER_WORD - 1) / _BITS_PER_WORD;
+
+ m->bitp = (_mask_t *)mrp_allocz(n * sizeof(*m->bitp));
+
+ if (m->bitp == NULL) {
+ m->bits = w;
+
+ return NULL;
+ }
+
+ m->bitp[0] = w;
+ m->nbit = n * _BITS_PER_WORD;
+ }
+ else {
+ o = m->nbit / _BITS_PER_WORD;
+ n = (bits + _BITS_PER_WORD - 1) / _BITS_PER_WORD;
+
+ if (!mrp_reallocz(m->bitp, o, n + 1))
+ return NULL;
+
+ m->nbit = n * _BITS_PER_WORD;
+ }
+
+ return m;
+}
+
+
+/** Resize mask to accomodate the given number of bits, truncate if possible. */
+static inline mrp_mask_t *mrp_mask_trunc(mrp_mask_t *m, int bits)
+{
+ int n;
+ uint64_t *bitp;
+
+ if (m->nbit <= bits)
+ return mrp_mask_ensure(m, bits);
+
+ n = (bits + _BITS_PER_WORD - 1) / _BITS_PER_WORD;
+
+ if (n == 1) {
+ bitp = m->bitp;
+ m->bits = bitp[0];
+
+ mrp_free(bitp);
+ }
+ else
+ mrp_reallocz(m->bitp, m->nbit / _BITS_PER_WORD, n);
+
+ m->nbit = n * _BITS_PER_WORD;
+
+ return m;
+}
+
+
+/** Set the given bit in the mask. */
+static inline mrp_mask_t *mrp_mask_set(mrp_mask_t *m, int bit)
+{
+ int w, b;
+
+ if (!mrp_mask_ensure(m, bit))
+ return NULL;
+
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ m->bits |= _MASK(b);
+ else {
+ w = _WORD(bit);
+ m->bitp[w] |= _MASK(b);
+ }
+
+ return m;
+}
+
+
+/** Clear the given bit in the mask. */
+static inline mrp_mask_t *mrp_mask_clear(mrp_mask_t *m, int bit)
+{
+ int w, b;
+
+ if (bit >= m->nbit)
+ return m;
+
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ m->bits &= ~_MASK(b);
+ else {
+ w = _WORD(bit);
+ m->bitp[w] &= ~_MASK(b);
+ }
+
+ return m;
+}
+
+
+/** Test the given bit in the mask. */
+static inline int mrp_mask_test(mrp_mask_t *m, int bit)
+{
+ int w, b;
+
+ if (bit >= m->nbit)
+ return 0;
+
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ return !!(m->bits & _MASK(b));
+ else {
+ w = _WORD(bit);
+ return !!(m->bitp[w] & _MASK(b));
+ }
+}
+
+
+/** Copy the given mask, overwriting dst. */
+static inline mrp_mask_t *mrp_mask_copy(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ mrp_mask_reset(dst);
+
+ dst->nbit = src->nbit;
+
+ if (src->nbit == _BITS_PER_WORD)
+ *dst = *src;
+ else {
+ dst->bitp = (_mask_t *)mrp_alloc(dst->nbit * _BITS_PER_WORD);
+
+ if (dst->bitp == NULL)
+ return NULL;
+
+ memcpy(dst->bitp, src->bitp, dst->nbit * _BITS_PER_WORD);
+ }
+
+ return dst;
+}
+
+
+/** Set all bits in src into dst (dst |= src). */
+static inline mrp_mask_t *mrp_mask_or(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ int i, n;
+
+ if (!mrp_mask_ensure(dst, src->nbit))
+ return NULL;
+
+ if (src->nbit == _BITS_PER_WORD) {
+ if (dst->nbit == _BITS_PER_WORD)
+ dst->bits |= src->bits;
+ else
+ dst->bitp[0] |= src->bits;
+ }
+ else {
+ n = src->nbit / _BITS_PER_WORD;
+
+ for (i = 0; i < n; i++)
+ dst->bitp[i] |= src->bitp[i];
+ }
+
+ return dst;
+}
+
+
+/** Mask all bits in dst with the corresponding ones from src (dst &= src). */
+static inline mrp_mask_t *mrp_mask_and(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ int i, n;
+
+ n = MRP_MIN(dst->nbit, src->nbit);
+ mrp_mask_trunc(dst, n);
+
+ n /= _BITS_PER_WORD;
+
+ if (dst->nbit == _BITS_PER_WORD) {
+ if (src->nbit == _BITS_PER_WORD)
+ dst->bits &= src->bits;
+ else
+ dst->bits &= src->bitp[0];
+ }
+ else {
+ for (i = 0; i < n; i++)
+ dst->bitp[i] &= src->bitp[i];
+ }
+
+ return dst;
+}
+
+
+/** Set all bits in src into dst (dst ^= src). */
+static inline mrp_mask_t *mrp_mask_xor(mrp_mask_t *dst, mrp_mask_t *src)
+{
+ int i, n;
+
+ if (!mrp_mask_ensure(dst, src->nbit))
+ return NULL;
+
+ if (src->nbit == _BITS_PER_WORD) {
+ if (dst->nbit == _BITS_PER_WORD)
+ dst->bits |= src->bits;
+ else
+ dst->bitp[0] |= src->bits;
+
+#if 0
+ /*
+ * Hmm... this would consider those bits in src which are not
+ * actually there but are in dst to be implicit 0's. However,
+ * I'm not sure if this really is a good idea... Needs a bit
+ * exposure to using this code to decide.
+ */
+
+ n = dst->nbit / _BITS_PER_WORD;
+ for (i = 1; i < n; i++)
+ dst->bitp[i] ^= 0;
+#endif
+ }
+ else {
+ n = src->nbit / _BITS_PER_WORD;
+ for (i = 0; i < n; i++)
+ dst->bitp[i] ^= src->bitp[i];
+
+#if 0
+ /*
+ * Hmm... ditto for this piece of code.
+ */
+
+ n = dst->nbit / _BITS_PER_WORD;
+ while (i < n)
+ dst->bitp[i] ^= 0;
+#endif
+ }
+
+ return dst;
+}
+
+
+/** Negate all bits in mask (~mask). */
+static inline mrp_mask_t *mrp_mask_neg(mrp_mask_t *m)
+{
+ int i, n;
+
+ if (m->nbit == _BITS_PER_WORD)
+ m->bits = ~m->bits;
+ else {
+ n = m->nbit / _BITS_PER_WORD;
+
+ for (i = 0; i < n; i++)
+ m->bitp[i] = ~m->bitp[i];
+ }
+
+ return m;
+}
+
+
+/** Find the first bit set (1-based indexing) in the given mask. */
+static inline int mrp_ffsll(_mask_t bits)
+{
+#ifdef __GNUC__
+ return __builtin_ffsll(bits);
+#else
+ _mask_t mask = 0xffffffff;
+ int w, n;
+
+ if (!bits)
+ return 0;
+
+ n = 0;
+ w = _BITS_PER_WORD / 2;
+ while (w) {
+ if (!(bits & mask)) {
+ bits >>= w;
+ mask >>= w / 2;
+ n += w;
+ w /= 2;
+ }
+ else {
+ bits &= mask;
+ mask >>= w / 2;
+ w /= 2;
+ }
+ }
+
+ return n + 1;
+#endif
+}
+
+
+/** Get the first bit set starting at the given bit. */
+static inline int mrp_mask_next_set(mrp_mask_t *m, int bit)
+{
+ _mask_t wrd, clr;
+ int w, b, n;
+
+ while (bit < m->nbit - 1) {
+ w = _WORD(bit);
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ wrd = m->bits;
+ else
+ wrd = m->bitp[w];
+
+ clr = ~(_MASK(b) - 1);
+ n = mrp_ffsll(wrd & clr);
+
+ if (n > 0)
+ return w * _BITS_PER_WORD + n - 1;
+
+ bit = (bit + _BITS_PER_WORD) & ~(_BITS_PER_WORD - 1);
+ }
+
+ return -1;
+}
+
+
+/** Get the first bit cleared starting at the given bit. */
+static inline int mrp_mask_next_clear(mrp_mask_t *m, int bit)
+{
+ _mask_t wrd, clr;
+ int w, b, n;
+
+ while (bit < m->nbit - 1) {
+ w = _WORD(bit);
+ b = _BIT(bit);
+
+ if (m->nbit == _BITS_PER_WORD)
+ wrd = m->bits;
+ else
+ wrd = m->bitp[w];
+
+ clr = _MASK(b) - 1;
+ n = mrp_ffsll(~(wrd | clr));
+
+ if (n > 0)
+ return w * _BITS_PER_WORD + n - 1;
+
+ bit = (bit + _BITS_PER_WORD) & ~(_BITS_PER_WORD - 1);
+ }
+
+ return -1;
+}
+
+
+/** Loop through all bits set in a mask. */
+#define MRP_MASK_FOREACH_SET(m, bit, start) \
+ for (bit = mrp_mask_next_set(m, start); \
+ bit >= 0; \
+ bit = mrp_mask_next_set(m, bit + 1))
+
+
+/** Loop through all bits cleared in a mask. */
+#define MRP_MASK_FOREACH_CLEAR(m, bit, start) \
+ for (bit = mrp_mask_next_clear(m, start); \
+ bit >= 0; \
+ bit = mrp_mask_next_clear(m, bit + 1))
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MASK_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <errno.h>
+#include <unistd.h>
+#include <execinfo.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/hashtbl.h>
+
+
+#define DEFAULT_DEPTH 8 /* default backtrace depth */
+#define MAX_DEPTH 128 /* max. backtrace depth */
+
+/*
+ * memory allocator state
+ */
+
+typedef struct {
+ mrp_list_hook_t blocks; /* list of allocated blocks */
+ size_t hdrsize; /* header size */
+ int depth; /* backtrace depth */
+ uint32_t cur_blocks; /* currently allocated blocks */
+ uint32_t max_blocks; /* max allocated blocks */
+ uint64_t cur_alloc; /* currently allocated memory */
+ uint64_t max_alloc; /* max allocated memory */
+ int poison; /* poisoning pattern */
+ size_t chunk_size; /* object pool chunk size */
+ mrp_mm_type_t mode; /* passthru/debug mode */
+
+ void *(*alloc)(size_t size, const char *file, int line, const char *func);
+ void *(*realloc)(void *ptr, size_t size, const char *file,
+ int line, const char *func);
+ int (*memalign)(void **ptr, size_t align, size_t size,
+ const char *file, int line, const char *func);
+ void (*free)(void *ptr, const char *file, int line, const char *func);
+} mm_t;
+
+
+/*
+ * memory block
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to allocated blocks */
+ mrp_list_hook_t more; /* with the same backtrace */
+ const char *file; /* file of immediate caller */
+ int line; /* line of immediate caller */
+ const char *func; /* name of immediate caller */
+ size_t size; /* requested size */
+ void *bt[]; /* for accessing backtrace */
+} memblk_t;
+
+
+
+static mm_t __mm = { /* allocator state */
+ .hdrsize = MRP_ALIGN(MRP_OFFSET(memblk_t, bt[DEFAULT_DEPTH]),
+ MRP_MM_ALIGN),
+ .depth = DEFAULT_DEPTH,
+ .poison = 0xdeadbeef,
+};
+
+
+static const char *get_config_key(const char *config, const char *key)
+{
+ const char *beg;
+ int len;
+
+ if (config != NULL) {
+ len = strlen(key);
+
+ beg = config;
+ while (beg != NULL) {
+ beg = strstr(beg, key);
+
+ if (beg != NULL) {
+ if ((beg == config || beg[-1] == ':') &&
+ (beg[len] == '=' || beg[len] == ':' || beg[len] == '\0'))
+ return (beg[len] == '=' ? beg + len + 1 : "");
+ else
+ beg++;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+static int32_t get_config_int32(const char *cfg, const char *key,
+ int32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = get_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+static uint32_t get_config_uint32(const char *cfg, const char *key,
+ uint32_t defval)
+{
+ const char *v;
+ char *end;
+ int i;
+
+ v = get_config_key(cfg, key);
+
+ if (v != NULL) {
+ if (*v) {
+ i = strtol(v, &end, 10);
+
+ if (end && (!*end || *end == ':'))
+ return i;
+ }
+ }
+
+ return defval;
+}
+
+
+static int get_config_bool(const char *config, const char *key, int defval)
+{
+ const char *v;
+
+ v = get_config_key(config, key);
+
+ if (v != NULL) {
+ if (*v) {
+
+ if ((!strncasecmp(v, "false", 5) && (v[5] == ':' || !v[5])) ||
+ (!strncasecmp(v, "true" , 4) && (v[4] == ':' || !v[4])))
+ return (v[0] == 't' || v[0] == 'T');
+ }
+ else if (*v == '\0')
+ return TRUE;
+ }
+
+ return defval;
+}
+
+
+static int get_config_string(const char *cfg, const char *key,
+ const char *defval, char *buf, size_t size)
+{
+ const char *v;
+ char *end;
+ int len;
+
+ v = get_config_key(cfg, key);
+
+ if (v == NULL)
+ v = defval;
+
+ end = strchr(v, ':');
+
+ if (end != NULL)
+ len = end - v;
+ else
+ len = strlen(v);
+
+ len = snprintf(buf, size, "%*.*s", len, len, v);
+
+ if (len >= (int)size - 1)
+ buf[size - 1] = '\0';
+
+ return len;
+}
+
+
+
+MRP_INIT_AT(101) static void setup(void)
+{
+ char *config = getenv(MRP_MM_CONFIG_ENVVAR);
+
+ mrp_list_init(&__mm.blocks);
+
+ __mm.depth = get_config_int32(config, "depth", DEFAULT_DEPTH);
+
+ if (__mm.depth > MAX_DEPTH)
+ __mm.depth = MAX_DEPTH;
+
+ __mm.hdrsize = MRP_ALIGN(MRP_OFFSET(memblk_t, bt[__mm.depth]),
+ MRP_MM_ALIGN);
+
+ __mm.cur_blocks = 0;
+ __mm.max_blocks = 0;
+ __mm.cur_alloc = 0;
+ __mm.max_alloc = 0;
+
+ __mm.poison = get_config_uint32(config, "poison", 0xdeadbeef);
+ __mm.chunk_size = sysconf(_SC_PAGESIZE) * 2;
+
+ if (config == NULL || !get_config_bool(config, "debug", FALSE))
+ mrp_mm_config(MRP_MM_PASSTHRU);
+ else
+ mrp_mm_config(MRP_MM_DEBUG);
+}
+
+
+static void __attribute__((destructor)) cleanup(void)
+{
+ if (__mm.mode == MRP_MM_DEBUG) {
+ mrp_mm_dump(stdout);
+ /*mrp_mm_check(stdout);*/
+ }
+}
+
+
+
+int32_t mrp_mm_config_int32(const char *key, int32_t defval)
+{
+ return get_config_int32(getenv(MRP_MM_CONFIG_ENVVAR), key, defval);
+}
+
+
+uint32_t mrp_mm_config_uint32(const char *key, uint32_t defval)
+{
+ return get_config_uint32(getenv(MRP_MM_CONFIG_ENVVAR), key, defval);
+}
+
+
+int mrp_mm_config_bool(const char *key, int defval)
+{
+ return get_config_bool(getenv(MRP_MM_CONFIG_ENVVAR), key, defval);
+}
+
+
+int mrp_mm_config_string(const char *key, const char *defval,
+ char *buf, size_t size)
+{
+ return get_config_string(getenv(MRP_MM_CONFIG_ENVVAR), key, defval,
+ buf, size);
+}
+
+
+/*
+ * memblk handling
+ */
+
+static memblk_t *memblk_alloc(size_t size, const char *file, int line,
+ const char *func, void **bt)
+{
+ memblk_t *blk;
+
+ if (MRP_UNLIKELY(size == 0))
+ blk = NULL;
+ else {
+ if ((blk = malloc(__mm.hdrsize + size)) != NULL) {
+ mrp_list_init(&blk->hook);
+ mrp_list_init(&blk->more);
+ mrp_list_append(&__mm.blocks, &blk->hook);
+
+ blk->file = file;
+ blk->line = line;
+ blk->func = func;
+ blk->size = size;
+
+ memcpy(blk->bt, bt, __mm.depth * sizeof(*bt));
+
+ __mm.cur_blocks++;
+ __mm.cur_alloc += size;
+
+ __mm.max_blocks = MRP_MAX(__mm.max_blocks, __mm.cur_blocks);
+ __mm.max_alloc = MRP_MAX(__mm.max_alloc , __mm.cur_alloc);
+ }
+ }
+
+ return blk;
+}
+
+
+static void memblk_free(memblk_t *blk, const char *file, int line,
+ const char *func, void **bt)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+ MRP_UNUSED(bt);
+
+ if (blk != NULL) {
+ mrp_list_delete(&blk->hook);
+
+ __mm.cur_blocks--;
+ __mm.cur_alloc -= blk->size;
+
+ if (__mm.poison != 0)
+ memset(&blk->bt[__mm.depth], __mm.poison, blk->size);
+
+ free(blk);
+ }
+}
+
+
+static memblk_t *memblk_resize(memblk_t *blk, size_t size, const char *file,
+ int line, const char *func, void **bt)
+{
+ memblk_t *resized;
+
+ if (blk != NULL) {
+ mrp_list_delete(&blk->hook);
+
+ if (size != 0) {
+ resized = realloc(blk, __mm.hdrsize + size);
+
+ if (resized != NULL) {
+ mrp_list_init(&resized->hook);
+
+ mrp_list_append(&__mm.blocks, &resized->hook);
+
+ __mm.cur_alloc -= resized->size;
+ __mm.cur_alloc += size;
+ __mm.max_alloc = MRP_MAX(__mm.max_alloc, __mm.cur_alloc);
+
+ resized->file = file;
+ resized->line = line;
+ resized->func = func;
+
+ memcpy(resized->bt, bt, __mm.depth * sizeof(*bt));
+
+ resized->size = size;
+ }
+ else
+ mrp_list_append(&__mm.blocks, &blk->hook);
+ }
+ else {
+ resized = NULL;
+ memblk_free(blk, file, line, func, bt);
+ }
+
+ return resized;
+ }
+ else
+ return memblk_alloc(size, file, line, func, bt);
+}
+
+
+static inline void *memblk_to_ptr(memblk_t *blk)
+{
+ if (blk != NULL)
+ return (void *)&blk->bt[__mm.depth];
+ else
+ return NULL;
+}
+
+
+static inline memblk_t *ptr_to_memblk(void *ptr)
+{
+ /*
+ * XXX Hmm... maybe we should also add pre- and post-sentinels
+ * and check them here to have minimal protection/detection of
+ * trivial buffer overflow bugs when running in debug mode.
+ */
+
+ if (ptr != NULL)
+ return ptr - MRP_OFFSET(memblk_t, bt[__mm.depth]);
+ else
+ return NULL;
+}
+
+
+/*
+ * debugging allocator
+ */
+
+static inline int __mm_backtrace(void **bt, size_t size)
+{
+ int i, n;
+
+ n = backtrace(bt, (int)size);
+ for (i = n; i < (int)size; i++)
+ bt[i] = NULL;
+
+ return n;
+}
+
+
+static void *__mm_alloc(size_t size, const char *file, int line,
+ const char *func)
+{
+ memblk_t *blk;
+ void *bt[__mm.depth + 1];
+
+ __mm_backtrace(bt, MRP_ARRAY_SIZE(bt));
+ blk = memblk_alloc(size, file, line, func, bt + 1);
+
+ return memblk_to_ptr(blk);
+}
+
+
+static void *__mm_realloc(void *ptr, size_t size, const char *file,
+ int line, const char *func)
+{
+ memblk_t *blk;
+ void *bt[__mm.depth + 1];
+
+ __mm_backtrace(bt, MRP_ARRAY_SIZE(bt));
+ blk = ptr_to_memblk(ptr);
+
+ if (blk != NULL)
+ blk = memblk_resize(blk, size, file, line, func, bt + 1);
+ else
+ blk = memblk_alloc(size, file, line, func, bt + 1);
+
+ return memblk_to_ptr(blk);
+}
+
+
+static int __mm_memalign(void **ptr, size_t align, size_t size,
+ const char *file, int line, const char *func)
+{
+ MRP_UNUSED(align);
+ MRP_UNUSED(size);
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ *ptr = NULL;
+ errno = ENOSYS;
+
+ mrp_log_error("XXX %s not implemented!!!", __FUNCTION__);
+ return -1;
+}
+
+
+static void __mm_free(void *ptr, const char *file, int line,
+ const char *func)
+{
+ memblk_t *blk;
+ void *bt[__mm.depth + 1];
+
+ if (ptr != NULL) {
+ __mm_backtrace(bt, MRP_ARRAY_SIZE(bt));
+ blk = ptr_to_memblk(ptr);
+
+ if (blk != NULL)
+ memblk_free(blk, file, line, func, bt + 1);
+ }
+}
+
+
+/*
+ * passthru allocator
+ */
+
+static void *__passthru_alloc(size_t size, const char *file, int line,
+ const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ if (MRP_UNLIKELY(size == 0))
+ return NULL;
+ else
+ return malloc(size);
+}
+
+
+static void *__passthru_realloc(void *ptr, size_t size, const char *file,
+ int line, const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ return realloc(ptr, size);
+}
+
+
+static int __passthru_memalign(void **ptr, size_t align, size_t size,
+ const char *file, int line, const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ return posix_memalign(ptr, align, size);
+}
+
+
+static void __passthru_free(void *ptr, const char *file, int line,
+ const char *func)
+{
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ free(ptr);
+}
+
+
+/*
+ * common public interface - uses either passthru or debugging
+ */
+
+void *mrp_mm_alloc(size_t size, const char *file, int line, const char *func)
+{
+ return __mm.alloc(size, file, line, func);
+}
+
+
+void *mrp_mm_realloc(void *ptr, size_t size, const char *file, int line,
+ const char *func)
+{
+ return __mm.realloc(ptr, size, file, line, func);
+}
+
+
+char *mrp_mm_strdup(const char *s, const char *file, int line, const char *func)
+{
+ char *p;
+ size_t size;
+
+ if (s != NULL) {
+ size = strlen(s) + 1;
+ p = mrp_mm_alloc(size, file, line, func);
+
+ if (p != NULL)
+ strcpy(p, s);
+ }
+ else
+ p = NULL;
+
+ return p;
+}
+
+
+int mrp_mm_memalign(void **ptr, size_t align, size_t size, const char *file,
+ int line, const char *func)
+{
+ return __mm.memalign(ptr, align, size, file, line, func);
+}
+
+
+void mrp_mm_free(void *ptr, const char *file, int line, const char *func)
+{
+ return __mm.free(ptr, file, line, func);
+}
+
+
+int mrp_mm_config(mrp_mm_type_t type)
+{
+ if (__mm.cur_blocks != 0)
+ return FALSE;
+
+ switch (type) {
+ case MRP_MM_PASSTHRU:
+ __mm.alloc = __passthru_alloc;
+ __mm.realloc = __passthru_realloc;
+ __mm.memalign = __passthru_memalign;
+ __mm.free = __passthru_free;
+ __mm.mode = MRP_MM_PASSTHRU;
+ return TRUE;
+
+ case MRP_MM_DEBUG:
+ __mm.alloc = __mm_alloc;
+ __mm.realloc = __mm_realloc;
+ __mm.memalign = __mm_memalign;
+ __mm.free = __mm_free;
+ __mm.mode = MRP_MM_DEBUG;
+ return TRUE;
+
+ default:
+ mrp_log_error("Invalid memory allocator type 0x%x requested.", type);
+ return FALSE;
+ }
+}
+
+
+#define NBUCKET 1024
+
+static int btcmp(void **bt1, void **bt2)
+{
+ ptrdiff_t diff;
+ int i;
+
+ for (i = 0; i < __mm.depth; i++) {
+ diff = bt1[i] - bt2[i];
+
+ if (diff < 0)
+ return -1;
+ else if (diff > 0)
+ return +1;
+ }
+
+ return 0;
+}
+
+
+static uint32_t blkhash(memblk_t *blk)
+{
+ uint32_t h;
+ int i;
+
+ h = 0;
+ for (i = 0; i < __mm.depth; i++)
+ h ^= (blk->bt[i] - NULL) & 0xffffffffUL;
+
+ return h % NBUCKET;
+}
+
+
+static memblk_t *blkfind(mrp_list_hook_t *buckets, memblk_t *blk)
+{
+ uint32_t h = blkhash(blk);
+ mrp_list_hook_t *head = buckets + h;
+ mrp_list_hook_t *p, *n;
+ memblk_t *b;
+
+ mrp_list_foreach(head, p, n) {
+ b = mrp_list_entry(p, typeof(*b), hook);
+ if (!btcmp(&b->bt[0], &blk->bt[0]))
+ return b;
+ }
+
+ return NULL;
+}
+
+
+static void collect_blocks(mrp_list_hook_t *buckets)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *head, *blk;
+ uint32_t h;
+ int i;
+
+ for (i = 0; i < NBUCKET; i++)
+ mrp_list_init(buckets + i);
+
+ mrp_list_foreach(&__mm.blocks, p, n) {
+ blk = mrp_list_entry(p, typeof(*blk), hook);
+
+ mrp_list_init(&blk->more);
+ head = blkfind(buckets, blk);
+
+ if (head != NULL) {
+ mrp_list_append(&head->more, &blk->more);
+ head->size += blk->size;
+ }
+ else {
+ h = blkhash(blk);
+ mrp_list_delete(&blk->hook);
+ mrp_list_append(buckets + h, &blk->hook);
+ }
+ }
+}
+
+
+static uint32_t group_usage(memblk_t *head, int exclude_head)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *blk;
+ uint32_t total;
+
+ total = exclude_head ? 0 : head->size;
+ mrp_list_foreach(&head->more, p, n) {
+ blk = mrp_list_entry(p, typeof(*blk), more);
+ total += blk->size;
+ }
+
+ return total;
+}
+
+
+static void dump_group(FILE *fp, memblk_t *head)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *blk;
+ char **syms, *sym;
+ uint32_t total;
+ int nblk, i;
+
+ fprintf(fp, "Allocations with call stack fingerprint:\n");
+ syms = backtrace_symbols(head->bt, __mm.depth);
+ for (i = 0; i < __mm.depth && head->bt[i]; i++) {
+ sym = syms && syms[i] ? strrchr(syms[i], '/') : NULL;
+ fprintf(fp, " %p (%s)\n", head->bt[i], sym ? sym + 1 : "<unknown>");
+ }
+ free(syms);
+
+ total = head->size - group_usage(head, TRUE);
+ nblk = 1;
+
+ fprintf(fp, " %lu bytes at %p\n", (unsigned long)total,
+ memblk_to_ptr(head));
+
+ mrp_list_foreach(&head->more, p, n) {
+ blk = mrp_list_entry(p, typeof(*blk), more);
+
+ total += blk->size;
+ nblk++;
+
+ fprintf(fp, " %zd bytes at %p\n", blk->size, memblk_to_ptr(blk));
+ }
+
+ if (nblk > 1)
+ fprintf(fp, " total %lu bytes in %d blocks\n",
+ (unsigned long)total, nblk);
+}
+
+
+static void sort_blocks(mrp_list_hook_t *buckets, mrp_list_hook_t *sorted)
+{
+ mrp_list_hook_t *bp, *bn, *sp, *sn;
+ memblk_t *head, *entry, *next;
+ int i;
+
+ mrp_list_init(sorted);
+
+ for (i = 0; i < NBUCKET; i++) {
+ mrp_list_foreach(buckets + i, bp, bn) {
+ head = mrp_list_entry(bp, typeof(*head), hook);
+
+ next = NULL;
+ mrp_list_foreach(sorted, sp, sn) {
+ entry = mrp_list_entry(sp, typeof(*entry), hook);
+
+ if (head->size <= entry->size) {
+ next = entry;
+ break;
+ }
+ }
+
+ mrp_list_delete(&head->hook);
+
+ if (next != NULL)
+ mrp_list_insert_before(&next->hook, &head->hook);
+ else
+ mrp_list_append(sorted, &head->hook);
+ }
+ }
+}
+
+
+static void dump_blocks(FILE *fp, mrp_list_hook_t *sorted)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *head;
+
+ mrp_list_foreach(sorted, p, n) {
+ head = mrp_list_entry(p, typeof(*head), hook);
+ dump_group(fp, head);
+ }
+}
+
+
+static void relink_blocks(mrp_list_hook_t *sorted)
+{
+ mrp_list_hook_t *p, *n;
+ memblk_t *head;
+ uint32_t rest;
+
+ mrp_list_foreach(sorted, p, n) {
+ head = mrp_list_entry(p, typeof(*head), hook);
+ mrp_list_delete(&head->hook);
+ mrp_list_append(&__mm.blocks, &head->hook);
+
+ rest = group_usage(head, TRUE);
+ head->size -= rest;
+ }
+}
+
+
+void mrp_mm_dump(FILE *fp)
+{
+ mrp_list_hook_t buckets[NBUCKET];
+ mrp_list_hook_t sorted;
+
+ mrp_list_init(&sorted);
+
+ collect_blocks(buckets);
+ sort_blocks(buckets, &sorted);
+ dump_blocks(fp, &sorted);
+ relink_blocks(&sorted);
+
+ fprintf(fp, "Max: %llu bytes (%.2f M, %.2f G), %ld blocks\n",
+ (unsigned long long)__mm.max_alloc,
+ 1.0 * __mm.max_alloc / (1024 * 1024),
+ 1.0 * __mm.max_alloc / (1024 * 1024 * 1024),
+ (unsigned long)__mm.max_blocks);
+ fprintf(fp, "Current: %llu bytes (%.2f M, %.2f G) in %ld blocks.\n",
+ (unsigned long long)__mm.cur_alloc,
+ 1.0 * __mm.cur_alloc / (1024 * 1024),
+ 1.0 * __mm.cur_alloc / (1024 * 1024 * 1024),
+ (unsigned long)__mm.cur_blocks);
+}
+
+
+void mrp_mm_check(FILE *fp)
+{
+ mrp_mm_dump(fp);
+}
+
+
+
+
+
+/*
+ * object pool interface
+ */
+
+typedef unsigned int mask_t;
+
+#define W sizeof(mask_t)
+#define B (W * 8)
+#define MASK_BYTES (sizeof(mask_t))
+#define MASK_BITS (MASK_BYTES * 8)
+#define MASK_EMPTY ((mask_t)-1)
+#define MASK_FULL ((mask_t) 0)
+
+typedef struct pool_chunk_s pool_chunk_t;
+
+static int pool_calc_sizes(mrp_objpool_t *pool);
+static int pool_grow(mrp_objpool_t *pool, int nobj);
+static int pool_shrink(mrp_objpool_t *pool, int nobj);
+static pool_chunk_t *chunk_alloc(int nperchunk);
+static void chunk_free(pool_chunk_t *chunk);
+static inline int chunk_empty(pool_chunk_t *chunk);
+static void pool_foreach_object(mrp_objpool_t *pool,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data);
+static void chunk_foreach_object(pool_chunk_t *chunk,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data);
+
+
+/*
+ * an object pool
+ */
+
+struct mrp_objpool_s {
+ char *name; /* verbose pool name */
+ size_t limit; /* max. number of objects */
+ size_t objsize; /* size of a single object */
+ size_t prealloc; /* preallocate this many */
+ size_t nobj; /* currently allocated */
+ int (*setup)(void *); /* object setup callback */
+ void (*cleanup)(void *); /* object cleanup callback */
+ uint32_t flags; /* pool flags */
+ int poison; /* poisoning pattern */
+
+ size_t nperchunk; /* objects per chunk */
+ size_t dataidx; /* data */
+ mrp_list_hook_t space; /* chunk with frees slots */
+ size_t nspace; /* number of such chunks */
+ mrp_list_hook_t full; /* fully allocated chunks */
+ size_t nfull; /* number of such chunks */
+};
+
+
+/*
+ * a chunk of memory allocated to an object pool
+ */
+
+struct pool_chunk_s {
+ mrp_objpool_t *pool; /* pool we're alloced to */
+ mrp_list_hook_t hook; /* hook to chunk list */
+ mask_t cache; /* cache bits */
+ mask_t used[]; /* allocation mask */
+};
+
+
+
+mrp_objpool_t *mrp_objpool_create(mrp_objpool_config_t *cfg)
+{
+ mrp_objpool_t *pool;
+
+ if ((pool = mrp_allocz(sizeof(*pool))) != NULL) {
+ if ((pool->name = mrp_strdup(cfg->name)) == NULL)
+ goto fail;
+
+ pool->limit = cfg->limit;
+ pool->objsize = MRP_MAX(cfg->objsize, (size_t)MRP_MM_OBJSIZE_MIN);
+ pool->prealloc = cfg->prealloc;
+ pool->setup = cfg->setup;
+ pool->cleanup = cfg->cleanup;
+ pool->flags = cfg->flags;
+ pool->poison = cfg->poison;
+
+ mrp_list_init(&pool->space);
+ mrp_list_init(&pool->full);
+ pool->nspace = 0;
+ pool->nfull = 0;
+
+ if (!pool_calc_sizes(pool))
+ goto fail;
+
+ if (!mrp_objpool_grow(pool, pool->prealloc))
+ goto fail;
+
+ mrp_debug("pool <%s> created, with %zd/%zd objects.", pool->name,
+ pool->prealloc, pool->limit);
+
+ return pool;
+ }
+
+
+ fail:
+ mrp_objpool_destroy(pool);
+ return NULL;
+}
+
+
+static void free_object(void *obj, void *user_data)
+{
+ mrp_objpool_t *pool = (mrp_objpool_t *)user_data;
+
+ printf("Releasing unfreed object %p from pool <%s>.\n", obj, pool->name);
+ mrp_objpool_free(obj);
+}
+
+
+void mrp_objpool_destroy(mrp_objpool_t *pool)
+{
+ if (pool != NULL) {
+ if (pool->cleanup != NULL)
+ pool_foreach_object(pool, free_object, pool);
+
+ mrp_free(pool->name);
+ mrp_free(pool);
+ }
+}
+
+
+void *mrp_objpool_alloc(mrp_objpool_t *pool)
+{
+ pool_chunk_t *chunk;
+ void *obj;
+ unsigned int cidx, uidx, sidx;
+
+ if (pool->limit && pool->nobj >= pool->limit)
+ return NULL;
+
+ if (mrp_list_empty(&pool->space)) {
+ if (!pool_grow(pool, 1))
+ return NULL;
+ }
+
+ chunk = mrp_list_entry(pool->space.next, pool_chunk_t, hook);
+ cidx = ffs(chunk->cache);
+
+ if (!cidx) {
+ mrp_log_error("object pool bug: no free slots in cache mask.");
+ return NULL;
+ }
+ else
+ cidx--;
+
+ uidx = ffs(chunk->used[cidx]);
+
+ if (!uidx) {
+ mrp_log_error("object pool bug: no free slots in used mask.");
+ return NULL;
+ }
+ else
+ uidx--;
+
+ sidx = cidx * MASK_BITS + uidx;
+ obj = ((void *)&chunk->used[pool->dataidx]) + (sidx * pool->objsize);
+
+ mrp_debug("%p: %u/%u: %u, offs %zd\n", obj, cidx, uidx, sidx,
+ sidx * pool->objsize);
+
+ chunk->used[cidx] &= ~(1 << uidx);
+
+ if (chunk->used[cidx] == MASK_FULL) {
+ chunk->cache &= ~(1 << cidx);
+
+ if (chunk->cache == MASK_FULL) { /* chunk exhausted */
+ mrp_list_delete(&chunk->hook);
+ pool->nspace--;
+ mrp_list_append(&pool->full, &chunk->hook);
+ pool->nfull++;
+ }
+ }
+
+ if (pool->setup == NULL || pool->setup(obj)) {
+ pool->nobj++;
+ return obj;
+ }
+ else {
+ mrp_objpool_free(obj);
+ return NULL;
+ }
+}
+
+
+void mrp_objpool_free(void *obj)
+{
+ pool_chunk_t *chunk;
+ mrp_objpool_t *pool;
+ unsigned int cidx, uidx, sidx;
+ mask_t cache, used;
+ void *base;
+
+ if (obj == NULL)
+ return;
+
+ chunk = (pool_chunk_t *)(((ptrdiff_t)obj) & ~(__mm.chunk_size - 1));
+ pool = chunk->pool;
+
+ base = (void *)&chunk->used[pool->dataidx];
+ sidx = (obj - base) / pool->objsize;
+ cidx = sidx / MASK_BITS;
+ uidx = sidx & (MASK_BITS - 1);
+
+ mrp_debug("%p: %u/%u: %u, offs %zd\n", obj, cidx, uidx, sidx,
+ sidx * pool->objsize);
+
+ cache = chunk->cache;
+ used = chunk->used[cidx];
+
+ if (used & (1 << uidx)) {
+ mrp_log_error("Trying to free unallocated object %p of pool <%s>.",
+ obj, pool->name);
+ return;
+ }
+
+ if (pool->cleanup != NULL)
+ pool->cleanup(obj);
+
+ if (pool->flags & MRP_OBJPOOL_FLAG_POISON)
+ memset(obj, pool->poison, pool->objsize);
+
+ chunk->used[cidx] |= (1 << uidx);
+ chunk->cache |= (1 << cidx);
+
+ if (cache == MASK_FULL) { /* chunk was full */
+ mrp_list_delete(&chunk->hook);
+ pool->nfull--;
+ mrp_list_append(&pool->space, &chunk->hook);
+ pool->nspace++;
+ }
+
+ pool->nobj--;
+}
+
+
+int mrp_objpool_grow(mrp_objpool_t *pool, int nobj)
+{
+ int nchunk = (nobj + pool->nperchunk - 1) / pool->nperchunk;
+
+ return pool_grow(pool, nchunk) == nchunk;
+}
+
+
+int mrp_objpool_shrink(mrp_objpool_t *pool, int nobj)
+{
+ int nchunk = (nobj + pool->nperchunk - 1) / pool->nperchunk;
+
+ return pool_shrink(pool, nchunk) == nchunk;
+}
+
+
+static int pool_calc_sizes(mrp_objpool_t *pool)
+{
+ size_t S, C, Hf, Hv, P;
+ size_t n, T;
+
+ if (!pool->objsize)
+ return FALSE;
+
+ pool->objsize = MRP_ALIGN(pool->objsize, MRP_MM_ALIGN);
+
+ /*
+ * Pool chunks consist of an administrative header followed by object
+ * slots each of which can be either claimed/allocated or free. The
+ * header contains a back pointer to the pool, a hook to one of the
+ * chunk lists and a two-level bit-mask for slot allocation status.
+ * The two-level mask consists of a 32-bit cache word and actual slot
+ * status words. The nth bit of the cache word caches whether there are
+ * any free among the nth - (n + 31)th slots. The slot status words keep
+ * the status of the actual slots. To find a free slot we find the idx
+ * of the 1st word with a free slot from the cache and then the free
+ * slot index in that word. To be able to use FFS we use inverted bit
+ * semantics (0=allocated, 1=free) and we populate the words starting
+ * at the LSB.
+ *
+ * Here we calculate how many objects we'll be able to squeeze into a
+ * single pool chunk and how many mask bits we'll need to administer
+ * the status of these. To do this we use the following equations:
+ *
+ * 1) Hf + Hv + n * S = C
+ * 2) Hv = W + (n + B - 1) / B * W
+ * where
+ * C: chunk size
+ * S: object size (aligned to our minimum alignment)
+ * Hf: header size, fixed part
+ * Hv: Header size, variable part (allocation mask)
+ * n: number of objects / chunk
+ * W: bitmask word size in bytes
+ * B: bitmask word size in bits
+ *
+ * Solving the equations for n gives us
+ * n = (B*C - B*Hf - W*(2*B - 1)) / (B*S + W)
+ *
+ * If any, the only non-obvious thing below is that instead of trying
+ * to express padding as part of the equation system (which seems to be
+ * way beyond my abilities in math nowadays), we initally assume no
+ * padding then check and compensate for it in the end if necessary.
+ */
+
+ Hf = sizeof(pool_chunk_t);
+ C = __mm.chunk_size;
+ P = 0;
+
+ S = MRP_ALIGN(pool->objsize, MRP_MM_ALIGN);
+ n = (B * C - B * Hf - W * (2*B - 1)) / (B * S + W);
+ Hv = W + W * (n + B - 1) / B;
+
+ P = (Hf + Hv) % sizeof(void *);
+ if (P != 0) {
+ P = sizeof(void *) - P;
+
+ if (Hv + Hf + P + n * S > C) {
+ n--;
+ Hv = W + W * (n + B - 1) / B;
+ }
+ }
+
+ T = Hf + Hv + P + n * S;
+
+ if (T > C) {
+ mrp_log_error("Could not size pool '%s' properly.", pool->name);
+ return FALSE;
+ }
+
+ pool->nperchunk = n;
+ pool->dataidx = (n + B - 1) / B;
+
+ if (pool->limit && (pool->limit % pool->nperchunk) != 0)
+ pool->limit += (pool->nperchunk - (pool->limit % pool->nperchunk));
+
+ return TRUE;
+}
+
+
+static int pool_grow(mrp_objpool_t *pool, int nchunk)
+{
+ pool_chunk_t *chunk;
+ int cnt;
+
+ for (cnt = 0; cnt < nchunk; cnt++) {
+ chunk = chunk_alloc(pool->nperchunk);
+
+ if (chunk != NULL) {
+ chunk->pool = pool;
+ mrp_list_append(&pool->space, &chunk->hook);
+ pool->nspace++;
+ }
+ else
+ break;
+ }
+
+ return cnt;
+}
+
+
+static int pool_shrink(mrp_objpool_t *pool, int nchunk)
+{
+ mrp_list_hook_t *p, *n;
+ pool_chunk_t *chunk;
+ int cnt;
+
+ cnt = 0;
+ mrp_list_foreach(&pool->space, p, n) {
+ chunk = mrp_list_entry(p, pool_chunk_t, hook);
+
+ if (chunk_empty(chunk)) {
+ mrp_list_delete(&chunk->hook);
+ chunk_free(chunk);
+ pool->nspace--;
+ cnt++;
+ }
+
+ if (cnt >= nchunk)
+ break;
+ }
+
+ return cnt;
+}
+
+
+static void pool_foreach_object(mrp_objpool_t *pool,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data)
+{
+ mrp_list_hook_t *p, *n;
+ pool_chunk_t *chunk;
+
+ mrp_list_foreach(&pool->full, p, n) {
+ chunk = mrp_list_entry(p, pool_chunk_t, hook);
+ chunk_foreach_object(chunk, cb, user_data);
+ }
+
+ mrp_list_foreach(&pool->space, p, n) {
+ chunk = mrp_list_entry(p, pool_chunk_t, hook);
+ chunk_foreach_object(chunk, cb, user_data);
+ }
+}
+
+
+static void chunk_foreach_object(pool_chunk_t *chunk,
+ void (*cb)(void *obj, void *user_data),
+ void *user_data)
+{
+ mrp_objpool_t *pool = chunk->pool;
+ void *obj;
+ int sidx, cidx, uidx;
+ mask_t used;
+
+ sidx = 0;
+ while (sidx < (int)pool->nperchunk) {
+ cidx = sidx / MASK_BITS;
+ uidx = sidx & (MASK_BITS - 1);
+ used = chunk->used[cidx];
+
+ if (!(used & (1 << uidx))) {
+ obj = ((void *)&chunk->used[pool->dataidx]) + (sidx*pool->objsize);
+ cb(obj, user_data);
+ sidx++;
+ }
+ else {
+ if (used == MASK_EMPTY)
+ sidx = (sidx + MASK_BITS) & ~(MASK_BITS - 1);
+ else
+ sidx++;
+ }
+ }
+}
+
+
+static inline int chunk_empty(pool_chunk_t *chunk)
+{
+ mask_t mask;
+ int i, n;
+
+ if (chunk->cache != (MASK_EMPTY & 0xffff))
+ return FALSE;
+ else {
+ for (n = chunk->pool->nperchunk, i = 0; n > 0; n -= MASK_BITS, i++) {
+ if (n >= (int)MASK_BITS)
+ mask = MASK_EMPTY;
+ else
+ mask = (1 << n) - 1;
+
+ if ((chunk->used[i] & mask) != mask)
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+}
+
+
+static void chunk_init(pool_chunk_t *chunk, int nperchunk)
+{
+ int nword, left, i;
+
+ mrp_list_init(&chunk->hook);
+
+ left = nperchunk;
+ nword = (nperchunk + MASK_BITS - 1) / MASK_BITS;
+
+ /*
+ * initialize allocation bitmask
+ *
+ * Note that every bit that corresponds to a non-allocatable slots
+ * we mark as reserved. This keeps both the allocation and freeing
+ * code paths simpler.
+ */
+
+ chunk->cache = (1 << nword) - 1;
+
+ for (i = 0; left > 0; i++) {
+ if (left >= (int)MASK_BITS)
+ chunk->used[i] = MASK_EMPTY;
+ else
+ chunk->used[i] = (((mask_t)1) << left) - 1;
+
+ left -= B;
+ }
+}
+
+
+static pool_chunk_t *chunk_alloc(int nperchunk)
+{
+ void *chunk;
+ int err;
+
+ err = posix_memalign(&chunk, __mm.chunk_size, __mm.chunk_size);
+
+ if (err == 0) {
+ memset(chunk, 0, __mm.chunk_size);
+ chunk_init((pool_chunk_t *)chunk, nperchunk);
+ }
+ else
+ chunk = NULL;
+
+ return chunk;
+}
+
+
+static void chunk_free(pool_chunk_t *chunk)
+{
+ free(chunk);
+}
+
+
+#if 0
+static void test_sizes(void)
+{
+ size_t S, C, Hf, Hv, P;
+ size_t i, n, T, Hv1, n1, T1;
+ int ok, ok1;
+
+ Hf = sizeof(pool_chunk_t);
+ C = __mm.chunk_size;
+ P = 0;
+
+ printf(" C: %zd\n", C);
+ printf("Hf: %zd\n", Hf);
+
+ for (i = 1; i < __mm.chunk_size / 8; i++) {
+ S = MRP_ALIGN(i, MRP_MM_ALIGN);
+ n = (B * C - B * Hf - W * (2*B - 1)) / (B * S + W);
+ Hv = W + W * (n + B - 1) / B;
+
+ P = (Hf + Hv) % sizeof(void *);
+ if (P != 0) {
+ P = sizeof(void *) - P;
+
+ if (Hv + Hf + P + n * S > C) {
+ n--;
+ Hv = W + W * (n + B - 1) / B;
+ }
+ }
+
+ T = Hf + Hv + P + n * S;
+ ok = T <= C;
+
+ n1 = n + 1;
+ Hv1 = W + W * (n1 + B - 1) / B;
+ T1 = Hf + Hv1 + P + n1 * S;
+ ok1 = T1 > C;
+
+ printf(" i = %zd: %zd * %zd + %zd (%zd: %s, %zd: %s)\n", i, n, S, P,
+ T , ok ? "OK" : "FAIL",
+ T1, ok1 ? "OK" : "FAIL");
+ {
+ size_t hs, us;
+
+ us = sizeof(uint32_t);
+ hs = (Hf + Hv + P) / us;
+
+ printf(" H+P: %zd (%zd * %zd = %zd)\n", Hf + Hv + P,
+ hs, us, hs * us);
+
+ if (((Hf + Hv + P) % sizeof(void *)) != 0) {
+ printf("Padding error!\n");
+ exit(1);
+ }
+ }
+
+ if (!ok || !ok1)
+ exit(1);
+ }
+}
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MM_H__
+#define __MURPHY_MM_H__
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_MM_ALIGN 8 /* object alignment */
+#define MRP_MM_CONFIG_ENVVAR "__MURPHY_MM_CONFIG"
+
+#define mrp_alloc(size) mrp_mm_alloc((size), __LOC__)
+#define mrp_free(ptr) mrp_mm_free((ptr), __LOC__)
+#define mrp_strdup(s) mrp_mm_strdup((s), __LOC__)
+#define mrp_datadup(ptr, size) ({ \
+ typeof(ptr) _ptr = mrp_alloc(size); \
+ \
+ if (_ptr != NULL) \
+ memcpy(_ptr, ptr, size); \
+ \
+ _ptr; \
+ })
+
+#define mrp_allocz(size) ({ \
+ void *_ptr; \
+ \
+ if ((_ptr = mrp_mm_alloc(size, __LOC__)) != NULL) \
+ memset(_ptr, 0, size); \
+ \
+ _ptr;})
+
+#define mrp_calloc(n, size) mrp_allocz((n) * (size))
+
+#define mrp_reallocz(ptr, o, n) ({ \
+ typeof(ptr) _ptr; \
+ typeof(o) _o; \
+ typeof(n) _n = (n); \
+ size_t _size = sizeof(*_ptr) * (_n); \
+ \
+ if ((ptr) != NULL) \
+ _o = o; \
+ else \
+ _o = 0; \
+ \
+ _ptr = (typeof(ptr))mrp_mm_realloc(ptr, _size, __LOC__); \
+ if (_ptr != NULL || _n == 0) { \
+ if ((unsigned)(_n) > (unsigned)(_o)) \
+ memset(_ptr + (_o), 0, \
+ ((_n)-(_o)) * sizeof(*_ptr)); \
+ ptr = _ptr; \
+ } \
+ _ptr; })
+
+#define mrp_realloc(ptr, size) ({ \
+ typeof(ptr) _ptr; \
+ size_t _size = size; \
+ \
+ _ptr = (typeof(ptr))mrp_mm_realloc(ptr, _size, __LOC__); \
+ if (_ptr != NULL || _size == 0) \
+ ptr = _ptr; \
+ \
+ _ptr; })
+
+#define mrp_clear(obj) memset((obj), 0, sizeof(*(obj)))
+
+
+#define mrp_alloc_array(type, n) ((type *)mrp_alloc(sizeof(type) * (n)))
+#define mrp_allocz_array(type, n) ((type *)mrp_allocz(sizeof(type) * (n)))
+
+typedef enum {
+ MRP_MM_PASSTHRU = 0, /* passthru allocator */
+ MRP_MM_DEFAULT = MRP_MM_PASSTHRU, /* default is passthru */
+ MRP_MM_DEBUG /* debugging allocator */
+} mrp_mm_type_t;
+
+
+int mrp_mm_config(mrp_mm_type_t type);
+void mrp_mm_check(FILE *fp);
+void mrp_mm_dump(FILE *fp);
+
+void *mrp_mm_alloc(size_t size, const char *file, int line, const char *func);
+void *mrp_mm_realloc(void *ptr, size_t size, const char *file, int line,
+ const char *func);
+char *mrp_mm_strdup(const char *s, const char *file, int line,
+ const char *func);
+int mrp_mm_memalign(void **ptr, size_t align, size_t size, const char *file,
+ int line, const char *func);
+void mrp_mm_free(void *ptr, const char *file, int line, const char *func);
+
+
+
+
+#define MRP_MM_OBJSIZE_MIN 16 /* minimum object size */
+
+enum {
+ MRP_OBJPOOL_FLAG_POISON = 0x1, /* poison free'd objects */
+};
+
+
+/*
+ * object pool configuration
+ */
+
+typedef struct {
+ char *name; /* verbose pool name */
+ size_t limit; /* max. number of objects */
+ size_t objsize; /* size of a single object */
+ size_t prealloc; /* preallocate this many */
+ int (*setup)(void *); /* object setup callback */
+ void (*cleanup)(void *); /* object cleanup callback */
+ uint32_t flags; /* MRP_OBJPOOL_FLAG_* */
+ int poison; /* poisoning pattern */
+} mrp_objpool_config_t;
+
+
+typedef struct mrp_objpool_s mrp_objpool_t;
+
+/** Create a new object pool with the given configuration. */
+mrp_objpool_t *mrp_objpool_create(mrp_objpool_config_t *cfg);
+
+/** Destroy an object pool, freeing all associated memory. */
+void mrp_objpool_destroy(mrp_objpool_t *pool);
+
+/** Allocate a new object from the pool. */
+void *mrp_objpool_alloc(mrp_objpool_t *pool);
+
+/** Free the given object. */
+void mrp_objpool_free(void *obj);
+
+/** Grow @pool to accomodate @nobj new objects. */
+int mrp_objpool_grow(mrp_objpool_t *pool, int nobj);
+
+/** Shrink @pool by @nobj new objects, if possible. */
+int mrp_objpool_shrink(mrp_objpool_t *pool, int nobj);
+
+/** Get the value of a boolean key from the configuration. */
+int mrp_mm_config_bool(const char *key, int defval);
+
+/** Get the value of a boolean key from the configuration. */
+int32_t mrp_mm_config_int32(const char *key, int32_t defval);
+
+/** Get the value of a boolean key from the configuration. */
+uint32_t mrp_mm_config_uint32(const char *key, uint32_t defval);
+
+/** Get the value of a string key from the configuration. */
+int mrp_mm_config_string(const char *key, const char *defval,
+ char *buf, size_t size);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MM_H__ */
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <arpa/inet.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/msg.h>
+
+#define NDIRECT_TYPE 256 /* directly indexed types */
+
+static mrp_data_descr_t **direct_types; /* directly indexed types */
+static mrp_data_descr_t **other_types; /* linearly searched types */
+static int nother_type;
+
+
+static inline void destroy_field(mrp_msg_field_t *f)
+{
+ uint32_t i;
+
+ if (f != NULL) {
+ mrp_list_delete(&f->hook);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ mrp_free(f->str);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ mrp_free(f->blb);
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ if ((f->type & ~MRP_MSG_FIELD_ARRAY) == MRP_MSG_FIELD_STRING) {
+ for (i = 0; i < f->size[0]; i++) {
+ mrp_free(f->astr[i]);
+ }
+ }
+
+ mrp_free(f->aany);
+ }
+ break;
+ }
+
+ mrp_free(f);
+ }
+}
+
+
+static inline mrp_msg_field_t *create_field(uint16_t tag, va_list *ap)
+{
+ mrp_msg_field_t *f;
+ uint16_t type, base;
+ uint32_t size;
+ void *blb;
+
+ type = va_arg(*ap, uint32_t);
+
+#define CREATE(_f, _tag, _type, _fldtype, _fld, _last, _errlbl) do { \
+ \
+ (_f) = mrp_allocz(MRP_OFFSET(typeof(*_f), _last) + \
+ sizeof(_f->_last)); \
+ \
+ if ((_f) != NULL) { \
+ mrp_list_init(&(_f)->hook); \
+ (_f)->tag = _tag; \
+ (_f)->type = _type; \
+ (_f)->_fld = va_arg(*ap, _fldtype); \
+ } \
+ else { \
+ goto _errlbl; \
+ } \
+ } while (0)
+
+#define CREATE_ARRAY(_f, _tag, _type, _fld, _fldtype, _errlbl) do { \
+ uint16_t _base; \
+ uint32_t _i; \
+ \
+ (_f) = mrp_allocz(MRP_OFFSET(typeof(*_f), size[1])); \
+ \
+ if ((_f) != NULL) { \
+ mrp_list_init(&(_f)->hook); \
+ (_f)->tag = _tag; \
+ (_f)->type = _type | MRP_MSG_FIELD_ARRAY; \
+ _base = _type & ~MRP_MSG_FIELD_ARRAY; \
+ \
+ _f->size[0] = va_arg(*ap, uint32_t); \
+ _f->_fld = mrp_allocz_array(typeof(*_f->_fld), \
+ _f->size[0]); \
+ \
+ if (_f->_fld == NULL && _f->size[0] != 0) \
+ goto _errlbl; \
+ else \
+ memcpy(_f->_fld, va_arg(*ap, typeof(_f->_fld)), \
+ _f->size[0] * sizeof(_f->_fld[0])); \
+ \
+ if (_base == MRP_MSG_FIELD_STRING) { \
+ for (_i = 0; _i < _f->size[0]; _i++) { \
+ _f->astr[_i] = mrp_strdup(_f->astr[_i]); \
+ if (_f->astr[_i] == NULL) \
+ goto _errlbl; \
+ } \
+ } \
+ } \
+ else \
+ goto _errlbl; \
+ } while (0)
+
+ f = NULL;
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ CREATE(f, tag, type, char *, str, str, fail);
+ f->str = mrp_strdup(f->str);
+ if (f->str == NULL)
+ goto fail;
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ CREATE(f, tag, type, int, bln, bln, fail);
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ CREATE(f, tag, type, unsigned int, u8, u8, fail);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ CREATE(f, tag, type, signed int, s8, s8, fail);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ CREATE(f, tag, type, unsigned int, u16, u16, fail);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ CREATE(f, tag, type, signed int, s16, s16, fail);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ CREATE(f, tag, type, unsigned int, u32, u32, fail);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ CREATE(f, tag, type, signed int, s32, s32, fail);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ CREATE(f, tag, type, uint64_t, u64, u64, fail);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ CREATE(f, tag, type, int64_t, s64, s64, fail);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ CREATE(f, tag, type, double, dbl, dbl, fail);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ size = va_arg(*ap, uint32_t);
+ CREATE(f, tag, type, void *, blb, size[0], fail);
+
+ blb = f->blb;
+ f->size[0] = size;
+ f->blb = mrp_allocz(size);
+
+ if (f->blb != NULL) {
+ memcpy(f->blb, blb, size);
+ f->size[0] = size;
+ }
+ else
+ goto fail;
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ base = type & ~MRP_MSG_FIELD_ARRAY;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ CREATE_ARRAY(f, tag, base, astr, char *, fail);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ CREATE_ARRAY(f, tag, base, abln, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ CREATE_ARRAY(f, tag, base, au8, unsigned int, fail);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ CREATE_ARRAY(f, tag, base, as8, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ CREATE_ARRAY(f, tag, base, au16, unsigned int, fail);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ CREATE_ARRAY(f, tag, base, as16, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ CREATE_ARRAY(f, tag, base, au32, unsigned int, fail);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ CREATE_ARRAY(f, tag, base, as32, int, fail);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ CREATE_ARRAY(f, tag, base, au64, unsigned long long, fail);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ CREATE_ARRAY(f, tag, base, as64, long long, fail);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ CREATE_ARRAY(f, tag, base, adbl, double, fail);
+ break;
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+ break;
+ }
+
+ return f;
+
+ fail:
+ destroy_field(f);
+ return NULL;
+
+#undef CREATE
+#undef CREATE_ARRAY
+}
+
+
+static void msg_destroy(mrp_msg_t *msg)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_msg_field_t *f;
+
+ if (msg != NULL) {
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+ destroy_field(f);
+ }
+
+ mrp_free(msg);
+ }
+}
+
+
+mrp_msg_t *mrp_msg_createv(uint16_t tag, va_list ap)
+{
+ mrp_msg_t *msg;
+ mrp_msg_field_t *f;
+ va_list aq;
+
+ va_copy(aq, ap);
+ if ((msg = mrp_allocz(sizeof(*msg))) != NULL) {
+ mrp_list_init(&msg->fields);
+ mrp_refcnt_init(&msg->refcnt);
+
+ while (tag != MRP_MSG_FIELD_INVALID) {
+ f = create_field(tag, &aq);
+
+ if (f != NULL) {
+ mrp_list_append(&msg->fields, &f->hook);
+ msg->nfield++;
+ }
+ else {
+ msg_destroy(msg);
+ msg = NULL;
+ goto out;
+ }
+ tag = va_arg(aq, uint32_t);
+ }
+ }
+ out:
+ va_end(aq);
+
+ return msg;
+}
+
+
+mrp_msg_t *mrp_msg_create(uint16_t tag, ...)
+{
+ mrp_msg_t *msg;
+ va_list ap;
+
+ va_start(ap, tag);
+ msg = mrp_msg_createv(tag, ap);
+ va_end(ap);
+
+ return msg;
+}
+
+
+mrp_msg_t *mrp_msg_ref(mrp_msg_t *msg)
+{
+ return mrp_ref_obj(msg, refcnt);
+}
+
+
+void mrp_msg_unref(mrp_msg_t *msg)
+{
+ if (mrp_unref_obj(msg, refcnt))
+ msg_destroy(msg);
+}
+
+
+int mrp_msg_append(mrp_msg_t *msg, uint16_t tag, ...)
+{
+ mrp_msg_field_t *f;
+ va_list ap;
+
+ va_start(ap, tag);
+ f = create_field(tag, &ap);
+ va_end(ap);
+
+ if (f != NULL) {
+ mrp_list_append(&msg->fields, &f->hook);
+ msg->nfield++;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_msg_prepend(mrp_msg_t *msg, uint16_t tag, ...)
+{
+ mrp_msg_field_t *f;
+ va_list ap;
+
+ va_start(ap, tag);
+ f = create_field(tag, &ap);
+ va_end(ap);
+
+ if (f != NULL) {
+ mrp_list_prepend(&msg->fields, &f->hook);
+ msg->nfield++;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_msg_set(mrp_msg_t *msg, uint16_t tag, ...)
+{
+ mrp_msg_field_t *of, *nf;
+ va_list ap;
+
+ of = mrp_msg_find(msg, tag);
+
+ if (of != NULL) {
+ va_start(ap, tag);
+ nf = create_field(tag, &ap);
+ va_end(ap);
+
+ if (nf != NULL) {
+ mrp_list_append(&of->hook, &nf->hook);
+ destroy_field(of);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int mrp_msg_iterate(mrp_msg_t *msg, void **it, uint16_t *tagp, uint16_t *typep,
+ mrp_msg_value_t *valp, size_t *sizep)
+{
+ mrp_list_hook_t *p = *(mrp_list_hook_t **)it;
+ mrp_msg_field_t *f;
+
+ if (p == NULL)
+ p = msg->fields.next;
+
+ if (p == &msg->fields)
+ return FALSE;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ *tagp = f->tag;
+ *typep = f->type;
+
+ switch (f->type) {
+#define HANDLE_TYPE(type, member) \
+ case MRP_MSG_FIELD_##type: \
+ valp->member = f->member; \
+ if (sizep != NULL) \
+ *sizep = sizeof(typeof(f->member)); \
+ break
+
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8);
+ HANDLE_TYPE(SINT8 , s8);
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+
+ case MRP_MSG_FIELD_STRING:
+ valp->str = f->str;
+ if (sizep != NULL)
+ *sizep = strlen(f->str);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ valp->blb = f->blb;
+ if (sizep != NULL)
+ *sizep = (size_t)f->size[0];
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ valp->aany = f->aany;
+ if (sizep != NULL)
+ *sizep = f->size[0];
+ }
+ else
+ return FALSE;
+#undef HANDLE_TYPE
+ }
+
+ *it = p->next;
+
+ return TRUE;
+}
+
+
+mrp_msg_field_t *mrp_msg_find(mrp_msg_t *msg, uint16_t tag)
+{
+ mrp_msg_field_t *f;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+ if (f->tag == tag)
+ return f;
+ }
+
+ return NULL;
+}
+
+
+int mrp_msg_get(mrp_msg_t *msg, ...)
+{
+#define HANDLE_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp = va_arg(ap, typeof(valp)); \
+ valp->_member = f->_member; \
+ break
+
+#define HANDLE_ARRAY(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ cntp = va_arg(ap, typeof(cntp)); \
+ valp = va_arg(ap, typeof(valp)); \
+ *cntp = f->size[0]; \
+ valp->_member = f->_member; \
+ break
+
+
+ mrp_msg_field_t *f;
+ mrp_msg_value_t *valp;
+ uint32_t *cntp;
+ mrp_list_hook_t *start, *p;
+ uint16_t tag, type;
+ int found;
+ va_list ap;
+
+ va_start(ap, msg);
+
+ /*
+ * Okay... this might look a bit weird at first sight. This is
+ * mostly because we don't use the standard list iterating macros
+ * in the inner loop. There is a good reason for that: we want to
+ * minimise the number of times we scan the message which is just
+ * a linked list of fields. We do this by arranging the nested
+ * loops below in such a way that if the order of fields to fetch
+ * in the argument list matches the order of fields in the message
+ * we end up running the outer and inner loops in a 'phase lock'.
+ * So if the caller fetches the fields in the correct order we end
+ * up scanning the message at most once but only up to the last
+ * field to fetch.
+ */
+
+ start = msg->fields.next;
+ found = FALSE;
+
+ while ((tag = va_arg(ap, unsigned int)) != MRP_MSG_FIELD_INVALID) {
+ type = va_arg(ap, unsigned int);
+ found = FALSE;
+
+ for (p = start; p != start->prev; p = p->next) {
+ if (p == &msg->fields)
+ continue;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (f->tag != tag)
+ continue;
+
+ if (f->type != type)
+ goto out;
+
+ switch (type) {
+ HANDLE_TYPE(STRING, str);
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8 );
+ HANDLE_TYPE(SINT8 , s8 );
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+ default:
+ if (type & MRP_MSG_FIELD_ARRAY) {
+ switch (type & ~MRP_MSG_FIELD_ARRAY) {
+ HANDLE_ARRAY(STRING, astr);
+ HANDLE_ARRAY(BOOL , abln);
+ HANDLE_ARRAY(UINT8 , au8 );
+ HANDLE_ARRAY(SINT8 , as8 );
+ HANDLE_ARRAY(UINT16, au16);
+ HANDLE_ARRAY(SINT16, as16);
+ HANDLE_ARRAY(UINT32, au32);
+ HANDLE_ARRAY(SINT32, as32);
+ HANDLE_ARRAY(UINT64, au64);
+ HANDLE_ARRAY(SINT64, as64);
+ HANDLE_ARRAY(DOUBLE, adbl);
+ default:
+ goto out;
+
+ }
+ }
+ else
+ goto out;
+ }
+
+ start = p->next;
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ break;
+ }
+
+ out:
+ va_end(ap);
+
+ return found;
+
+#undef HANDLE_TYPE
+#undef HANDLE_ARRAY
+
+}
+
+
+int mrp_msg_iterate_get(mrp_msg_t *msg, void **it, ...)
+{
+#define HANDLE_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp = va_arg(ap, typeof(valp)); \
+ valp->_member = f->_member; \
+ break
+
+#define HANDLE_ARRAY(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ cntp = va_arg(ap, typeof(cntp)); \
+ valp = va_arg(ap, typeof(valp)); \
+ *cntp = f->size[0]; \
+ valp->_member = f->_member; \
+ break
+
+#define ANY_TYPE(_type, _member) \
+ case MRP_MSG_FIELD_##_type: \
+ valp->_member = f->_member; \
+ break
+
+ mrp_msg_field_t *f;
+ mrp_msg_value_t *valp;
+ uint32_t *cntp;
+ mrp_list_hook_t *start, *p;
+ uint16_t tag, type, *typep;
+ int found;
+ va_list ap;
+
+ va_start(ap, it);
+
+ /*
+ * Okay... this might look a bit weird at first sight. This is
+ * mostly because we don't use the standard list iterating macros
+ * in the inner loop. There is a good reason for that: we want to
+ * minimise the number of times we scan the message which is just
+ * a linked list of fields. We do this by arranging the nested
+ * loops below in such a way that if the order of fields to fetch
+ * in the argument list matches the order of fields in the message
+ * we end up running the outer and inner loops in a 'phase lock'.
+ * So if the caller fetches the fields in the correct order we end
+ * up scanning the message at most once but only up to the last
+ * field to fetch.
+ */
+
+ start = (*it) ? (mrp_list_hook_t *)*it : msg->fields.next;
+ found = FALSE;
+
+ while ((tag = va_arg(ap, unsigned int)) != MRP_MSG_FIELD_INVALID) {
+ type = va_arg(ap, unsigned int);
+ found = FALSE;
+
+ if (type == MRP_MSG_FIELD_ANY) {
+ typep = va_arg(ap, uint16_t *);
+ valp = va_arg(ap, mrp_msg_value_t *);
+ }
+ else {
+ typep = NULL;
+ valp = NULL;
+ }
+
+ for (p = start; p != start->prev; p = p->next) {
+ if (p == &msg->fields)
+ continue;
+
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (f->tag != tag)
+ continue;
+
+ if (type == MRP_MSG_FIELD_ANY) {
+ *typep = f->type;
+ switch (f->type) {
+ ANY_TYPE(STRING, str);
+ ANY_TYPE(BOOL , bln);
+ ANY_TYPE(UINT8 , u8 );
+ ANY_TYPE(SINT8 , s8 );
+ ANY_TYPE(UINT16, u16);
+ ANY_TYPE(SINT16, s16);
+ ANY_TYPE(UINT32, u32);
+ ANY_TYPE(SINT32, s32);
+ ANY_TYPE(UINT64, u64);
+ ANY_TYPE(SINT64, s64);
+ ANY_TYPE(DOUBLE, dbl);
+ default:
+ mrp_log_error("XXX TODO: currently cannot fetch array "
+ "message fields with iterators.");
+ }
+
+ goto next;
+ }
+
+ if (f->type != type)
+ goto out;
+
+ switch (type) {
+ HANDLE_TYPE(STRING, str);
+ HANDLE_TYPE(BOOL , bln);
+ HANDLE_TYPE(UINT8 , u8 );
+ HANDLE_TYPE(SINT8 , s8 );
+ HANDLE_TYPE(UINT16, u16);
+ HANDLE_TYPE(SINT16, s16);
+ HANDLE_TYPE(UINT32, u32);
+ HANDLE_TYPE(SINT32, s32);
+ HANDLE_TYPE(UINT64, u64);
+ HANDLE_TYPE(SINT64, s64);
+ HANDLE_TYPE(DOUBLE, dbl);
+ default:
+ if (type & MRP_MSG_FIELD_ARRAY) {
+ switch (type & ~MRP_MSG_FIELD_ARRAY) {
+ HANDLE_ARRAY(STRING, astr);
+ HANDLE_ARRAY(BOOL , abln);
+ HANDLE_ARRAY(UINT8 , au8 );
+ HANDLE_ARRAY(SINT8 , as8 );
+ HANDLE_ARRAY(UINT16, au16);
+ HANDLE_ARRAY(SINT16, as16);
+ HANDLE_ARRAY(UINT32, au32);
+ HANDLE_ARRAY(SINT32, as32);
+ HANDLE_ARRAY(UINT64, au64);
+ HANDLE_ARRAY(SINT64, as64);
+ HANDLE_ARRAY(DOUBLE, adbl);
+ default:
+ goto out;
+
+ }
+ }
+ else
+ goto out;
+ }
+
+ next:
+ start = p->next;
+ found = TRUE;
+ break;
+ }
+
+ if (!found)
+ break;
+ }
+
+ out:
+ va_end(ap);
+
+ if (found)
+ *it = start;
+
+ return found;
+
+#undef HANDLE_TYPE
+#undef HANDLE_ARRAY
+
+}
+
+
+static const char *field_type_name(uint16_t type)
+{
+#define BASIC(t, n) [MRP_MSG_FIELD_##t] = n
+#define ARRAY(t, n) [MRP_MSG_FIELD_##t] = "array of "n"s"
+ static const char *basic[] = {
+ BASIC(STRING, "string" ),
+ BASIC(BOOL , "boolean"),
+ BASIC(UINT8 , "uint8" ),
+ BASIC(SINT8 , "sint8" ),
+ BASIC(UINT16, "uint16" ),
+ BASIC(SINT16, "sint16" ),
+ BASIC(UINT32, "uint32" ),
+ BASIC(SINT32, "sint32" ),
+ BASIC(UINT64, "uint64" ),
+ BASIC(SINT64, "sint64" ),
+ BASIC(DOUBLE, "double" ),
+ BASIC(BLOB , "blob" )
+ };
+
+ static const char *array[] = {
+ ARRAY(STRING, "string" ),
+ ARRAY(BOOL , "boolean"),
+ ARRAY(UINT8 , "uint8" ),
+ ARRAY(SINT8 , "sint8" ),
+ ARRAY(UINT16, "uint16" ),
+ ARRAY(SINT16, "sint16" ),
+ ARRAY(UINT32, "uint32" ),
+ ARRAY(SINT32, "sint32" ),
+ ARRAY(UINT64, "uint64" ),
+ ARRAY(SINT64, "sint64" ),
+ ARRAY(DOUBLE, "double" ),
+ ARRAY(BLOB , "blob" )
+ };
+#undef BASIC
+#undef ARRAY
+
+ uint16_t base;
+
+ if (MRP_MSG_FIELD_INVALID < type && type <= MRP_MSG_FIELD_MAX)
+ return basic[type];
+ else {
+ if (type & MRP_MSG_FIELD_ARRAY) {
+ base = type & ~MRP_MSG_FIELD_ARRAY;
+
+ if (MRP_MSG_FIELD_INVALID < base && base <= MRP_MSG_FIELD_MAX)
+ return array[base];
+ }
+ }
+
+ return "unknown type";
+}
+
+
+int mrp_msg_dump(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_field_t *f;
+ mrp_list_hook_t *p, *n;
+ int l;
+ uint32_t i;
+ uint16_t base;
+ const char *tname;
+
+ if (msg == NULL)
+ return fprintf(fp, "{\n <no message>\n}\n");
+
+ l = fprintf(fp, "{\n");
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ l += fprintf(fp, " 0x%x ", f->tag);
+
+#define DUMP(_indent, _fmt, _typename, _val) \
+ l += fprintf(fp, "%*.*s= <%s> "_fmt"\n", _indent, _indent, "", \
+ _typename, _val)
+
+ tname = field_type_name(f->type);
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(0, "'%s'", tname, f->str);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(0, "%s", tname, f->bln ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(0, "%u", tname, f->u8);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(0, "%d", tname, f->s8);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(0, "%u", tname, f->u16);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(0, "%d", tname, f->s16);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(0, "%u", tname, f->u32);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(0, "%d", tname, f->s32);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(0, "%Lu", tname, (long long unsigned)f->u64);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(0, "%Ld", tname, (long long signed)f->s64);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(0, "%f", tname, f->dbl);
+ break;
+ case MRP_MSG_FIELD_BLOB: {
+ char *p;
+ uint32_t i;
+
+ fprintf(fp, "= <%s> <%u bytes, ", tname, f->size[0]);
+
+ for (i = 0, p = f->blb; i < f->size[0]; i++, p++) {
+ if (isprint(*p) && *p != '\n' && *p != '\t' && *p != '\r')
+ fprintf(fp, "%c", *p);
+ else
+ fprintf(fp, ".");
+ }
+ fprintf(fp, ">\n");
+ }
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ base = f->type & ~MRP_MSG_FIELD_ARRAY;
+ tname = field_type_name(base);
+
+ fprintf(fp, "\n");
+ for (i = 0; i < f->size[0]; i++) {
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(8, "'%s'", tname, f->astr[i]);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(8, "%s", tname, f->abln[i] ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(8, "%u", tname, f->au8[i]);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(8, "%d", tname, f->as8[i]);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(8, "%u", tname, f->au16[i]);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(8, "%d", tname, f->as16[i]);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(8, "%u", tname, f->au32[i]);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(8, "%d", tname, f->as32[i]);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(8, "%Lu", tname,
+ (unsigned long long)f->au64[i]);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(8, "%Ld", tname,
+ (long long)f->as64[i]);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(8, "%f", tname, f->adbl[i]);
+ break;
+ default:
+ fprintf(fp, "%*.*s= <%s>\n", 8, 8, "", tname);
+ break;
+ }
+ }
+ }
+ else
+ fprintf(fp, "= <%s>\n", tname);
+ }
+ }
+ l += fprintf(fp, "}\n");
+
+ return l;
+#undef DUMP
+}
+
+
+#define MSG_MIN_CHUNK 32
+
+ssize_t mrp_msg_default_encode(mrp_msg_t *msg, void **bufp)
+{
+ mrp_msg_field_t *f;
+ mrp_list_hook_t *p, *n;
+ mrp_msgbuf_t mb;
+ uint32_t len, asize, i;
+ uint16_t type;
+ size_t size;
+
+ size = msg->nfield * (2 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ if (mrp_msgbuf_write(&mb, size)) {
+ MRP_MSGBUF_PUSH(&mb, htobe16(MRP_MSG_TAG_DEFAULT), 1, nomem);
+ MRP_MSGBUF_PUSH(&mb, htobe16(msg->nfield), 1, nomem);
+
+ mrp_list_foreach(&msg->fields, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->tag) , 1, nomem);
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->type), 1, nomem);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(f->str) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, f->str, len, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->bln ? TRUE : FALSE), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, f->u8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, f->s8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->u16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->s16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->u32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->s32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->u64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->s64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, f->dbl, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ len = f->size[0];
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, f->blb, len, 1, nomem);
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ type = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ asize = f->size[0];
+ MRP_MSGBUF_PUSH(&mb, htobe32(asize), 1, nomem);
+
+ for (i = 0; i < asize; i++) {
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(f->astr[i]) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, f->astr[i], len,
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->abln[i]?TRUE:FALSE),
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, f->au8[i], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, f->as8[i], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->au16[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->as16[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->au32[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(f->as32[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->au64[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(f->as64[i]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, f->adbl[i], 1, nomem);
+ break;
+
+ default:
+ goto invalid_type;
+ }
+ }
+ }
+ else {
+ invalid_type:
+ errno = EINVAL;
+ mrp_msgbuf_cancel(&mb);
+ nomem:
+ *bufp = NULL;
+ return -1;
+ }
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ return mb.p - mb.buf;
+}
+
+
+mrp_msg_t *mrp_msg_default_decode(void *buf, size_t size)
+{
+ mrp_msg_t *msg;
+ mrp_msgbuf_t mb;
+ mrp_msg_value_t v;
+ void *value;
+ uint16_t nfield, tag, type, base;
+ uint32_t len, n, i, j;
+
+ msg = mrp_msg_create_empty();
+
+ if (msg == NULL)
+ return NULL;
+
+ mrp_msgbuf_read(&mb, buf, size);
+
+ nfield = be16toh(MRP_MSGBUF_PULL(&mb, typeof(nfield), 1, nodata));
+
+ for (i = 0; i < nfield; i++) {
+ tag = be16toh(MRP_MSGBUF_PULL(&mb, typeof(tag) , 1, nodata));
+ type = be16toh(MRP_MSGBUF_PULL(&mb, typeof(type), 1, nodata));
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ if (len > 0)
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ value = "";
+ if (!mrp_msg_append(msg, tag, type, value))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v.bln = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.bln))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v.u8 = MRP_MSGBUF_PULL(&mb, typeof(v.u8), 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, v.u8))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v.s8 = MRP_MSGBUF_PULL(&mb, typeof(v.s8), 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, v.s8))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v.u16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.u16), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.u16))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v.s16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.s16), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.s16))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v.u32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.u32), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.u32))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v.s32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.s32), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.s32))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v.u64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.u64), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.u64))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v.s64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.s64), 1, nodata));
+ if (!mrp_msg_append(msg, tag, type, v.s64))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v.dbl = MRP_MSGBUF_PULL(&mb, typeof(v.dbl), 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, v.dbl))
+ goto fail;
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ if (!mrp_msg_append(msg, tag, type, len, value))
+ goto fail;
+ break;
+
+ default:
+ if (!(type & MRP_MSG_FIELD_ARRAY)) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ base = type & ~MRP_MSG_FIELD_ARRAY;
+ n = be32toh(MRP_MSGBUF_PULL(&mb, typeof(n), 1, nodata));
+ {
+ char *astr[n];
+ bool abln[n];
+ uint8_t au8 [n];
+ int8_t as8 [n];
+ uint16_t au16[n];
+ int16_t as16[n];
+ uint32_t au32[n];
+ int32_t as32[n];
+ uint64_t au64[n];
+ int64_t as64[n];
+ double adbl[n];
+
+ for (j = 0; j < n; j++) {
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len),
+ 1, nodata));
+ if (len > 0)
+ astr[j] = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ astr[j] = "";
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ abln[j] = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1,
+ nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ au8[j] = MRP_MSGBUF_PULL(&mb, typeof(v.u8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ as8[j] = MRP_MSGBUF_PULL(&mb, typeof(v.s8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ au16[j] = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.u16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ as16[j] = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v.s16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ au32[j] = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.u32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ as32[j] = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v.s32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ au64[j] = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.u64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ as64[j] = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v.s64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ adbl[j] = MRP_MSGBUF_PULL(&mb, typeof(v.dbl),
+ 1, nodata);
+ break;
+
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+
+#define HANDLE_TYPE(_type, _var) \
+ case _type: \
+ if (!mrp_msg_append(msg, tag, \
+ MRP_MSG_FIELD_ARRAY |_type, \
+ n, _var)) \
+ goto fail; \
+ break
+
+ switch (base) {
+ HANDLE_TYPE(MRP_MSG_FIELD_STRING, astr);
+ HANDLE_TYPE(MRP_MSG_FIELD_BOOL , abln);
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT8 , au8 );
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT8 , as8 );
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT16, au16);
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT16, as16);
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT32, au32);
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT32, as32);
+ HANDLE_TYPE(MRP_MSG_FIELD_UINT64, au64);
+ HANDLE_TYPE(MRP_MSG_FIELD_SINT64, as64);
+ HANDLE_TYPE(MRP_MSG_FIELD_DOUBLE, adbl);
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+#undef HANDLE_TYPE
+ }
+ }
+ }
+
+ return msg;
+
+
+ fail:
+ nodata:
+ mrp_msg_unref(msg);
+ return NULL;
+}
+
+
+static int guarded_array_size(void *data, mrp_data_member_t *array)
+{
+#define MAX_ITEMS (32 * 1024)
+ uint16_t base;
+ void *value, *guard;
+ size_t size;
+ int cnt;
+
+ if (array->type & MRP_MSG_FIELD_ARRAY) {
+ base = array->type & ~MRP_MSG_FIELD_ARRAY;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size = sizeof(array->str); break;
+ case MRP_MSG_FIELD_BOOL: size = sizeof(array->bln); break;
+ case MRP_MSG_FIELD_UINT8: size = sizeof(array->u8); break;
+ case MRP_MSG_FIELD_SINT8: size = sizeof(array->s8); break;
+ case MRP_MSG_FIELD_UINT16: size = sizeof(array->u16); break;
+ case MRP_MSG_FIELD_SINT16: size = sizeof(array->s16); break;
+ case MRP_MSG_FIELD_UINT32: size = sizeof(array->u32); break;
+ case MRP_MSG_FIELD_SINT32: size = sizeof(array->s32); break;
+ case MRP_MSG_FIELD_UINT64: size = sizeof(array->u64); break;
+ case MRP_MSG_FIELD_SINT64: size = sizeof(array->s64); break;
+ case MRP_MSG_FIELD_DOUBLE: size = sizeof(array->dbl); break;
+ default: return -1;
+ }
+
+ guard = &array->str;
+ value = *(void **)(data + array->offs);
+ for (cnt = 0; cnt < MAX_ITEMS; cnt++, value += size) {
+ if (!memcmp(value, guard, size))
+ return cnt + 1;
+ }
+ }
+
+ return -1;
+#undef MAX_ITEMS
+}
+
+
+static int counted_array_size(void *data, mrp_data_member_t *cnt)
+{
+ void *val = data + cnt->offs;
+
+ switch (cnt->type) {
+ case MRP_MSG_FIELD_UINT8: return (int)*(uint8_t *)val;
+ case MRP_MSG_FIELD_SINT8: return (int)*( int8_t *)val;
+ case MRP_MSG_FIELD_UINT16: return (int)*(uint16_t *)val;
+ case MRP_MSG_FIELD_SINT16: return (int)*( int16_t *)val;
+ case MRP_MSG_FIELD_UINT32: return (int)*(uint32_t *)val;
+ case MRP_MSG_FIELD_SINT32: return (int)*( int32_t *)val;
+ }
+
+ return -1;
+}
+
+
+static int get_array_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ mrp_data_member_t *arr;
+
+ if (0 < idx && idx < type->nfield) {
+ arr = type->fields + idx;
+
+ if (arr->type & MRP_MSG_FIELD_ARRAY) {
+ if (arr->guard)
+ return guarded_array_size(data, arr);
+ else {
+ if ((int)arr->u32 < type->nfield)
+ return counted_array_size(data, type->fields + arr->u32);
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+int mrp_data_get_array_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ return get_array_size(data, type, idx);
+}
+
+
+static int get_blob_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ mrp_data_member_t *blb, *cnt;
+ void *val;
+
+ if (0 < idx && idx < type->nfield) {
+ blb = type->fields + idx;
+
+ if ((int)blb->u32 < type->nfield) {
+ cnt = type->fields + blb->u32;
+ val = data + cnt->offs;
+
+ switch (cnt->type) {
+ case MRP_MSG_FIELD_UINT8: return (int)*(uint8_t *)val;
+ case MRP_MSG_FIELD_SINT8: return (int)*( int8_t *)val;
+ case MRP_MSG_FIELD_UINT16: return (int)*(uint16_t *)val;
+ case MRP_MSG_FIELD_SINT16: return (int)*( int16_t *)val;
+ case MRP_MSG_FIELD_UINT32: return (int)*(uint32_t *)val;
+ case MRP_MSG_FIELD_SINT32: return (int)*( int32_t *)val;
+ }
+ }
+ }
+
+ return -1;
+}
+
+
+int mrp_data_get_blob_size(void *data, mrp_data_descr_t *type, int idx)
+{
+ return get_blob_size(data, type, idx);
+}
+
+
+static int check_and_init_array_descr(mrp_data_descr_t *type, int idx)
+{
+ mrp_data_member_t *array, *cnt, *m;
+ int i;
+
+ array = type->fields + idx;
+
+ if (!array->guard) {
+ cnt = NULL;
+
+ for (i = 0, m = type->fields; i < type->nfield; i++, m++) {
+ if (m->offs == array->u32) {
+ cnt = m;
+ break;
+ }
+ }
+
+ if (cnt == NULL || cnt >= array)
+ return FALSE;
+
+ if (cnt->type < MRP_MSG_FIELD_UINT8 || cnt->type > MRP_MSG_FIELD_SINT32)
+ return FALSE;
+
+ array->u32 = i;
+
+ return TRUE;
+ }
+ else {
+ return TRUE;
+ }
+}
+
+
+int mrp_msg_register_type(mrp_data_descr_t *type)
+{
+ mrp_data_member_t *f;
+ int idx, i;
+
+ if (direct_types == NULL) {
+ direct_types = mrp_allocz_array(typeof(*direct_types), NDIRECT_TYPE);
+
+ if (direct_types == NULL)
+ return FALSE;
+ }
+
+ if (type->tag == MRP_MSG_TAG_DEFAULT) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ mrp_list_init(&type->allocated);
+
+ /* enumerate fields, check arrays, collect extra allocations */
+ for (i = 0, f = type->fields; i < type->nfield; i++, f++) {
+ f->tag = (uint16_t)i + 1;
+
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ if (!check_and_init_array_descr(type, i))
+ return FALSE;
+
+ mrp_list_append(&type->allocated, &f->hook);
+ }
+ else {
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ case MRP_MSG_FIELD_BLOB:
+ mrp_list_append(&type->allocated, &f->hook);
+ }
+ }
+ }
+
+ if (type->tag <= NDIRECT_TYPE) {
+ idx = type->tag - 1;
+
+ if (direct_types[idx] == NULL)
+ direct_types[idx] = type;
+ else
+ return FALSE;
+ }
+ else {
+ if (mrp_reallocz(other_types, nother_type, nother_type + 1) != NULL)
+ other_types[nother_type++] = type;
+ else
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+mrp_data_descr_t *mrp_msg_find_type(uint16_t tag)
+{
+ int i;
+
+ if (MRP_UNLIKELY(tag == MRP_MSG_TAG_DEFAULT))
+ return NULL;
+
+ if (tag <= NDIRECT_TYPE)
+ return direct_types[tag - 1];
+ else {
+ for (i = 0; i < nother_type; i++) {
+ if (other_types[i] != NULL && other_types[i]->tag == tag)
+ return other_types[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+static __attribute__((destructor)) void cleanup_types(void)
+{
+ mrp_free(direct_types);
+ mrp_free(other_types);
+ nother_type = 0;
+}
+
+
+size_t mrp_data_encode(void **bufp, void *data, mrp_data_descr_t *descr,
+ size_t reserve)
+{
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ uint16_t type;
+ mrp_msgbuf_t mb;
+ mrp_msg_value_t *v;
+ uint32_t len, asize, blblen, j;
+ int i, cnt;
+ size_t size;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+ size = reserve + nfield * (2 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ if (mrp_msgbuf_write(&mb, size)) {
+ if (reserve)
+ mrp_msgbuf_reserve(&mb, reserve, 1);
+
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->tag) , 1, nomem);
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(v->str) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->str, len, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->bln ? TRUE : FALSE), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, v->u8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, v->s8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->u16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->s16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->u32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->s32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->u64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->s64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, v->dbl, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ blblen = (uint32_t)get_blob_size(data, descr, i);
+
+ if (blblen == (uint32_t)-1)
+ goto invalid_type;
+
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->u32), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->blb, blblen, 1, nomem);
+ break;
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ type = f->type & ~(MRP_MSG_FIELD_ARRAY);
+ cnt = get_array_size(data, descr, i);
+
+ if (cnt < 0)
+ goto invalid_type;
+
+ asize = (uint32_t)cnt;
+ MRP_MSGBUF_PUSH(&mb, htobe32(asize), 1, nomem);
+
+ for (j = 0; j < asize; j++) {
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(v->astr[j]) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->astr[j], len,
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->abln[j]?TRUE:FALSE),
+ 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, v->au8[j], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, v->as8[j], 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->au16[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->as16[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->au32[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->as32[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->au64[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->as64[j]), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, v->adbl[j], 1, nomem);
+ break;
+
+ default:
+ goto invalid_type;
+ }
+ }
+ }
+ else {
+ invalid_type:
+ errno = EINVAL;
+ mrp_msgbuf_cancel(&mb);
+ nomem:
+ *bufp = NULL;
+ return 0;
+ }
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ return (size_t)(mb.p - mb.buf);
+}
+
+
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+
+
+void *mrp_data_decode(void **bufp, size_t *sizep, mrp_data_descr_t *descr)
+{
+ void *data;
+ mrp_data_member_t *fields, *f;
+ int nfield;
+ mrp_msgbuf_t mb;
+ uint16_t tag, base;
+ mrp_msg_value_t *v;
+ void *value;
+ uint32_t len, n, j, size;
+ int i;
+
+ fields = descr->fields;
+ nfield = descr->nfield;
+ data = mrp_allocz(descr->size);
+
+ if (MRP_UNLIKELY(data == NULL))
+ return NULL;
+
+ mrp_msgbuf_read(&mb, *bufp, *sizep);
+
+ for (i = 0; i < nfield; i++) {
+ tag = be16toh(MRP_MSGBUF_PULL(&mb, typeof(tag) , 1, nodata));
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto unknown_field;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ if (len > 0)
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ value = "";
+ v->str = mrp_strdup((char *)value);
+ if (v->str == NULL)
+ goto nomem;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v->bln = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v->u8 = MRP_MSGBUF_PULL(&mb, typeof(v->u8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v->s8 = MRP_MSGBUF_PULL(&mb, typeof(v->s8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v->u16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->u16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v->s16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->s16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v->u32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->u32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v->s32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->s32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v->u64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->u64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v->s64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->s64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v->dbl = MRP_MSGBUF_PULL(&mb, typeof(v->dbl), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ v->blb = mrp_datadup(value, len);
+ if (v->blb == NULL)
+ goto nomem;
+ break;
+
+ default:
+ if (!(f->type & MRP_MSG_FIELD_ARRAY)) {
+ unknown_field:
+ errno = EINVAL;
+ goto fail;
+ }
+
+ base = f->type & ~MRP_MSG_FIELD_ARRAY;
+ n = be32toh(MRP_MSGBUF_PULL(&mb, typeof(n), 1, nodata));
+
+ if (!f->guard && get_array_size(data, descr, i) != (int)n) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ size = n;
+
+ switch (base) {
+ case MRP_MSG_FIELD_STRING: size *= sizeof(*v->astr); break;
+ case MRP_MSG_FIELD_BOOL: size *= sizeof(*v->abln); break;
+ case MRP_MSG_FIELD_UINT8: size *= sizeof(*v->au8); break;
+ case MRP_MSG_FIELD_SINT8: size *= sizeof(*v->as8); break;
+ case MRP_MSG_FIELD_UINT16: size *= sizeof(*v->au16); break;
+ case MRP_MSG_FIELD_SINT16: size *= sizeof(*v->as16); break;
+ case MRP_MSG_FIELD_UINT32: size *= sizeof(*v->au32); break;
+ case MRP_MSG_FIELD_SINT32: size *= sizeof(*v->as32); break;
+ case MRP_MSG_FIELD_UINT64: size *= sizeof(*v->au64); break;
+ case MRP_MSG_FIELD_SINT64: size *= sizeof(*v->as64); break;
+ case MRP_MSG_FIELD_DOUBLE: size *= sizeof(*v->adbl); break;
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+
+ v->aany = mrp_allocz(size);
+ if (v->aany == NULL)
+ goto nomem;
+
+ for (j = 0; j < n; j++) {
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len),
+ 1, nodata));
+ if (len > 0)
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ else
+ value = "";
+
+ v->astr[j] = mrp_strdup(value);
+ if (v->astr[j] == NULL)
+ goto nomem;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v->abln[j] = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t,
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v->au8[j] = MRP_MSGBUF_PULL(&mb, typeof(v->u8),
+ 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v->as8[j] = MRP_MSGBUF_PULL(&mb, typeof(v->s8),
+ 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v->au16[j] = be16toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->u16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v->as16[j] = be16toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->s16),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v->au32[j] = be32toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->u32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v->as32[j] = be32toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->s32),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v->au64[j] = be64toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->u64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v->as64[j] = be64toh(MRP_MSGBUF_PULL(&mb,
+ typeof(v->s64),
+ 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v->adbl[j] = MRP_MSGBUF_PULL(&mb, typeof(v->dbl),
+ 1, nodata);
+ break;
+
+ default:
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ *sizep -= mb.p - mb.buf;
+ return data;
+
+ nodata:
+ nomem:
+ fail:
+ if (data != NULL) {
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ case MRP_MSG_FIELD_BLOB:
+ mrp_free(*(void **)(data + f->offs));
+ }
+ }
+
+ mrp_free(data);
+ }
+
+ return NULL;
+}
+
+
+int mrp_data_dump(void *data, mrp_data_descr_t *descr, FILE *fp)
+{
+#define DUMP(_indent, _fmt, _typename, _val) \
+ l += fprintf(fp, "%*.*s= <%s> "_fmt"\n", _indent, _indent, "", \
+ _typename, _val)
+
+ mrp_data_member_t *dm;
+ mrp_msg_value_t *v;
+ uint16_t base;
+ int i, j, l, cnt;
+ const char *tname;
+
+
+ l = fprintf(fp, "{\n");
+ for (i = 0, dm = descr->fields; i < descr->nfield; i++, dm++) {
+ l += fprintf(fp, " @%d ", dm->offs);
+ v = (mrp_msg_value_t *)(data + dm->offs);
+ tname = field_type_name(dm->type);
+
+ switch (dm->type) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(0, "'%s'", tname, v->str);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(0, "%s", tname, v->bln ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(0, "%u", tname, v->u8);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(0, "%d", tname, v->s8);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(0, "%u", tname, v->u16);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(0, "%d", tname, v->s16);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(0, "%u", tname, v->u32);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(0, "%d", tname, v->s32);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(0, "%Lu", tname, (long long unsigned)v->u64);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(0, "%Ld", tname, (long long signed)v->s64);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(0, "%f", tname, v->dbl);
+ break;
+ default:
+ if (dm->type & MRP_MSG_FIELD_ARRAY) {
+ base = dm->type & ~MRP_MSG_FIELD_ARRAY;
+ cnt = get_array_size(data, descr, i);
+
+ if (cnt < 0) {
+ fprintf(fp, "= <%s> ???\n", tname);
+ continue;
+ }
+
+ fprintf(fp, "= <%s> (%d)\n", tname, cnt);
+ tname = field_type_name(base);
+
+ for (j = 0; j < cnt; j++) {
+ switch (base) {
+ case MRP_MSG_FIELD_STRING:
+ DUMP(8, "'%s'", tname, v->astr[j]);
+ break;
+ case MRP_MSG_FIELD_BOOL:
+ DUMP(8, "%s", tname, v->abln[j] ? "true" : "false");
+ break;
+ case MRP_MSG_FIELD_UINT8:
+ DUMP(8, "%u", tname, v->au8[j]);
+ break;
+ case MRP_MSG_FIELD_SINT8:
+ DUMP(8, "%d", tname, v->as8[j]);
+ break;
+ case MRP_MSG_FIELD_UINT16:
+ DUMP(8, "%u", tname, v->au16[j]);
+ break;
+ case MRP_MSG_FIELD_SINT16:
+ DUMP(8, "%d", tname, v->as16[j]);
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ DUMP(8, "%u", tname, v->au32[j]);
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ DUMP(8, "%d", tname, v->as32[j]);
+ break;
+ case MRP_MSG_FIELD_UINT64:
+ DUMP(8, "%Lu", tname, (long long unsigned)v->au64[j]);
+ break;
+ case MRP_MSG_FIELD_SINT64:
+ DUMP(8, "%Ld", tname, (long long signed)v->as64[j]);
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ DUMP(8, "%f", tname, v->adbl[j]);
+ break;
+ default:
+ fprintf(fp, "%*.*s<%s>\n", 8, 8, "", tname);
+ break;
+ }
+ }
+ }
+ }
+ }
+ l += fprintf(fp, "}\n");
+
+ return l;
+}
+
+
+int mrp_data_free(void *data, uint16_t tag)
+{
+ mrp_data_descr_t *type;
+ mrp_list_hook_t *p, *n;
+ mrp_data_member_t *f;
+ void *ptr;
+ int i, idx, cnt;
+
+ if (data == NULL)
+ return TRUE;
+
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ mrp_list_foreach(&type->allocated, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+ ptr = *(void **)(data + f->offs);
+
+ if (f->type == (MRP_MSG_FIELD_ARRAY | MRP_MSG_FIELD_STRING)) {
+ idx = f - type->fields;
+ cnt = get_array_size(data, type, idx);
+
+ for (i = 0; i < cnt; i++)
+ mrp_free(((char **)ptr)[i]);
+ }
+
+ mrp_free(ptr);
+ }
+
+ mrp_free(data);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void *mrp_msgbuf_write(mrp_msgbuf_t *mb, size_t size)
+{
+ mrp_clear(mb);
+
+ mb->buf = mrp_allocz(size);
+
+ if (mb->buf != NULL) {
+ mb->size = size;
+ mb->p = mb->buf;
+ mb->l = size;
+
+ return mb->p;
+ }
+ else
+ return NULL;
+}
+
+
+void mrp_msgbuf_read(mrp_msgbuf_t *mb, void *buf, size_t size)
+{
+ mb->buf = mb->p = buf;
+ mb->size = mb->l = size;
+}
+
+
+void mrp_msgbuf_cancel(mrp_msgbuf_t *mb)
+{
+ mrp_free(mb->buf);
+ mb->buf = mb->p = NULL;
+}
+
+
+void *mrp_msgbuf_ensure(mrp_msgbuf_t *mb, size_t size)
+{
+ int diff;
+
+ if (MRP_UNLIKELY(size > mb->l)) {
+ diff = size - mb->l;
+
+ if (diff < MSG_MIN_CHUNK)
+ diff = MSG_MIN_CHUNK;
+
+ mb->p -= (ptrdiff_t)mb->buf;
+
+ if (mrp_realloc(mb->buf, mb->size + diff)) {
+ memset(mb->buf + mb->size, 0, diff);
+ mb->size += diff;
+ mb->p += (ptrdiff_t)mb->buf;
+ mb->l += diff;
+ }
+ else
+ mrp_msgbuf_cancel(mb);
+ }
+
+ return mb->p;
+}
+
+
+void *mrp_msgbuf_reserve(mrp_msgbuf_t *mb, size_t size, size_t align)
+{
+ void *reserved;
+ ptrdiff_t offs, pad;
+ size_t len;
+
+ len = size;
+ offs = mb->p - mb->buf;
+
+ if (offs % align != 0) {
+ pad = align - (offs % align);
+ len += pad;
+ }
+ else
+ pad = 0;
+
+ if (mrp_msgbuf_ensure(mb, len)) {
+ if (pad != 0)
+ memset(mb->p, 0, pad);
+
+ reserved = mb->p + pad;
+
+ mb->p += len;
+ mb->l -= len;
+ }
+ else
+ reserved = NULL;
+
+ return reserved;
+}
+
+
+void *mrp_msgbuf_pull(mrp_msgbuf_t *mb, size_t size, size_t align)
+{
+ void *pulled;
+ ptrdiff_t offs, pad;
+ size_t len;
+
+ len = size;
+ offs = mb->p - mb->buf;
+
+ if (offs % align != 0) {
+ pad = align - (offs % align);
+ len += pad;
+ }
+ else
+ pad = 0;
+
+ if (mb->l >= len) {
+ pulled = mb->p + pad;
+
+ mb->p += len;
+ mb->l -= len;
+ }
+ else
+ pulled = NULL;
+
+ return pulled;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MSG_H__
+#define __MURPHY_MSG_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include <murphy/common/list.h>
+#include <murphy/common/refcnt.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * message field types
+ */
+
+#define A(t) MRP_MSG_FIELD_##t
+typedef enum {
+ MRP_MSG_FIELD_INVALID = 0x00, /* defined invalid type */
+ MRP_MSG_FIELD_STRING = 0x01, /* mqi_varchar */
+ MRP_MSG_FIELD_INTEGER = 0x02, /* mqi_integer */
+ MRP_MSG_FIELD_UNSIGNED = 0x03, /* mqi_unsignd */
+ MRP_MSG_FIELD_DOUBLE = 0x04, /* mqi_floating */
+ MRP_MSG_FIELD_BOOL = 0x05, /* boolean */
+ MRP_MSG_FIELD_UINT8 = 0x06, /* unsigned 8-bit integer */
+ MRP_MSG_FIELD_SINT8 = 0x07, /* signed 8-bit integer */
+ MRP_MSG_FIELD_INT8 = A(SINT8), /* alias for SINT8 */
+ MRP_MSG_FIELD_UINT16 = 0x08, /* unsigned 16-bit integer */
+ MRP_MSG_FIELD_SINT16 = 0x09, /* signed 16-bit integer */
+ MRP_MSG_FIELD_INT16 = A(SINT16), /* alias for SINT16 */
+ MRP_MSG_FIELD_UINT32 = 0x0a, /* unsigned 32-bit integer */
+ MRP_MSG_FIELD_SINT32 = 0x0b, /* signed 32-bit integer */
+ MRP_MSG_FIELD_INT32 = A(SINT32), /* alias for SINT32 */
+ MRP_MSG_FIELD_UINT64 = 0x0c, /* unsigned 64-bit integer */
+ MRP_MSG_FIELD_SINT64 = 0x0d, /* signed 64-bit integer */
+ MRP_MSG_FIELD_INT64 = A(SINT64), /* alias for SINT64 */
+ MRP_MSG_FIELD_BLOB = 0x0e, /* a blob (not allowed in arrays) */
+ MRP_MSG_FIELD_MAX = 0x0e,
+ MRP_MSG_FIELD_ANY = 0x0f, /* any type of field when querying */
+
+ MRP_MSG_FIELD_ARRAY = 0x80, /* bit-mask to mark arrays */
+} mrp_msg_field_type_t;
+#undef A
+
+#define MRP_MSG_END ((char *)MRP_MSG_FIELD_INVALID) /* NULL */
+
+#define MRP_MSG_FIELD_ARRAY_OF(t) (MRP_MSG_FIELD_ARRAY | MRP_MSG_FIELD_##t)
+#define MRP_MSG_FIELD_IS_ARRAY(t) ((t) & MRP_MSG_FIELD_ARRAY)
+#define MRP_MSG_FIELD_ARRAY_TYPE(t) ((t) & ~MRP_MSG_FIELD_ARRAY)
+
+#define MRP_MSG_TAG_STRING(tag, arg) (tag), MRP_MSG_FIELD_STRING, (arg)
+#define MRP_MSG_TAG_BOOL(tag, arg) (tag), MRP_MSG_FIELD_BOOL , (arg)
+#define MRP_MSG_TAG_UINT8(tag, arg) (tag), MRP_MSG_FIELD_UINT8 , (arg)
+#define MRP_MSG_TAG_SINT8(tag, arg) (tag), MRP_MSG_FIELD_SINT8 , (arg)
+#define MRP_MSG_TAG_UINT16(tag, arg) (tag), MRP_MSG_FIELD_UINT16, (arg)
+#define MRP_MSG_TAG_SINT16(tag, arg) (tag), MRP_MSG_FIELD_SINT16, (arg)
+#define MRP_MSG_TAG_UINT32(tag, arg) (tag), MRP_MSG_FIELD_UINT32, (arg)
+#define MRP_MSG_TAG_SINT32(tag, arg) (tag), MRP_MSG_FIELD_SINT32, (arg)
+#define MRP_MSG_TAG_UINT64(tag, arg) (tag), MRP_MSG_FIELD_UINT64, (arg)
+#define MRP_MSG_TAG_SINT64(tag, arg) (tag), MRP_MSG_FIELD_SINT64, (arg)
+#define MRP_MSG_TAG_DOUBLE(tag, arg) (tag), MRP_MSG_FIELD_DOUBLE, (arg)
+#define MRP_MSG_TAG_BLOB(tag, arg) (tag), MRP_MSG_FIELD_BLOB , (arg)
+
+#define MRP_MSG_TAGGED(tag, type, ...) (tag), (type), __VA_ARGS__
+#define MRP_MSG_TAG_ARRAY(tag, type, cnt, arr) \
+ (tag), MRP_MSG_FIELD_ARRAY | MRP_MSG_FIELD_##type, (cnt), (arr)
+#define MRP_MSG_TAG_STRING_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), STRING, (cnt), (arr))
+#define MRP_MSG_TAG_BOOL_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), BOOL, (cnt), (arr))
+#define MRP_MSG_TAG_UINT8_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT8, (cnt), (arr))
+#define MRP_MSG_TAG_SINT8_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT8, (cnt), (arr))
+#define MRP_MSG_TAG_UINT16_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT16, (cnt), (arr))
+#define MRP_MSG_TAG_SINT16_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT16, (cnt), (arr))
+#define MRP_MSG_TAG_UINT32_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT32, (cnt), (arr))
+#define MRP_MSG_TAG_SINT32_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT32, (cnt), (arr))
+#define MRP_MSG_TAG_UINT64_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), UINT64, (cnt), (arr))
+#define MRP_MSG_TAG_SINT64_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), SINT64, (cnt), (arr))
+#define MRP_MSG_TAG_DOUBLE_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), DOUBLE, (cnt), (arr))
+#define MRP_MSG_TAG_BLOB_ARRAY(tag, cnt, arr) \
+ MRP_MSG_TAG_ARRAY((tag), BLOB, (cnt), (arr))
+
+#define MRP_MSG_TAG_ANY(tag, typep, valuep) \
+ (tag), MRP_MSG_FIELD_ANY, (typep), (valuep)
+
+
+/** Sentinel to pass in as the last argument to mrp_msg_create. */
+#define MRP_MSG_FIELD_END NULL
+
+
+/*
+ * generic messages
+ *
+ * A generic message is just a collection of message fields. By default
+ * transports are in generic messaging mode in which case they take messages
+ * as input (for transmission) and provide messages as events (for receiption).
+ * A generic message field consists of a field tag, a field type, the actual
+ * type-specific field value, and for certain types a size.
+ *
+ * The field tag is used by the communicating parties to attach semantic
+ * meaning to the field data. One can think of it as the 'name' of the field
+ * within a message. It is not interpreted by the messaging layer in any way.
+ * The field type defines what kind of data the field contains contains and
+ * it must be one of the predefined MRP_MSG_FIELD_* types. The actual field
+ * data then depends on the type. size is only used for those data types that
+ * require a size (blobs and arrays).
+ */
+
+#define MRP_MSG_VALUE_UNION union { \
+ char *str; \
+ bool bln; \
+ uint8_t u8; \
+ int8_t s8; \
+ uint16_t u16; \
+ int16_t s16; \
+ uint32_t u32; \
+ int32_t s32; \
+ uint64_t u64; \
+ int64_t s64; \
+ double dbl; \
+ void *blb; \
+ void *aany; \
+ char **astr; \
+ bool *abln; \
+ uint8_t *au8; \
+ int8_t *as8; \
+ uint16_t *au16; \
+ int16_t *as16; \
+ uint32_t *au32; \
+ int32_t *as32; \
+ uint64_t *au64; \
+ int64_t *as64; \
+ double *adbl; \
+ }
+
+typedef MRP_MSG_VALUE_UNION mrp_msg_value_t;
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to list of fields */
+ uint16_t tag; /* message field tag */
+ uint16_t type; /* message field type */
+ MRP_MSG_VALUE_UNION; /* message field value */
+ uint32_t size[0]; /* size, if an array or a blob */
+} mrp_msg_field_t;
+
+
+typedef struct {
+ mrp_list_hook_t fields; /* list of message fields */
+ size_t nfield; /* number of fields */
+ mrp_refcnt_t refcnt; /* reference count */
+} mrp_msg_t;
+
+
+/** Create a new message. */
+mrp_msg_t *mrp_msg_create(uint16_t tag, ...) MRP_NULLTERM;
+
+/** Create a new message. */
+mrp_msg_t *mrp_msg_createv(uint16_t tag, va_list ap);
+
+/** Macro to create an empty message. */
+#define mrp_msg_create_empty() mrp_msg_create(MRP_MSG_FIELD_INVALID, NULL)
+
+/** Increase refcount of the given message. */
+mrp_msg_t *mrp_msg_ref(mrp_msg_t *msg);
+
+/** Decrease the refcount, free the message if refcount drops to zero. */
+void mrp_msg_unref(mrp_msg_t *msg);
+
+/** Append a field to a message. */
+int mrp_msg_append(mrp_msg_t *msg, uint16_t tag, ...);
+
+/** Prepend a field to a message. */
+int mrp_msg_prepend(mrp_msg_t *msg, uint16_t tag, ...);
+
+/** Set a field in a message to the given value. */
+int mrp_msg_set(mrp_msg_t *msg, uint16_t tag, ...);
+
+/** Iterate through the fields of a message. You must not any of the
+ fields while iterating. */
+int mrp_msg_iterate(mrp_msg_t *msg, void **it, uint16_t *tagp,
+ uint16_t *typep, mrp_msg_value_t *valp, size_t *sizep);
+
+/** Iterate through the matching fields of a message. You should not delete
+ * any of the fields while iterating through the message. */
+int mrp_msg_iterate_matching(mrp_msg_t *msg, void **it, uint16_t *tagp,
+ uint16_t *typep, mrp_msg_value_t *valp,
+ size_t *sizep);
+
+/** Find a field in a message. */
+mrp_msg_field_t *mrp_msg_find(mrp_msg_t *msg, uint16_t tag);
+
+/** Get the given fields (with matching tags and types) from the message. */
+int mrp_msg_get(mrp_msg_t *msg, ...) MRP_NULLTERM;
+
+/** Iterate through the message getting the given fields. */
+int mrp_msg_iterate_get(mrp_msg_t *msg, void **it, ...);
+
+/** Dump a message. */
+int mrp_msg_dump(mrp_msg_t *msg, FILE *fp);
+
+/** Encode the given message using the default message encoder. */
+ssize_t mrp_msg_default_encode(mrp_msg_t *msg, void **bufp);
+
+/** Decode the given message using the default message decoder. */
+mrp_msg_t *mrp_msg_default_decode(void *buf, size_t size);
+
+
+/*
+ * custom data types
+ *
+ * In addition to generic messages, you can instruct the messaging and
+ * transport layers to encode/decode messages directly from/to custom data
+ * structures. To do so you need to describe your data structures and register
+ * them using data descriptors. A descriptor basically consists of a type
+ * tag, structure size, number of members and and array of structure member
+ * descriptors.
+ *
+ * The data type tag is used to identify the descriptor and consequently
+ * the custom data type both during sending and receiving (ie. encoding and
+ * decoding). It is assigned by the registering entity, it must be unique,
+ * and it cannot be MRP_MSG_TAG_DEFAULT (0x0), or else registration will
+ * fail. The size is used to allocate necessary memory for the data on the
+ * receiving end. The member descriptors are used to describe the offset
+ * and types of the members within the custom data type.
+ */
+
+#define MRP_MSG_TAG_DEFAULT 0x0 /* tag for default encode/decoder */
+
+typedef struct {
+ uint16_t offs; /* offset within structure */
+ uint16_t tag; /* tag for this member */
+ uint16_t type; /* type of this member */
+ bool guard; /* whether sentinel-terminated */
+ MRP_MSG_VALUE_UNION; /* sentinel or offset of count field */
+ mrp_list_hook_t hook; /* hook to list of extra allocations */
+} mrp_data_member_t;
+
+
+typedef struct {
+ mrp_refcnt_t refcnt; /* reference count */
+ uint16_t tag; /* structure tag */
+ size_t size; /* size of this structure */
+ int nfield; /* number of members */
+ mrp_data_member_t *fields; /* member descriptors */
+ mrp_list_hook_t allocated; /* fields needing extra allocation */
+} mrp_data_descr_t;
+
+
+/** Convenience macro to declare a custom data type (and its members). */
+#define MRP_DATA_DESCRIPTOR(_var, _tag, _type, ...) \
+ static mrp_data_member_t _var##_members[] = { \
+ __VA_ARGS__ \
+ }; \
+ \
+ static mrp_data_descr_t _var = { \
+ .size = sizeof(_type), \
+ .tag = _tag, \
+ .fields = _var##_members, \
+ .nfield = MRP_ARRAY_SIZE(_var##_members) \
+ }
+
+/** Convenience macro to declare a data member. */
+#define MRP_DATA_MEMBER(_data_type, _member, _member_type) { \
+ .offs = MRP_OFFSET(_data_type, _member), \
+ .type = _member_type, \
+ .guard = FALSE \
+ }
+
+/** Convenience macro to declare an array data member with a count field. */
+#define MRP_DATA_ARRAY_COUNT(_data_type, _array, _count, _base_type) { \
+ .offs = MRP_OFFSET(_data_type, _array), \
+ .type = MRP_MSG_FIELD_ARRAY | _base_type, \
+ .guard = FALSE, \
+ { .u32 = MRP_OFFSET(_data_type, _count) } \
+ }
+
+/** Convenience macro to declare an array data member with a sentinel value. */
+#define MRP_DATA_ARRAY_GUARD(_data_type, _array, _guard_member, _guard_val, \
+ _base_type) { \
+ .offs = MRP_OFFSET(_data_type, _array), \
+ .type = MRP_MSG_FIELD_ARRAY | _base_type, \
+ .guard = TRUE, \
+ { ._guard_member = _guard_val } \
+ }
+
+/** Convenience macro to declare a blob data member with a count field. */
+#define MRP_DATA_BLOB_MEMBER(_data_type, _blob, _count) { \
+ .offs = MRP_OFFSET(_data_type, _blob), \
+ .type = MRP_MSG_FIELD_BLOB, \
+ .guard = FALSE, \
+ .u32 = MRP_OFFSET(_data_type, _count) \
+ }
+
+
+/** Encode a structure using the given message descriptor. */
+size_t mrp_data_encode(void **bufp, void *data, mrp_data_descr_t *descr,
+ size_t reserve);
+
+/** Decode a structure using the given message descriptor. */
+void *mrp_data_decode(void **bufp, size_t *sizep, mrp_data_descr_t *descr);
+
+/** Dump the given data buffer. */
+int mrp_data_dump(void *data, mrp_data_descr_t *descr, FILE *fp);
+
+/** Get the size of a data array member. */
+int mrp_data_get_array_size(void *data, mrp_data_descr_t *type, int idx);
+
+/** Get the size of a data blob member. */
+int mrp_data_get_blob_size(void *data, mrp_data_descr_t *type, int idx);
+
+/** Register a new custom data type with the messaging/transport layer. */
+int mrp_msg_register_type(mrp_data_descr_t *type);
+
+/** Look up the data type descriptor corresponding to the given tag. */
+mrp_data_descr_t *mrp_msg_find_type(uint16_t tag);
+
+/** Free the given custom data allocated by the messaging layer. */
+int mrp_data_free(void *data, uint16_t tag);
+
+/*
+ * message encoding/decoding buffer
+ *
+ * This message buffer and the associated functions and macros can be
+ * used to write message encoding/decoding functions for bitpipe-type
+ * transports, ie. for transports where the underlying IPC just provides
+ * a raw data connection between the communication endpoints and does not
+ * impose/expect any structure on/from the data being transmitted.
+ *
+ * Practically all the basic stream and datagram socket transports are
+ * such. They use the default encoding/decoding functions provided by
+ * the messaging layer together with a very simple transport frame scheme,
+ * where each frame consists of the amount a size indicating the size of
+ * the encoded message in the bitpipe and the actual encoded message data.
+ *
+ * Note that at the moment this framing scheme is rather implicit in the
+ * sense that you won't find a data type representing a frame. Rather the
+ * framing is simply done in the sending/receiving code of the individual
+ * transports.
+ */
+
+typedef struct {
+ void *buf; /* buffer to encode to/decode from */
+ size_t size; /* size of the buffer */
+ void *p; /* encoding/decoding pointer */
+ size_t l; /* space left in the buffer */
+} mrp_msgbuf_t;
+
+
+
+/** Initialize the given message buffer for writing. */
+void *mrp_msgbuf_write(mrp_msgbuf_t *mb, size_t size);
+
+/** Initialize the given message buffer for reading. */
+void mrp_msgbuf_read(mrp_msgbuf_t *mb, void *buf, size_t size);
+
+/** Deinitialize the given message buffer, usually due to some error. */
+void mrp_msgbuf_cancel(mrp_msgbuf_t *mb);
+
+/** Reallocate the buffer if needed to accomodate size bytes of data. */
+void *mrp_msgbuf_ensure(mrp_msgbuf_t *mb, size_t size);
+
+/** Reserve the given amount of space from the buffer. */
+void *mrp_msgbuf_reserve(mrp_msgbuf_t *mb, size_t size, size_t align);
+
+/** Pull the given amount of data from the buffer. */
+void *mrp_msgbuf_pull(mrp_msgbuf_t *mb, size_t size, size_t align);
+
+/** Push data with alignment to the buffer, jumping to errlbl on errors. */
+#define MRP_MSGBUF_PUSH(mb, data, align, errlbl) do { \
+ size_t _size = sizeof(data); \
+ typeof(data) *_ptr; \
+ \
+ _ptr = mrp_msgbuf_reserve((mb), _size, (align)); \
+ \
+ if (_ptr != NULL) \
+ *_ptr = data; \
+ else \
+ goto errlbl; \
+ } while (0)
+
+/** Push aligned data to the buffer, jumping to errlbl on errors. */
+#define MRP_MSGBUF_PUSH_DATA(mb, data, size, align, errlbl) do { \
+ size_t _size = (size); \
+ void *_ptr; \
+ \
+ _ptr = mrp_msgbuf_reserve((mb), _size, (align)); \
+ \
+ if (_ptr != NULL) \
+ memcpy(_ptr, data, _size); \
+ else \
+ goto errlbl; \
+ } while (0)
+
+/** Pull aligned data of type from the buffer, jump to errlbl on errors. */
+#define MRP_MSGBUF_PULL(mb, type, align, errlbl) ({ \
+ size_t _size = sizeof(type); \
+ type *_ptr; \
+ \
+ _ptr = mrp_msgbuf_pull((mb), _size, (align)); \
+ \
+ if (_ptr == NULL) \
+ goto errlbl; \
+ \
+ *_ptr; \
+ })
+
+/** Pull aligned data of type from the buffer, jump to errlbl on errors. */
+#define MRP_MSGBUF_PULL_DATA(mb, size, align, errlbl) ({ \
+ size_t _size = size; \
+ void *_ptr; \
+ \
+ _ptr = mrp_msgbuf_pull((mb), _size, (align)); \
+ \
+ if (_ptr == NULL) \
+ goto errlbl; \
+ \
+ _ptr; \
+ })
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_MSG_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-common
+Description: Murphy policy framework, common library.
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-common @JSON_LIBS@
+Cflags: -I${includedir} @JSON_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-dbus
+Description: Murphy policy framework, libdbus based dbus library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-dbus-libdbus @LIBDBUS_LIBS@
+Cflags: -I${includedir} @LIBDBUS_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-sd-bus
+Description: Murphy policy framework, systemd-bus based dbus library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-sd-bus @SDBUS_LIBS@
+Cflags: -I${includedir} @SDBUS_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-ecore
+Description: Murphy policy framework, EFL/ecore mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-ecore @ECORE_LIBS@
+Cflags: -I${includedir} @ECORE_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-glib
+Description: Murphy policy framework, GLIB mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-glib @GLIB_LIBS@
+Cflags: -I${includedir} @GLIB_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-dbus
+Description: Murphy policy framework, dbus library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-libdbus @LIBDBUS_LIBS@
+Cflags: -I${includedir} @LIBDBUS_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-pulse
+Description: Murphy policy framework, PulseAudio mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-pulse
+Cflags: -I${includedir} @PULSE_CFLAGS@
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-qt
+Description: Murphy policy framework, Qt mainloop glue library.
+Requires: murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-qt @QTCORE_LIBS@
+Cflags: -I${includedir} @QTCORE_CFLAGS@
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/tlv.h>
+#include <murphy/common/native-types.h>
+
+
+/*
+ * TLV tags we use when encoding/decoding our native types
+ */
+
+typedef enum {
+ TAG_NONE = MRP_TLV_UNTAGGED, /* untagged data */
+ TAG_STRUCT, /* a native structure */
+ TAG_MEMBER, /* a native structure member */
+ TAG_ARRAY, /* an array */
+ TAG_NELEM, /* size of an array (in elements) */
+} tag_t;
+
+
+/*
+ * extra header we use to keep track of memory while decoding
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to chunk list */
+ char data[0]; /* user-visible data */
+} chunk_t;
+
+
+static int encode_struct(mrp_tlv_t *tlv, void *data, mrp_native_type_t *t,
+ mrp_typemap_t *idmap);
+static int decode_struct(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ void **datap, uint32_t *idp, mrp_typemap_t *idmap);
+static int print_struct(char **buf, size_t *size, int level,
+ void *data, mrp_native_type_t *t);
+static void free_native(mrp_native_type_t *t);
+
+static void *alloc_chunk(mrp_list_hook_t **chunks, size_t size);
+static void free_chunks(mrp_list_hook_t *chunks);
+
+
+/*
+ * list and table of registered native types
+ */
+
+static MRP_LIST_HOOK(types);
+static int ntype;
+
+static mrp_native_type_t **typetbl;
+
+
+static mrp_native_member_t *native_member(mrp_native_type_t *t, int idx)
+{
+ if (0 <= idx && idx < (int)t->nmember)
+ return t->members + idx;
+ else {
+ errno = EINVAL;
+ return NULL;
+ }
+}
+
+
+static int member_index(mrp_native_type_t *t, const char *name)
+{
+ mrp_native_member_t *m;
+ size_t i;
+
+ for (i = 0, m = t->members; i < t->nmember; i++, m++)
+ if (!strcmp(m->any.name, name))
+ return m - t->members;
+
+ return -1;
+}
+
+
+static int copy_member(mrp_native_type_t *t, mrp_native_member_t *m)
+{
+ mrp_native_member_t *tm;
+ size_t size;
+
+ if ((tm = native_member(t, member_index(t, m->any.name))) != NULL)
+ return tm - t->members;
+ else
+ tm = t->members + t->nmember;
+
+ *tm = *m;
+
+ if (*m->any.name != '"')
+ tm->any.name = mrp_strdup(m->any.name);
+ else {
+ size = strlen(m->any.name) + 1 - 2;
+
+ if ((tm->any.name = mrp_allocz(size)) != NULL)
+ strncpy(tm->any.name, m->any.name + 1, size - 1);
+ }
+
+ if (tm->any.name != NULL) {
+ t->nmember++;
+ return tm - t->members;
+ }
+ else
+ return -1;
+}
+
+
+static mrp_native_type_t *find_type(const char *type_name)
+{
+ mrp_native_type_t *t;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&types, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (!strcmp(t->name, type_name))
+ return t;
+ }
+
+ return NULL;
+}
+
+
+static mrp_native_type_t *lookup_type(uint32_t id)
+{
+ mrp_native_type_t *t;
+ mrp_list_hook_t *p, *n;
+
+ /* XXX TODO: turn this into a real lookup instead of linear search */
+
+ if (1 <= id && id <= (uint32_t)ntype)
+ if ((t = typetbl[id]) != NULL && t->id == id)
+ return t;
+
+ mrp_log_warning("Type lookup for %u failed, doing linear search...\n", id);
+
+ mrp_list_foreach(&types, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ if (t->id == id)
+ return t;
+ }
+
+ return NULL;
+}
+
+
+static mrp_native_type_t *member_type(mrp_native_member_t *m)
+{
+ mrp_native_type_t *t;
+
+ if (m->any.type != MRP_TYPE_STRUCT)
+ t = lookup_type(m->any.type);
+ else
+ t = lookup_type(m->strct.data_type.id);
+
+ if (t == NULL)
+ errno = EINVAL;
+
+ return t;
+}
+
+
+static inline uint32_t map_type(uint32_t id, mrp_typemap_t *idmap)
+{
+ uint32_t mapped = MRP_INVALID_TYPE;
+
+ if (id < MRP_TYPE_STRUCT || idmap == NULL)
+ mapped = id;
+ else {
+ while (idmap->type_id != MRP_INVALID_TYPE) {
+ if (idmap->type_id == id) {
+ mapped = MRP_TYPE_STRUCT + idmap->mapped;
+ break;
+ }
+ else
+ idmap++;
+ }
+ }
+
+ return mapped;
+}
+
+
+static inline uint32_t mapped_type(uint32_t mapped, mrp_typemap_t *idmap)
+{
+ uint32_t id = MRP_INVALID_TYPE;
+
+ if (mapped < MRP_TYPE_STRUCT || idmap == NULL)
+ id = mapped;
+ else {
+ while (idmap->type_id != MRP_INVALID_TYPE) {
+ if (MRP_TYPE_STRUCT + idmap->mapped == mapped) {
+ id = idmap->type_id;
+ break;
+ }
+ else
+ idmap++;
+ }
+ }
+
+ return id;
+}
+
+
+uint32_t mrp_type_id(const char *type_name)
+{
+ mrp_native_type_t *t;
+
+ if ((t = find_type(type_name)) != NULL)
+ return t->id;
+ else
+ return MRP_INVALID_TYPE;
+}
+
+
+static size_t type_size(uint32_t id)
+{
+ mrp_native_type_t *t = lookup_type(id);
+
+ if (t != NULL)
+ return t->size;
+ else
+ return 0;
+}
+
+
+static int matching_types(mrp_native_type_t *t1, mrp_native_type_t *t2)
+{
+ MRP_UNUSED(t1);
+ MRP_UNUSED(t2);
+
+ /* XXX TODO */
+ return 0;
+}
+
+
+static void register_default_types(void)
+{
+#define DEFAULT_NTYPE (MRP_TYPE_STRUCT + 1)
+
+#define DECLARE_TYPE(_ctype, _mtype) \
+ static mrp_native_type_t _mtype##_type = { \
+ .name = #_ctype, \
+ .id = MRP_TYPE_##_mtype, \
+ .size = sizeof(_ctype), \
+ .members = NULL, \
+ .nmember = 0, \
+ .hook = { NULL, NULL } \
+ }
+
+#define REGISTER_TYPE(_type) \
+ mrp_list_init(&(_type)->hook); \
+ mrp_list_append(&types, &(_type)->hook); \
+ typetbl[(_type)->id] = (_type)
+
+ if (mrp_reallocz(typetbl, 0, DEFAULT_NTYPE) == NULL) {
+ mrp_log_error("Failed to initialize native type table.");
+ abort();
+ }
+
+ DECLARE_TYPE( int8_t , INT8 );
+ DECLARE_TYPE(uint8_t , UINT8 );
+ DECLARE_TYPE(int16_t , INT16 );
+ DECLARE_TYPE(uint16_t , UINT16);
+ DECLARE_TYPE(int32_t , INT32 );
+ DECLARE_TYPE(uint32_t , UINT32);
+ DECLARE_TYPE(int64_t , INT64 );
+ DECLARE_TYPE(uint64_t , UINT64);
+ DECLARE_TYPE(float , FLOAT );
+ DECLARE_TYPE(double , DOUBLE);
+ DECLARE_TYPE(bool , BOOL );
+ DECLARE_TYPE(int , INT );
+ DECLARE_TYPE(unsigned int , UINT );
+ DECLARE_TYPE(short , SHORT );
+ DECLARE_TYPE(unsigned short, USHORT);
+ DECLARE_TYPE(size_t , SIZET );
+ DECLARE_TYPE(ssize_t , SSIZET);
+ DECLARE_TYPE(char * , STRING);
+ DECLARE_TYPE(void * , BLOB );
+ DECLARE_TYPE(void * , ARRAY );
+ DECLARE_TYPE(void * , STRUCT);
+
+ REGISTER_TYPE(&INT8_type);
+ REGISTER_TYPE(&UINT8_type);
+ REGISTER_TYPE(&INT16_type);
+ REGISTER_TYPE(&UINT16_type);
+ REGISTER_TYPE(&INT32_type);
+ REGISTER_TYPE(&UINT32_type);
+ REGISTER_TYPE(&INT64_type);
+ REGISTER_TYPE(&UINT64_type);
+ REGISTER_TYPE(&FLOAT_type);
+ REGISTER_TYPE(&DOUBLE_type);
+ REGISTER_TYPE(&BOOL_type);
+ REGISTER_TYPE(&INT_type);
+ REGISTER_TYPE(&UINT_type);
+ REGISTER_TYPE(&SHORT_type);
+ REGISTER_TYPE(&USHORT_type);
+ REGISTER_TYPE(&SIZET_type);
+ REGISTER_TYPE(&SSIZET_type);
+ REGISTER_TYPE(&STRING_type);
+ REGISTER_TYPE(&BLOB_type);
+ REGISTER_TYPE(&ARRAY_type);
+ REGISTER_TYPE(&STRUCT_type);
+
+ ntype = DEFAULT_NTYPE;
+
+#undef DECLARE_TYPE
+#undef REGISTER_TYPE
+}
+
+
+uint32_t mrp_register_native(mrp_native_type_t *type)
+{
+ mrp_native_type_t *existing = find_type(type->name);
+ mrp_native_type_t *t, *elemt;
+ mrp_native_member_t *s, *d, *m;
+ int idx;
+
+ (void)member_type;
+
+ if (existing != NULL && !matching_types(existing, type)) {
+ errno = EEXIST;
+ return MRP_INVALID_TYPE;
+ }
+
+ if (ntype == 0)
+ register_default_types();
+
+ if ((t = mrp_allocz(sizeof(*t))) == NULL)
+ return MRP_INVALID_TYPE;
+
+ mrp_list_init(&t->hook);
+ t->name = mrp_strdup(type->name);
+
+ if (t->name == NULL)
+ goto fail;
+
+ t->size = type->size;
+ t->members = mrp_allocz_array(mrp_native_member_t, type->nmember);
+
+ if (t->members == NULL && type->nmember != 0)
+ goto fail;
+
+ /*
+ * Notes:
+ *
+ * While we copy the members, we also take care of reordering them
+ * so that any member that another one depends on ('size' members)
+ * get registered (and consequently encoded and decoded) before the
+ * dependant members.
+ */
+
+ s = type->members;
+ d = t->members;
+ while (t->nmember < type->nmember) {
+ /* make sure there are no duplicate members */
+ if (native_member(type, member_index(type, s->any.name)) != s) {
+ errno = EINVAL;
+ goto fail;
+ }
+
+ /* skip already copied members */
+ while (member_index(t, s->any.name) >= 0)
+ s++;
+
+ switch (s->any.type) {
+ case MRP_TYPE_BLOB:
+ m = native_member(t, member_index(t, s->blob.size.name));
+
+ if (m == NULL) {
+ m = native_member(type,
+ member_index(type, s->blob.size.name));
+
+ if (m == NULL)
+ goto fail;
+ else
+ idx = copy_member(t, m);
+
+ if (idx < 0)
+ goto fail;
+ }
+ else
+ idx = m - t->members;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+
+ d = t->members + t->nmember;
+ d->blob.size.idx = idx;
+
+ break;
+
+ case MRP_TYPE_ARRAY:
+ if (s->array.kind == MRP_ARRAY_SIZE_EXPLICIT) {
+ m = native_member(t, member_index(t, s->array.size.name));
+
+ if (m == NULL) {
+ m = native_member(type,
+ member_index(type, s->array.size.name));
+
+ if (m == NULL)
+ goto fail;
+ else
+ idx = copy_member(t, m);
+
+ if (idx < 0)
+ goto fail;
+
+ }
+ else
+ idx = m - t->members;
+
+ d = t->members + t->nmember;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+
+ d->array.size.idx = idx;
+ }
+ else {
+ d = t->members + t->nmember;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+ }
+
+ d->array.elem.id = mrp_type_id(d->array.elem.name);
+
+ if (d->array.elem.id == MRP_INVALID_TYPE)
+ goto fail;
+
+ if (s->array.kind == MRP_ARRAY_SIZE_GUARDED) {
+ elemt = lookup_type(d->array.elem.id);
+
+ if (elemt == NULL)
+ goto fail;
+
+ if (elemt->id < MRP_TYPE_ARRAY)
+ idx = 0;
+ else {
+ idx = member_index(elemt, s->array.size.name);
+ d->array.size.idx = member_index(elemt, s->array.size.name);
+
+ if (d->array.size.idx == (uint32_t)-1)
+ goto fail;
+ }
+ }
+
+ break;
+
+ case MRP_TYPE_STRUCT:
+ d = t->members + t->nmember;
+
+ if (copy_member(t, s) < 0)
+ goto fail;
+
+ d->strct.data_type.id = mrp_type_id(d->strct.data_type.name);
+
+ if (d->strct.data_type.id == MRP_INVALID_TYPE)
+ goto fail;
+ break;
+
+ default:
+ if (copy_member(t, s) < 0)
+ goto fail;
+ }
+ }
+
+ if (mrp_reallocz(typetbl, ntype, ntype + 1) == NULL)
+ goto fail;
+
+ t->id = ntype;
+ mrp_list_append(&types, &t->hook);
+ typetbl[ntype] = t;
+ ntype++;
+
+ return t->id;
+
+ fail:
+ free_native(t);
+
+ return MRP_INVALID_TYPE;
+}
+
+
+static void free_native(mrp_native_type_t *t)
+{
+ mrp_native_member_t *m;
+ size_t i;
+
+ if (t == NULL)
+ return;
+
+ mrp_list_delete(&t->hook);
+
+ mrp_free(t->name);
+ for (i = 0, m = t->members; i < t->nmember; i++, m++)
+ mrp_free(m->any.name);
+ mrp_free(t);
+}
+
+
+static int encode_basic(mrp_tlv_t *tlv, mrp_type_t type, mrp_value_t *v)
+{
+ switch (type) {
+ case MRP_TYPE_INT8: return mrp_tlv_push_int8 (tlv, TAG_NONE, v->s8);
+ case MRP_TYPE_UINT8: return mrp_tlv_push_uint8 (tlv, TAG_NONE, v->u8);
+ case MRP_TYPE_INT16: return mrp_tlv_push_int16 (tlv, TAG_NONE, v->s16);
+ case MRP_TYPE_UINT16: return mrp_tlv_push_uint16(tlv, TAG_NONE, v->u16);
+ case MRP_TYPE_INT32: return mrp_tlv_push_int32 (tlv, TAG_NONE, v->s32);
+ case MRP_TYPE_UINT32: return mrp_tlv_push_uint32(tlv, TAG_NONE, v->u32);
+ case MRP_TYPE_INT64: return mrp_tlv_push_int64 (tlv, TAG_NONE, v->s64);
+ case MRP_TYPE_UINT64: return mrp_tlv_push_uint64(tlv, TAG_NONE, v->u64);
+ case MRP_TYPE_FLOAT: return mrp_tlv_push_float (tlv, TAG_NONE, v->flt);
+ case MRP_TYPE_DOUBLE: return mrp_tlv_push_double(tlv, TAG_NONE, v->dbl);
+ case MRP_TYPE_BOOL: return mrp_tlv_push_bool (tlv, TAG_NONE, v->bln);
+ case MRP_TYPE_STRING: return mrp_tlv_push_string(tlv, TAG_NONE, v->str);
+
+ case MRP_TYPE_INT:
+ return mrp_tlv_push_int32 (tlv, TAG_NONE, (int32_t)v->i);
+ case MRP_TYPE_UINT:
+ return mrp_tlv_push_uint32(tlv, TAG_NONE, (uint32_t)v->ui);
+ case MRP_TYPE_SHORT:
+ return mrp_tlv_push_int32 (tlv, TAG_NONE, (int32_t)v->si);
+ case MRP_TYPE_USHORT:
+ return mrp_tlv_push_uint32(tlv, TAG_NONE, (uint32_t)v->usi);
+ case MRP_TYPE_SIZET:
+ return mrp_tlv_push_uint32(tlv, TAG_NONE, (uint32_t)v->sz);
+ case MRP_TYPE_SSIZET:
+ return mrp_tlv_push_int32 (tlv, TAG_NONE, (int32_t)v->ssz);
+
+ default:
+ return -1;
+ }
+}
+
+
+static inline int get_blob_size(void *base, mrp_native_type_t *t,
+ mrp_native_blob_t *m, size_t *sizep)
+{
+ mrp_native_member_t *sizem;
+ mrp_value_t *v;
+
+ if ((sizem = native_member(t, m->size.idx)) == NULL)
+ return -1;
+
+ if (sizem->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)base;
+ else
+ v = base;
+
+ switch (sizem->any.type) {
+ case MRP_TYPE_INT8: *sizep = v->s8; return 0;
+ case MRP_TYPE_UINT8: *sizep = v->u8; return 0;
+ case MRP_TYPE_INT16: *sizep = v->s16; return 0;
+ case MRP_TYPE_UINT16: *sizep = v->u16; return 0;
+ case MRP_TYPE_INT32: *sizep = v->s32; return 0;
+ case MRP_TYPE_UINT32: *sizep = v->u32; return 0;
+ case MRP_TYPE_INT64: *sizep = (size_t)v->s32; return 0;
+ case MRP_TYPE_UINT64: *sizep = (size_t)v->u32; return 0;
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+
+static int guard_offset_and_size(mrp_native_array_t *m, size_t *offsp,
+ size_t *sizep)
+{
+ mrp_native_type_t *t = lookup_type(m->elem.id);
+ mrp_native_member_t *g;
+
+ if (t == NULL)
+ return -1;
+
+ switch (t->id) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_STRING:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ *offsp = 0;
+ *sizep = t->size;
+ return 0;
+
+ default:
+ if ((g = native_member(t, m->size.idx)) == NULL)
+ return -1;
+
+ *offsp = g->any.offs;
+ *sizep = type_size(g->any.type);
+ return 0;
+ }
+}
+
+
+static inline int get_explicit_array_size(void *base, mrp_native_type_t *t,
+ mrp_native_array_t *m)
+{
+ mrp_native_member_t *nelemm;
+ mrp_value_t *v;
+ int n;
+
+ if ((nelemm = native_member(t, m->size.idx)) == NULL)
+ return -1;
+ if (nelemm->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)(base + nelemm->any.offs);
+ else
+ v = base + nelemm->any.offs;
+
+ switch (nelemm->any.type) {
+ case MRP_TYPE_INT8: n = v->s8; break;
+ case MRP_TYPE_UINT8: n = v->u8; break;
+ case MRP_TYPE_INT16: n = v->s16; break;
+ case MRP_TYPE_UINT16: n = v->u16; break;
+ case MRP_TYPE_INT32: n = v->s32; break;
+ case MRP_TYPE_UINT32: n = v->u32; break;
+ case MRP_TYPE_INT64: n = (int)v->s64; break;
+ case MRP_TYPE_UINT64: n = (int)v->u64; break;
+
+ case MRP_TYPE_INT: n = (int) v->i; break;
+ case MRP_TYPE_UINT: n = (unsigned int) v->ui; break;
+ case MRP_TYPE_SHORT: n = (short) v->si; break;
+ case MRP_TYPE_USHORT: n = (unsigned short)v->usi; break;
+ case MRP_TYPE_SIZET: n = (size_t) v->sz; break;
+ case MRP_TYPE_SSIZET: n = (ssize_t) v->ssz; break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ return n;
+}
+
+
+static inline int get_guarded_array_size(void *arrp, mrp_native_array_t *m)
+{
+ mrp_value_t *guard;
+ size_t goffs, gsize, esize;
+ int n;
+
+ if ((esize = type_size(m->elem.id)) == 0)
+ return -1;
+
+ if (guard_offset_and_size(m, &goffs, &gsize) < 0)
+ return -1;
+
+ guard = &m->sentinel;
+
+ for (n = 0; memcmp(arrp + n * esize + goffs, guard, gsize); n++)
+ ;
+ return n;
+}
+
+
+static int get_array_size(void *base, mrp_native_type_t *t, void *arrp,
+ mrp_native_array_t *m, size_t *nelemp,
+ size_t *esizep)
+{
+ int n;
+
+ if ((*esizep = type_size(m->elem.id)) == 0)
+ return -1;
+
+ switch (m->kind) {
+ case MRP_ARRAY_SIZE_FIXED:
+ *nelemp = m->size.nelem;
+ return 0;
+
+ case MRP_ARRAY_SIZE_EXPLICIT:
+ if ((n = get_explicit_array_size(base, t, m)) < 0)
+ return -1;
+
+ *nelemp = (size_t)n;
+ return 0;
+
+ case MRP_ARRAY_SIZE_GUARDED:
+ if ((n = get_guarded_array_size(arrp, m)) < 0)
+ return -1;
+
+ *nelemp = (size_t)n;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+
+static int terminate_guarded_array(void *elem, mrp_native_array_t *m,
+ mrp_native_type_t *mt)
+{
+ mrp_native_member_t *g;
+
+ if (m->elem.id <= MRP_TYPE_STRING)
+ memcpy(elem, &m->sentinel, mt->size);
+ else if (m->elem.id > MRP_TYPE_STRUCT) {
+ if ((g = native_member(mt, m->size.idx)) == NULL)
+ return -1;
+
+ memcpy(elem + g->any.offs, &m->sentinel, type_size(g->any.type));
+ }
+
+ return 0;
+}
+
+
+static int encode_array(mrp_tlv_t *tlv, void *arrp, mrp_native_array_t *m,
+ size_t nelem, size_t elem_size, mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *t;
+ mrp_value_t *v;
+ void *elem;
+ size_t i;
+
+ if (mrp_tlv_push_uint32(tlv, TAG_ARRAY, map_type(m->elem.id, idmap)) < 0)
+ return -1;
+
+ if (mrp_tlv_push_uint32(tlv, TAG_NELEM, nelem) < 0)
+ return -1;
+
+ if ((t = lookup_type(m->elem.id)) == NULL)
+ return -1;
+
+ for (i = 0, elem = arrp; i < nelem; i++, elem += elem_size) {
+ v = elem;
+
+ switch (t->id) {
+ case MRP_TYPE_STRING:
+ v = *(void **)elem;
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (encode_basic(tlv, t->id, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ return -1;
+
+ default:
+ /* an MRP_TYPE_STRUCT */
+ if (encode_struct(tlv, elem, t, idmap) < 0)
+ return -1;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static int encode_struct(mrp_tlv_t *tlv, void *data, mrp_native_type_t *t,
+ mrp_typemap_t *idmap)
+{
+ mrp_native_member_t *m;
+ mrp_native_type_t *mt;
+ mrp_value_t *v;
+ uint32_t idx;
+ size_t size, nelem;
+
+ if (t == NULL)
+ return -1;
+
+ if (mrp_tlv_push_uint32(tlv, TAG_STRUCT, map_type(t->id, idmap)) < 0)
+ return -1;
+
+ for (idx = 0, m = t->members; idx < t->nmember; idx++, m++) {
+ if (mrp_tlv_push_uint32(tlv, TAG_MEMBER, idx) < 0)
+ return -1;
+
+ if (m->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)(data + m->any.offs);
+ else
+ v = data + m->any.offs;
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_STRING:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (encode_basic(tlv, m->any.type, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ if (get_blob_size(data, t, &m->blob, &size) < 0)
+ return -1;
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ if (get_array_size(data, t, v->ptr, &m->array, &nelem, &size) < 0)
+ return -1;
+ if (encode_array(tlv, v->ptr, &m->array, nelem,
+ size, idmap) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRUCT:
+ if ((mt = lookup_type(m->strct.data_type.id)) == NULL)
+ return -1;
+ if (encode_struct(tlv, v->ptr, mt, idmap) < 0)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_encode_native(void *data, uint32_t id, size_t reserve, void **bufp,
+ size_t *sizep, mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *t = lookup_type(id);
+ mrp_tlv_t tlv;
+
+ *bufp = NULL;
+ *sizep = 0;
+
+ if (t == NULL)
+ return -1;
+
+ if (mrp_tlv_setup_write(&tlv, reserve + 4096) < 0)
+ return -1;
+
+ if (reserve > 0)
+ if (mrp_tlv_reserve(&tlv, reserve, 1) == NULL)
+ goto fail;
+
+ if (encode_struct(&tlv, data, t, idmap) < 0)
+ goto fail;
+
+ mrp_tlv_trim(&tlv);
+ mrp_tlv_steal(&tlv, bufp, sizep);
+
+ return 0;
+
+ fail:
+ mrp_tlv_cleanup(&tlv);
+ return -1;
+}
+
+
+static void *allocate_indirect(mrp_list_hook_t **chunks, mrp_value_t *v,
+ mrp_native_member_t *m, mrp_typemap_t *idmap)
+{
+ size_t size;
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int8_t)));
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int16_t)));
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int32_t)));
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ return (v->ptr = alloc_chunk(chunks, sizeof(int64_t)));
+ case MRP_TYPE_FLOAT:
+ return (v->ptr = alloc_chunk(chunks, sizeof(float)));
+ case MRP_TYPE_DOUBLE:
+ return (v->ptr = alloc_chunk(chunks, sizeof(double)));
+ case MRP_TYPE_BOOL:
+ return (v->ptr = alloc_chunk(chunks, sizeof(bool)));
+ case MRP_TYPE_STRING:
+ return v; /* will be allocated by TLV pull */
+ case MRP_TYPE_BLOB:
+ return v; /* will be allocated by decoder */
+ case MRP_TYPE_ARRAY:
+ return v; /* will be allocated by decoder */
+ case MRP_TYPE_STRUCT:
+ if ((size = type_size(mapped_type(m->strct.data_type.id, idmap))) == 0)
+ return NULL;
+ return (v->ptr = alloc_chunk(chunks, size));
+ default:
+ return NULL;
+ }
+}
+
+
+static void *alloc_str_chunk(size_t size, void *chunksp)
+{
+ return alloc_chunk((mrp_list_hook_t **)chunksp, size);
+}
+
+
+static int decode_basic(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ mrp_type_t type, mrp_value_t *v)
+{
+ int32_t i;
+ uint32_t u;
+
+ switch (type) {
+ case MRP_TYPE_INT8: return mrp_tlv_pull_int8 (tlv, TAG_NONE, &v->s8);
+ case MRP_TYPE_UINT8: return mrp_tlv_pull_uint8 (tlv, TAG_NONE, &v->u8);
+ case MRP_TYPE_INT16: return mrp_tlv_pull_int16 (tlv, TAG_NONE, &v->s16);
+ case MRP_TYPE_UINT16: return mrp_tlv_pull_uint16(tlv, TAG_NONE, &v->u16);
+ case MRP_TYPE_INT32: return mrp_tlv_pull_int32 (tlv, TAG_NONE, &v->s32);
+ case MRP_TYPE_UINT32: return mrp_tlv_pull_uint32(tlv, TAG_NONE, &v->u32);
+ case MRP_TYPE_INT64: return mrp_tlv_pull_int64 (tlv, TAG_NONE, &v->s64);
+ case MRP_TYPE_UINT64: return mrp_tlv_pull_uint64(tlv, TAG_NONE, &v->u64);
+ case MRP_TYPE_FLOAT: return mrp_tlv_pull_float (tlv, TAG_NONE, &v->flt);
+ case MRP_TYPE_DOUBLE: return mrp_tlv_pull_double(tlv, TAG_NONE, &v->dbl);
+ case MRP_TYPE_BOOL: return mrp_tlv_pull_bool (tlv, TAG_NONE, &v->bln);
+ case MRP_TYPE_STRING:
+ return mrp_tlv_pull_string(tlv, TAG_NONE, &v->strp,
+ -1, alloc_str_chunk, chunks);
+
+ case MRP_TYPE_INT:
+ if (mrp_tlv_pull_int32(tlv, TAG_NONE, &i) < 0)
+ return -1;
+ v->i = (int)i;
+ return 0;
+
+ case MRP_TYPE_UINT:
+ if (mrp_tlv_pull_uint32(tlv, TAG_NONE, &u) < 0)
+ return -1;
+ v->ui = (unsigned int)u;
+ return 0;
+
+ case MRP_TYPE_SHORT:
+ if (mrp_tlv_pull_int32(tlv, TAG_NONE, &i) < 0)
+ return -1;
+ v->si = (short)i;
+ return 0;
+
+ case MRP_TYPE_USHORT:
+ if (mrp_tlv_pull_uint32(tlv, TAG_NONE, &u) < 0)
+ return -1;
+ v->usi = (unsigned short)u;
+ return 0;
+
+ case MRP_TYPE_SIZET:
+ if (mrp_tlv_pull_uint32(tlv, TAG_NONE, &u) < 0)
+ return -1;
+ v->sz = (size_t)u;
+ return 0;
+
+ case MRP_TYPE_SSIZET:
+ if (mrp_tlv_pull_int32(tlv, TAG_NONE, &i) < 0)
+ return -1;
+ v->ssz = (ssize_t)i;
+ return 0;
+
+ default:
+ return -1;
+ }
+}
+
+
+static int decode_array(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ void **arrp, mrp_native_array_t *m,
+ void *data, mrp_native_type_t *t,
+ mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *mt;
+ mrp_value_t *v;
+ void *elem, *base;
+ size_t elem_size, i;
+ uint32_t id, nelem;
+ int n, guard;
+
+ if (mrp_tlv_pull_uint32(tlv, TAG_ARRAY, &id) < 0)
+ return -1;
+
+ if ((id = mapped_type(id, idmap)) != m->elem.id)
+ return -1;
+
+ if ((elem_size = type_size(id)) == 0)
+ return -1;
+
+ if (mrp_tlv_pull_uint32(tlv, TAG_NELEM, &nelem) < 0)
+ return -1;
+
+ if ((mt = lookup_type(m->elem.id)) == NULL)
+ return -1;
+
+ switch (m->kind) {
+ case MRP_ARRAY_SIZE_EXPLICIT:
+ if ((n = get_explicit_array_size(data, t, m)) < 0)
+ return -1;
+ guard = 0;
+ break;
+ case MRP_ARRAY_SIZE_FIXED:
+ n = m->size.nelem;
+ guard = 0;
+ break;
+ case MRP_ARRAY_SIZE_GUARDED:
+ n = nelem;
+ guard = 1;
+ break;
+ default:
+ return -1;
+ }
+
+ if (n != (int)nelem)
+ return -1;
+
+ switch (m->layout) {
+ case MRP_LAYOUT_INLINED:
+ base = (void *)arrp;
+ break;
+ case MRP_LAYOUT_INDIRECT:
+ case MRP_LAYOUT_DEFAULT:
+ if ((*arrp = alloc_chunk(chunks, (nelem + guard) * elem_size)) == NULL)
+ return (nelem + guard) ? -1 : 0;
+ base = *arrp;
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 0, elem = base; i < nelem; i++, elem += elem_size) {
+ v = elem;
+
+ switch (mt->id) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_STRING:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (decode_basic(tlv, chunks, mt->id, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ return -1;
+
+ default:
+ /* an MRP_TYPE_STRUCT */
+ if (decode_struct(tlv, chunks, &elem, &id, idmap) < 0)
+ return -1;
+ }
+ }
+
+ if (guard) {
+ if (terminate_guarded_array(elem, m, mt) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int decode_struct(mrp_tlv_t *tlv, mrp_list_hook_t **chunks,
+ void **datap, uint32_t *idp, mrp_typemap_t *idmap)
+{
+ mrp_native_type_t *t;
+ mrp_native_member_t *m;
+ mrp_value_t *v;
+ char *str, **strp;
+ size_t max, i;
+ uint32_t idx, id;
+
+ if (datap == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (mrp_tlv_pull_uint32(tlv, TAG_STRUCT, &id) < 0)
+ return -1;
+ else
+ id = mapped_type(id, idmap);
+
+ if (*idp) {
+ if (*idp != id) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+ else
+ *idp = id;
+
+ if ((t = lookup_type(id)) == NULL)
+ return -1;
+
+ if (*datap == NULL)
+ if ((*datap = alloc_chunk(chunks, t->size)) == NULL)
+ return -1;
+
+ for (i = 0, m = t->members; i < t->nmember; i++, m++) {
+ if (mrp_tlv_pull_uint32(tlv, TAG_MEMBER, &idx) < 0)
+ return -1;
+
+ v = *datap + m->any.offs;
+
+ if (m->any.layout == MRP_LAYOUT_INDIRECT) {
+ if ((v = allocate_indirect(chunks, v, m, idmap)) == NULL)
+ return -1;
+ }
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (decode_basic(tlv, chunks, m->any.type, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRING:
+ if (m->any.layout == MRP_LAYOUT_INLINED) {
+ max = m->str.size;
+ str = v->str;
+ strp = &str;
+ }
+ else {
+ max = (size_t)-1;
+ strp = &v->strp;
+ }
+ if (mrp_tlv_pull_string(tlv, TAG_NONE, strp, max,
+ alloc_str_chunk, chunks) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ return -1;
+
+ case MRP_TYPE_ARRAY:
+ if (decode_array(tlv, chunks, &v->ptr, &m->array,
+ *datap, t, idmap) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRUCT:
+ id = m->strct.data_type.id;
+ if (decode_struct(tlv, chunks, &v->ptr, &id, idmap) < 0)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_decode_native(void **bufp, size_t *sizep, void **datap, uint32_t *idp,
+ mrp_typemap_t *idmap)
+{
+ mrp_tlv_t tlv;
+ mrp_list_hook_t *chunks;
+ void *data;
+ size_t diff;
+
+ chunks = NULL;
+ data = NULL;
+
+ if (mrp_tlv_setup_read(&tlv, *bufp, *sizep) < 0)
+ return -1;
+
+ if (decode_struct(&tlv, &chunks, &data, idp, idmap) == 0) {
+ diff = mrp_tlv_offset(&tlv);
+
+ if (diff <= *sizep) {
+ *bufp += diff;
+ *sizep -= diff;
+ *datap = data;
+
+ return 0;
+ }
+ }
+
+ free_chunks(chunks);
+
+ return -1;
+}
+
+
+void mrp_free_native(void *data, uint32_t id)
+{
+ mrp_list_hook_t *chunks;
+
+ MRP_UNUSED(id);
+
+ if (data != NULL) {
+ chunks = ((void *)data) - MRP_OFFSET(chunk_t, data);
+ free_chunks(chunks);
+ }
+}
+
+
+#define INDENT(_level, _fmt) "%*.*s"_fmt, _level * 4, _level * 4, ""
+
+#define PRINT(_l, _p, _size, fmt, args...) do { \
+ ssize_t _n; \
+ _n = snprintf((_p), (_size), INDENT(_l, fmt), ## args); \
+ if (_n >= (ssize_t)(_size)) \
+ return -1; \
+ (_p) += _n; \
+ (_size) -= _n; \
+ } while (0)
+
+
+static int print_basic(int level, char **bufp, size_t *sizep, int type,
+ const char *name, mrp_value_t *v)
+{
+#define NAME name ? name : "", name ? " = " : ""
+ char *p = *bufp;
+ size_t size = *sizep;
+
+ if (type >= MRP_TYPE_BLOB)
+ return -1;
+
+ switch (type) {
+ case MRP_TYPE_INT8:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->s8);
+ break;
+ case MRP_TYPE_UINT8:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->u8);
+ break;
+
+ case MRP_TYPE_INT16:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->s16);
+ break;
+ case MRP_TYPE_UINT16:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->u16);
+ break;
+
+ case MRP_TYPE_INT32:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->s32);
+ break;
+ case MRP_TYPE_UINT32:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->u32);
+ break;
+
+ case MRP_TYPE_INT64:
+ PRINT(level, p, size, "%s%s%lld\n", NAME, (long long)v->s64);
+ break;
+ case MRP_TYPE_UINT64:
+ PRINT(level, p, size, "%s%s%llu\n", NAME,
+ (unsigned long long)v->s64);
+ break;
+
+ case MRP_TYPE_FLOAT:
+ PRINT(level, p, size, "%s%s%f\n", NAME, v->flt);
+ break;
+ case MRP_TYPE_DOUBLE:
+ PRINT(level, p, size, "%s%s%f\n", NAME, v->dbl);
+ break;
+
+ case MRP_TYPE_BOOL:
+ PRINT(level, p, size, "%s%s%s\n", NAME,
+ v->bln ? "<true>" : "<false>");
+ break;
+
+ case MRP_TYPE_STRING:
+ PRINT(level, p, size, "%s%s%s\n", NAME,
+ v->str ? v->str : "<null>");
+ break;
+
+ case MRP_TYPE_INT:
+ PRINT(level, p, size, "%s%s%d\n", NAME, v->i);
+ break;
+ case MRP_TYPE_UINT:
+ PRINT(level, p, size, "%s%s%u\n", NAME, v->ui);
+ break;
+
+ case MRP_TYPE_SHORT:
+ PRINT(level, p, size, "%s%s%hd\n", NAME, v->si);
+ break;
+ case MRP_TYPE_USHORT:
+ PRINT(level, p, size, "%s%s%hu\n", NAME, v->usi);
+ break;
+
+ case MRP_TYPE_SIZET:
+ PRINT(level, p, size, "%s%s%zu\n", NAME, v->sz);
+ break;
+ case MRP_TYPE_SSIZET:
+ PRINT(level, p, size, "%s%s%zd\n", NAME, v->ssz);
+ break;
+
+ default:
+ PRINT(level, p, size, "%s%s%s\n", NAME, "<unknown>");
+ }
+
+ *bufp = p;
+ *sizep = size;
+
+ return 0;
+
+#undef NAME
+}
+
+
+static int print_array(char **bufp, size_t *sizep, int level,
+ void *arrp, mrp_native_array_t *a, size_t nelem,
+ size_t elem_size)
+{
+ mrp_native_type_t *et;
+ mrp_value_t *v;
+ void *elem;
+ size_t i;
+ char *p;
+ size_t size;
+
+ p = *bufp;
+ size = *sizep;
+
+ if ((et = lookup_type(a->elem.id)) == NULL)
+ return -1;
+
+ PRINT(level, p, size, "%s = [%s", a->name, nelem == 0 ? "]" : "\n");
+ level++;
+
+ for (i = 0, elem = arrp; i < nelem; i++, elem += elem_size) {
+ v = elem;
+
+ switch (et->id) {
+ case MRP_TYPE_STRING:
+ v = *(void **)elem;
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ if (print_basic(level, &p, &size, et->id, NULL, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB:
+ PRINT(level, p, size, "<blob>\n");
+ break;
+
+ case MRP_TYPE_ARRAY:
+ return -1;
+
+ default:
+ /* an MRP_TYPE_STRUCT */
+ if (print_struct(&p, &size, level, elem, et) < 0)
+ return -1;
+ break;
+ }
+ }
+
+ level--;
+ PRINT(level, p, size, "%s\n", nelem == 0 ? "" : "]");
+
+ *bufp = p;
+ *sizep = size;
+
+ return 0;
+}
+
+
+static int print_struct(char **bufp, size_t *sizep, int level,
+ void *data, mrp_native_type_t *t)
+{
+ mrp_native_member_t *m;
+ mrp_native_type_t *mt;
+ mrp_value_t *v;
+ uint32_t idx;
+ size_t esize, nelem;
+ char *p;
+ size_t size;
+
+ if (data == NULL) {
+ **bufp = '\0';
+
+ return 0;
+ }
+
+ if (t == NULL)
+ return -1;
+
+ p = *bufp;
+ size = *sizep;
+ PRINT(level, p, size, "{\n");
+ level++;
+
+ for (idx = 0, m = t->members; idx < t->nmember; idx++, m++) {
+ if (m->any.layout == MRP_LAYOUT_INDIRECT)
+ v = *(void **)(data + m->any.offs);
+ else
+ v = data + m->any.offs;
+
+ switch (m->any.type) {
+ case MRP_TYPE_INT8:
+ case MRP_TYPE_UINT8:
+ case MRP_TYPE_INT16:
+ case MRP_TYPE_UINT16:
+ case MRP_TYPE_INT32:
+ case MRP_TYPE_UINT32:
+ case MRP_TYPE_INT64:
+ case MRP_TYPE_UINT64:
+ case MRP_TYPE_FLOAT:
+ case MRP_TYPE_DOUBLE:
+ case MRP_TYPE_BOOL:
+ case MRP_TYPE_INT:
+ case MRP_TYPE_UINT:
+ case MRP_TYPE_SHORT:
+ case MRP_TYPE_USHORT:
+ case MRP_TYPE_SIZET:
+ case MRP_TYPE_SSIZET:
+ case MRP_TYPE_STRING:
+ if (print_basic(level, &p, &size, m->any.type, m->any.name, v) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_BLOB: /* XXX TODO implement blobs */
+ PRINT(level, p, size, "%s = <blob>\n", m->any.name);
+ break;
+
+ case MRP_TYPE_ARRAY:
+ if (get_array_size(data, t, v->ptr, &m->array, &nelem, &esize) < 0)
+ return -1;
+ if (print_array(&p, &size, level, v->ptr, &m->array,
+ nelem, esize) < 0)
+ return -1;
+ break;
+
+ case MRP_TYPE_STRUCT:
+ if ((mt = lookup_type(m->strct.data_type.id)) == NULL)
+ return -1;
+ if (print_struct(&p, &size, level, v->ptr, mt) < 0)
+ return -1;
+ break;
+
+ default:
+ return -1;
+ }
+ }
+
+ level--;
+ PRINT(level, p, size, "}\n");
+
+ *bufp = p;
+ *sizep = size;
+
+ return 0;
+}
+
+
+ssize_t mrp_print_native(char *buf, size_t size, void *data, uint32_t id)
+{
+ mrp_native_type_t *t;
+ char *p;
+
+ p = buf;
+
+ if (id < MRP_TYPE_STRUCT || (t = lookup_type(id)) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (print_struct(&p, &size, 0, data, t) == 0)
+ return (ssize_t)(p - buf);
+ else
+ return -1;
+}
+
+
+static inline size_t chunk_size(size_t size)
+{
+ return MRP_OFFSET(chunk_t, data[size]);
+}
+
+
+static void *alloc_chunk(mrp_list_hook_t **chunks, size_t size)
+{
+ chunk_t *chunk;
+
+ if (size == 0)
+ return NULL;
+
+ if (*chunks == NULL) {
+ if ((*chunks = mrp_allocz(sizeof(*chunks))) == NULL)
+ return NULL;
+ else
+ mrp_list_init(*chunks);
+ }
+
+ if ((chunk = mrp_allocz(chunk_size(size))) == NULL)
+ return NULL;
+
+ mrp_list_init(&chunk->hook);
+ mrp_list_append(*chunks, &chunk->hook);
+
+ return &chunk->data[0];
+}
+
+
+static void free_chunks(mrp_list_hook_t *chunks)
+{
+ mrp_list_hook_t *p, *n;
+
+ if (chunks != NULL) {
+ mrp_list_foreach(chunks, p, n) {
+ mrp_list_delete(p);
+
+ if (p != chunks)
+ mrp_free(p);
+ }
+
+ mrp_free(chunks);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_COMMON_NATIVE_TYPES_H__
+#define __MURPHY_COMMON_NATIVE_TYPES_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_INVALID_TYPE ((uint32_t)-1)
+
+
+/**
+ * pre-defined native type ids
+ */
+
+typedef enum {
+ MRP_TYPE_UNKNOWN = 0,
+ MRP_TYPE_INT8,
+ MRP_TYPE_UINT8,
+ MRP_TYPE_INT16,
+ MRP_TYPE_UINT16,
+ MRP_TYPE_INT32,
+ MRP_TYPE_UINT32,
+ MRP_TYPE_INT64,
+ MRP_TYPE_UINT64,
+ MRP_TYPE_FLOAT,
+ MRP_TYPE_DOUBLE,
+ MRP_TYPE_BOOL,
+ MRP_TYPE_INT,
+ MRP_TYPE_UINT,
+ MRP_TYPE_SHORT,
+ MRP_TYPE_USHORT,
+ MRP_TYPE_SIZET,
+ MRP_TYPE_SSIZET,
+ MRP_TYPE_STRING,
+ MRP_TYPE_BLOB,
+ MRP_TYPE_ARRAY,
+ MRP_TYPE_STRUCT,
+ MRP_TYPE_MAX
+} mrp_type_t;
+
+
+/**
+ * data type values
+ */
+
+typedef union {
+ int8_t s8;
+ int8_t *s8p;
+ uint8_t u8;
+ uint8_t *u8p;
+ int16_t s16;
+ int16_t *s16p;
+ uint16_t u16;
+ uint16_t *u16p;
+ int32_t s32;
+ int32_t *s32p;
+ uint32_t u32;
+ uint32_t *u32p;
+ int64_t s64;
+ int64_t *s64p;
+ uint64_t u64;
+ uint64_t *u64p;
+ float flt;
+ float *fltp;
+ double dbl;
+ double *dblp;
+ bool bln;
+ bool *blnp;
+ void *blb;
+ char str[0];
+ char *strp;
+ int i;
+ int *ip;
+ unsigned int ui;
+ unsigned int *uip;
+ short si;
+ short *sip;
+ unsigned short usi;
+ unsigned short *usip;
+ size_t sz;
+ size_t *szp;
+ ssize_t ssz;
+ ssize_t *sszp;
+ void *ptr;
+ void **ptrp;
+} mrp_value_t;
+
+
+/**
+ * type id map (for transport-specific mapping of type ids)
+ */
+
+typedef struct {
+ uint32_t type_id; /* native type id */
+ uint32_t mapped; /* mapped type id */
+} mrp_typemap_t;
+
+
+/** Macro to initialize a typemap entry. */
+#define MRP_TYPEMAP(_mapped_id, _type_id) \
+ { .type_id = _type_id, .mapped = _mapped_id }
+
+/** Macro to set a typemap termination entry. */
+#define MRP_TYPEMAP_END \
+ { MRP_INVALID_TYPE, MRP_INVALID_TYPE }
+
+/**
+ * type and member descriptors
+ */
+
+typedef enum {
+ MRP_LAYOUT_DEFAULT = 0, /* default, type-specific layout */
+ MRP_LAYOUT_INLINED, /* inlined/embedded layout */
+ MRP_LAYOUT_INDIRECT, /* indirect layout */
+} mrp_layout_t;
+
+#define MRP_NATIVE_COMMON_FIELDS /* fields common to all members */ \
+ char *name; /* name of this member */ \
+ uint32_t type; /* type id of this member */ \
+ size_t offs; /* offset from base pointer */ \
+ mrp_layout_t layout /* member layout */
+
+typedef struct {
+ MRP_NATIVE_COMMON_FIELDS; /* common fields to all members */
+} mrp_native_any_t;
+
+typedef struct { /* a blob member */
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ union { /* size-indicating member */
+ char *name; /* name */
+ uint32_t idx; /* or index */
+ } size;
+} mrp_native_blob_t;
+
+typedef enum {
+ MRP_ARRAY_SIZE_EXPLICIT, /* explicitly sized array */
+ MRP_ARRAY_SIZE_GUARDED, /* sentinel-guarded array */
+ MRP_ARRAY_SIZE_FIXED, /* a fixed size array */
+} mrp_array_size_t;
+
+typedef struct {
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ size_t size; /* inlined buffer size */
+} mrp_native_string_t;
+
+typedef struct { /* an array member */
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ mrp_array_size_t kind; /* which kind of array */
+ union { /* contained element type */
+ char *name; /* name */
+ uint32_t id; /* or type id */
+ } elem;
+ union { /* size or guard member */
+ char *name; /* name */
+ uint32_t idx; /* or index */
+ size_t nelem; /* or number of elements */
+ } size;
+ mrp_value_t sentinel; /* sentinel value, if guarded */
+} mrp_native_array_t;
+
+typedef struct { /* member of type struct */
+ MRP_NATIVE_COMMON_FIELDS; /* common member fields */
+ union { /* struct type */
+ char *name; /* name */
+ uint32_t id; /* or type id */
+ } data_type;
+} mrp_native_struct_t;
+
+typedef union {
+ mrp_native_any_t any;
+ mrp_native_string_t str;
+ mrp_native_blob_t blob;
+ mrp_native_array_t array;
+ mrp_native_struct_t strct;
+} mrp_native_member_t;
+
+typedef struct {
+ char *name; /* name of this type */
+ uint32_t id; /* assigned id for this type */
+ size_t size; /* size of this type */
+ mrp_native_member_t *members; /* members of this type if any */
+ size_t nmember; /* number of members */
+ mrp_list_hook_t hook; /* to list of registered types */
+} mrp_native_type_t;
+
+
+/** Helper macro to initialize native member fields. */
+#define __MRP_MEMBER_INIT(_objtype, _member, _type) \
+ .name = #_member, \
+ .type = _type, \
+ .offs = MRP_OFFSET(_objtype, _member)
+
+/** Helper macro to declare a native member with a given type an layout. */
+#define __MRP_MEMBER(_objtype, _type, _member, _layout) \
+ { \
+ .any = { \
+ __MRP_MEMBER_INIT(_objtype, _member, _type), \
+ .layout = MRP_LAYOUT_##_layout, \
+ } \
+ }
+
+/** Declare an indirect string member of the native type. */
+#define MRP_INDIRECT_STRING(_objtype, _member, _size) \
+ __MRP_MEMBER(_objtype, _member, MRP_TYPE_STRING, INDIRECT)
+
+/** Declare an inlined string member of the native type. */
+#define MRP_INLINED_STRING(_objtype, _member, _size) \
+ { \
+ .str = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_STRING), \
+ .layout = MRP_LAYOUT_INLINED, \
+ .size = _size, \
+ } \
+ }
+
+/** By default declare a string members indirect. */
+#define MRP_DEFAULT_STRING(_objtype, _member, _size) \
+ __MRP_MEMBER(_objtype, MRP_TYPE_STRING, _member, INDIRECT)
+
+/** Declare an explicitly sized array member of the native typet. */
+#define MRP_SIZED_ARRAY(_objtype, _member, _layout, _type, _size) \
+ { \
+ .array = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY), \
+ .layout = MRP_LAYOUT_##_layout, \
+ .kind = MRP_ARRAY_SIZE_EXPLICIT, \
+ .elem = { .name = #_type, }, \
+ .size = { .name = #_size, }, \
+ } \
+ }
+
+/** Declare a sentinel-guarded array member of the native type. */
+#define MRP_GUARDED_ARRAY(_objtype, _member, _layout, _type, _guard, \
+ ...) \
+ { \
+ .array = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY), \
+ .layout = MRP_LAYOUT_##_layout, \
+ .kind = MRP_ARRAY_SIZE_GUARDED, \
+ .elem = { .name = #_type, }, \
+ .size = { .name = #_guard, }, \
+ .sentinel = { __VA_ARGS__ }, \
+ } \
+ }
+
+/** Declare a fixed array member of the native type. */
+#define MRP_FIXED_ARRAY(_objtype, _member, _layout, _type) \
+ { \
+ .array = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_ARRAY), \
+ .layout = MRP_LAYOUT_##_layout, \
+ .kind = MRP_ARRAY_SIZE_FIXED, \
+ .elem = { .name = #_type, }, \
+ .size = { \
+ .nelem = MRP_ARRAY_SIZE(((_objtype *)0x0)->_member) \
+ }, \
+ } \
+ }
+
+/** Declare a struct member of the native type. */
+#define MRP_STRUCT(_objtype, _member, _layout, _type) \
+ { \
+ .strct = { \
+ __MRP_MEMBER_INIT(_objtype, _member, MRP_TYPE_STRUCT), \
+ .data_type = { .name = #_type }, \
+ } \
+ }
+
+/** Macros for declaring basic members of the native type. */
+#define MRP_INT8(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT8 , _m, _l)
+#define MRP_UINT8(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT8 , _m, _l)
+#define MRP_INT16(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT16 , _m, _l)
+#define MRP_UINT16(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT16, _m, _l)
+#define MRP_INT32(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT32 , _m, _l)
+#define MRP_UINT32(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT32, _m, _l)
+#define MRP_INT64(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT64 , _m, _l)
+#define MRP_UINT64(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT64, _m, _l)
+#define MRP_FLOAT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_FLOAT , _m, _l)
+#define MRP_DOUBLE(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_DOUBLE, _m, _l)
+#define MRP_BOOL(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_BOOL , _m, _l)
+
+#define MRP_INT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_INT , _m, _l)
+#define MRP_UINT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_UINT , _m, _l)
+#define MRP_SHORT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_SHORT , _m, _l)
+#define MRP_USHORT(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_USHORT, _m, _l)
+#define MRP_SIZET(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_SIZET , _m, _l)
+#define MRP_SSIZET(_ot, _m, _l) __MRP_MEMBER(_ot, MRP_TYPE_SSIZET, _m, _l)
+
+/** Macro for declaring string members of the native type. */
+#define MRP_STRING(_objtype, _member, _layout) \
+ MRP_##_layout##_STRING(_objtype, _member, \
+ sizeof(((_objtype *)0x0)->_member))
+
+/** Macro for declaring array members of the native type. */
+#define MRP_ARRAY(_objtype, _member, _layout, _kind, ...) \
+ MRP_##_kind##_ARRAY(_objtype, _member, _layout, __VA_ARGS__)
+
+/** Macro to declare a native type. */
+#define MRP_NATIVE_TYPE(_var, _type, ...) \
+ mrp_native_member_t _var##_members[] = { \
+ __VA_ARGS__ \
+ }; \
+ mrp_native_type_t _var = { \
+ .id = -1, \
+ .name = #_type, \
+ .size = sizeof(_type), \
+ .members = _var##_members, \
+ .nmember = MRP_ARRAY_SIZE(_var##_members), \
+ .hook = { NULL, NULL }, \
+ }
+
+/** Declare and register the given native type. */
+uint32_t mrp_register_native(mrp_native_type_t *type);
+
+/** Look up the type id of the given native type name. */
+uint32_t mrp_native_id(const char *type_name);
+
+/** Encode data of the given native type. */
+int mrp_encode_native(void *data, uint32_t id, size_t reserve, void **bufp,
+ size_t *sizep, mrp_typemap_t *idmap);
+
+/** Decode data of (the given) native type (if specified). */
+int mrp_decode_native(void **bufp, size_t *sizep, void **datap, uint32_t *idp,
+ mrp_typemap_t *idmap);
+
+/** Free data of the given native type, obtained from mrp_decode_native. */
+void mrp_free_native(void *data, uint32_t id);
+
+/** Print data of the given native type. */
+ssize_t mrp_print_native(char *buf, size_t size, void *data, uint32_t id);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_COMMON_NATIVE_TYPES_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/inotify.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <linux/cn_proc.h>
+#include <linux/netlink.h>
+#include <linux/connector.h>
+#include <linux/filter.h>
+
+#include <murphy/common/process.h>
+#include <murphy/common.h>
+
+
+#define MURPHY_PROCESS_INOTIFY_DIR "/var/run/murphy/processes"
+
+struct mrp_pid_watch_s {
+ pid_t pid;
+};
+
+typedef struct {
+ char *path; /* file name token */
+ pid_t pid;
+ char *filename;
+
+ /* either this ... */
+ mrp_process_watch_handler_t process_cb;
+ /* ... or this is set. */
+ mrp_pid_watch_handler_t pid_cb;
+
+ void *userdata;
+ mrp_io_watch_t *inotify_cb;
+} i_watch_t;
+
+typedef struct {
+ mrp_pid_watch_handler_t cb;
+ void *user_data;
+ mrp_pid_watch_t *w; /* identify the client */
+
+ mrp_list_hook_t hook;
+} nl_pid_client_t;
+
+typedef struct {
+ pid_t pid;
+ char pid_s[16]; /* memory for hashing */
+
+ mrp_list_hook_t clients;
+ int n_clients;
+ int busy : 1;
+ int dead : 1;
+} nl_pid_watch_t;
+
+/* murphy pid file directory notify */
+static int dir_fd;
+static int i_n_process_watches;
+
+/* inotify */
+static int i_fd;
+static mrp_htbl_t *i_watches;
+static mrp_io_watch_t *i_wd;
+
+/* netlink process listening */
+static int nl_sock;
+static bool subscribed;
+static mrp_io_watch_t *nl_wd;
+static mrp_htbl_t *nl_watches;
+static int nl_n_pid_watches;
+
+static bool id_ok(const char *id)
+{
+ int i, len;
+ /* restrict the input */
+
+ len = strlen(id);
+
+ for (i = 0; i < len; i++) {
+ bool character, number, special;
+
+ character = ((id[i] >= 'A' && id[i] <= 'Z') ||
+ (id[i] >= 'a' && id[i] <= 'z'));
+
+ number = (id[i] >= '0' && id[i] <= '9');
+
+ special = (id[i] == '-' || id[i] == '_');
+
+ if (!(character || number || special))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static char *path_from_id(const char *id)
+{
+ char buf[PATH_MAX];
+ int ret;
+
+ if (!id || !id_ok(id))
+ return NULL;
+
+ ret = snprintf(buf, PATH_MAX, "%s/%s", MURPHY_PROCESS_INOTIFY_DIR, id);
+
+ if (ret < 0 || ret >= PATH_MAX)
+ return NULL;
+
+ return mrp_strdup(buf);
+}
+
+
+static void htbl_free_nl_watch(void *key, void *object)
+{
+ nl_pid_watch_t *w = (nl_pid_watch_t *) object;
+
+ MRP_UNUSED(key);
+
+ if (!w->busy)
+ mrp_free(w);
+ else
+ w->dead = TRUE;
+}
+
+
+static void htbl_free_i_watch(void *key, void *object)
+{
+ i_watch_t *w = (i_watch_t *) object;
+
+ MRP_UNUSED(key);
+
+ mrp_free(w->path);
+ mrp_free(w->filename);
+ mrp_free(w);
+}
+
+
+static int initialize_dir()
+{
+ /* TODO: check if the directory is present; if not, create it */
+
+ return 0;
+}
+
+
+static void process_change(mrp_io_watch_t *wd, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ struct inotify_event *is;
+ int bufsize = sizeof(struct inotify_event) + PATH_MAX + 1;
+ char buf[bufsize];
+ i_watch_t *w;
+ FILE *f;
+
+ MRP_UNUSED(wd);
+ MRP_UNUSED(user_data);
+
+ if (!i_watches)
+ return;
+
+ if (events & MRP_IO_EVENT_IN) {
+ int read_bytes;
+ int processed_bytes = 0;
+
+ read_bytes = read(fd, buf, bufsize - 1);
+
+ if (read_bytes < 0) {
+ mrp_log_error("Failed to read event from inotify: %s",
+ strerror(errno));
+ return;
+ }
+
+ buf[read_bytes] = '\0';
+
+ while (processed_bytes < read_bytes) {
+ char *filename = NULL;
+
+ /* the kernel doesn't allow to read incomplete events */
+ is = (struct inotify_event *) (buf + processed_bytes);
+
+ processed_bytes += sizeof(struct inotify_event) + is->len;
+
+ if (is->len == 0) {
+ /* no file name */
+ continue;
+ }
+
+ if (is->wd != dir_fd) {
+ /* wrong descriptor? */
+ continue;
+ }
+
+ filename = path_from_id(is->name);
+
+ if (!filename)
+ continue;
+
+ w = (i_watch_t *) mrp_htbl_lookup(i_watches, filename);
+
+ if (w) {
+ f = fopen(filename, "r");
+
+ if (f) {
+ fclose(f);
+ mrp_log_info("Received inotify event for %s, READY", w->path);
+ w->process_cb(w->path, MRP_PROCESS_STATE_READY, w->userdata);
+ }
+ else {
+ mrp_log_info("Received inotify event for %s, NOT READY", w->path);
+ w->process_cb(w->path, MRP_PROCESS_STATE_NOT_READY, w->userdata);
+ }
+ }
+ mrp_free(filename);
+ }
+ }
+}
+
+
+static int send_proc_cmd(enum proc_cn_mcast_op cmd)
+{
+ int data_size = sizeof(enum proc_cn_mcast_op);
+
+ /* connector message size */
+ int cn_size = sizeof(struct cn_msg);
+
+ /* total size of bytes we need */
+ int message_size = NLMSG_SPACE(cn_size + data_size);
+
+ /* aligned size */
+ int payload_size = NLMSG_LENGTH(message_size - sizeof(struct nlmsghdr));
+
+ /* helper pointers */
+ struct nlmsghdr *nl;
+ struct cn_msg *cn;
+
+ /* message data */
+ char buf[message_size];
+
+ if (nl_sock <= 0) {
+ mrp_log_error("invalid netlink socket %d", nl_sock);
+ return -1;
+ }
+
+ /* structs point to the aligned memory */
+ nl = (struct nlmsghdr *) buf;
+ cn = (struct cn_msg *) NLMSG_DATA(nl);
+
+ /* fill the structures */
+ nl->nlmsg_len = payload_size;
+ nl->nlmsg_seq = 0;
+ nl->nlmsg_pid = getpid();
+ nl->nlmsg_type = NLMSG_DONE;
+ nl->nlmsg_flags = 0;
+
+ cn->len = data_size;
+ cn->seq = 0;
+ cn->ack = 0;
+ cn->id.idx = CN_IDX_PROC;
+ cn->id.val = CN_VAL_PROC;
+ cn->flags = 0;
+
+ /* all this was done for this data */
+ memcpy(cn->data, &cmd, data_size);
+
+ return send(nl_sock, buf, message_size, 0);
+}
+
+
+static int subscribe_proc_events()
+{
+ int ret = send_proc_cmd(PROC_CN_MCAST_LISTEN);
+
+ if (ret >= 0)
+ subscribed = TRUE;
+
+ return ret;
+}
+
+
+static int unsubscribe_proc_events()
+{
+ int ret = send_proc_cmd(PROC_CN_MCAST_IGNORE);
+
+ if (ret >= 0)
+ subscribed = FALSE;
+
+ return ret;
+}
+
+
+static void nl_watch(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ char buf[4096];
+ struct nlmsghdr *nl;
+ struct sockaddr_nl addr;
+ unsigned int sockaddr_len;
+ ssize_t len;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(events);
+ MRP_UNUSED(user_data);
+
+ sockaddr_len = sizeof(struct sockaddr_nl);
+
+ len = recvfrom(fd, buf, sizeof(buf), 0, (struct sockaddr *) &addr,
+ &sockaddr_len);
+
+ if (len < 0) {
+ mrp_log_error("failed to read data from socket");
+ return;
+ }
+
+ if (addr.nl_pid != 0) {
+ mrp_log_error("message wasn't from the kernel");
+ return;
+ }
+
+ /* set pointer to the first message in the buffer */
+ nl = (struct nlmsghdr *) buf;
+
+ while (NLMSG_OK(nl, (unsigned int) len)) {
+ struct cn_msg *cn;
+
+ /* we are expecting a non-multipart message -- filter errors and
+ * others away */
+ if (nl->nlmsg_type != NLMSG_DONE) {
+ if (nl->nlmsg_type == NLMSG_ERROR) {
+ /* TODO: error processing, resynchronization */
+ }
+ nl = NLMSG_NEXT(nl, len);
+ continue;
+ }
+
+ cn = (struct cn_msg *) NLMSG_DATA(nl);
+
+ if (cn->id.idx == CN_IDX_PROC && cn->id.val == CN_VAL_PROC) {
+ struct proc_event *ev = (struct proc_event *) cn->data;
+
+ switch (ev->what) {
+ case PROC_EVENT_EXIT:
+ {
+ mrp_list_hook_t *p, *n;
+ nl_pid_watch_t *nl_w;
+ char pid_s[16];
+ int ret;
+
+ mrp_log_info("process %d exited",
+ ev->event_data.exit.process_pid);
+
+ ret = snprintf(pid_s, sizeof(pid_s), "%u",
+ (unsigned int) ev->event_data.exit.process_pid);
+
+ if (ret < 0 || ret >= (int) sizeof(pid_s))
+ break;
+
+ /* check the pid */
+ nl_w = (nl_pid_watch_t *) mrp_htbl_lookup(nl_watches, pid_s);
+
+ if (!nl_w) {
+ mrp_log_error("pid %s exited but no-one was following it", pid_s);
+ break;
+ }
+
+ nl_w->busy = TRUE;
+ mrp_list_foreach(&nl_w->clients, p, n) {
+ nl_pid_client_t *client;
+
+ client = mrp_list_entry(p, typeof(*client), hook);
+ client->cb(nl_w->pid, MRP_PROCESS_STATE_NOT_READY,
+ client->user_data);
+ }
+ if (nl_w->dead)
+ mrp_free(nl_w);
+ else
+ nl_w->busy = FALSE;
+
+ /* TODO: should we automatically free the wathces? Or let
+ * client do that to preserver symmetricity? */
+ break;
+ }
+ default:
+ /* the filter isn't working for some reason */
+ mrp_log_error("some other message!\n");
+ break;
+ }
+ }
+
+ nl = NLMSG_NEXT(nl, len);
+ }
+}
+
+
+static int initialize(mrp_mainloop_t *ml, bool process, bool pid)
+{
+ if (process) {
+
+ if (initialize_dir() < 0)
+ goto error;
+
+ if (i_fd <= 0) {
+ i_fd = inotify_init();
+
+ if (i_fd <= 0)
+ goto error;
+ }
+
+ if (dir_fd <= 0) {
+
+ dir_fd = inotify_add_watch(i_fd, MURPHY_PROCESS_INOTIFY_DIR,
+ IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO | IN_MODIFY);
+
+ if (dir_fd < 0)
+ goto error;
+ }
+
+ if (!i_wd) {
+ i_wd = mrp_add_io_watch(ml, i_fd, MRP_IO_EVENT_IN, process_change, NULL);
+
+ if (!i_wd)
+ goto error;
+ }
+
+ if (!i_watches) {
+ mrp_htbl_config_t watches_conf;
+
+ watches_conf.comp = mrp_string_comp;
+ watches_conf.hash = mrp_string_hash;
+ watches_conf.free = htbl_free_i_watch;
+ watches_conf.nbucket = 0;
+ watches_conf.nentry = 10;
+
+ i_watches = mrp_htbl_create(&watches_conf);
+
+ if (!i_watches)
+ goto error;
+ }
+ }
+
+ if (pid) {
+ if (nl_sock <= 0) {
+ struct sockaddr_nl nl_addr;
+ int nl_options = SOCK_NONBLOCK | SOCK_DGRAM | SOCK_CLOEXEC;
+ struct sock_filter block[] = {
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+ };
+ struct sock_fprog fp;
+
+ /* socket creation */
+
+ nl_sock = socket(PF_NETLINK, nl_options, NETLINK_CONNECTOR);
+
+ if (nl_sock <= 0)
+ goto error;
+
+ memset(&nl_addr, 0, sizeof(struct sockaddr_nl));
+ memset(&fp, 0, sizeof(struct sock_fprog));
+
+ /* bind the socket to the address */
+
+ nl_addr.nl_pid = getpid();
+ nl_addr.nl_family = AF_NETLINK;
+ nl_addr.nl_groups = CN_IDX_PROC;
+
+ if (bind(nl_sock, (struct sockaddr *) &nl_addr,
+ sizeof(struct sockaddr_nl)) < 0)
+ goto error;
+
+ fp.filter = block;
+ fp.len = 1;
+
+ /* set socket filter that blocks everything */
+ if (setsockopt(nl_sock, SOL_SOCKET, SO_ATTACH_FILTER, &fp,
+ sizeof(struct sock_fprog)) < 0) {
+ mrp_log_error("setting blocking socket filter failed: %s",
+ strerror(errno));
+ goto error;
+ }
+
+ nl_wd = mrp_add_io_watch(ml, nl_sock, MRP_IO_EVENT_IN, nl_watch, NULL);
+ }
+
+ if (!nl_watches) {
+ mrp_htbl_config_t watches_conf;
+
+ watches_conf.comp = mrp_string_comp;
+ watches_conf.hash = mrp_string_hash;
+ watches_conf.free = htbl_free_nl_watch;
+ watches_conf.nbucket = 0;
+ watches_conf.nentry = 10;
+
+ nl_watches = mrp_htbl_create(&watches_conf);
+
+ if (!nl_watches)
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ mrp_log_error("initialization error");
+
+ if (process) {
+
+ if (i_watches) {
+ mrp_htbl_destroy(i_watches, FALSE);
+ i_watches = NULL;
+ }
+
+ if (i_wd) {
+ mrp_del_io_watch(i_wd);
+ i_wd = NULL;
+ }
+
+ if (i_fd && dir_fd) {
+ inotify_rm_watch(i_fd, dir_fd);
+ dir_fd = -1;
+ }
+
+ i_n_process_watches = 0;
+ }
+
+ if (pid) {
+
+ if (nl_sock > 0) {
+ close(nl_sock);
+ nl_sock = -1;
+ }
+
+ nl_n_pid_watches = 0;
+ }
+
+ return -1;
+}
+
+
+static int initialize_process(mrp_mainloop_t *ml)
+{
+ return initialize(ml, TRUE, FALSE);
+}
+
+
+static int initialize_pid(mrp_mainloop_t *ml)
+{
+ return initialize(ml, FALSE, TRUE);
+}
+
+
+int mrp_process_set_state(const char *id, mrp_process_state_t state)
+{
+ char *path = NULL;
+ FILE *f;
+ int ret = -1;
+
+ if (initialize_dir() < 0)
+ goto end;
+
+ path = path_from_id(id);
+
+ if (!path)
+ goto end;
+
+ switch (state) {
+ case MRP_PROCESS_STATE_UNKNOWN:
+ case MRP_PROCESS_STATE_NOT_READY:
+ if (unlink(path) < 0) {
+ if (errno != ENOENT) {
+ goto end;
+ }
+ }
+ break;
+ case MRP_PROCESS_STATE_READY:
+ f = fopen(path, "w");
+ if (!f)
+ goto end;
+
+ fclose(f);
+ break;
+ }
+
+ ret = 0;
+
+end:
+ mrp_free(path);
+ return ret;
+}
+
+
+static void filter_add_pid(struct sock_filter *p, pid_t pid, int proc_offset)
+{
+ int proc_event_data_offset = proc_offset +
+ offsetof(struct proc_event, event_data);
+ /* event_data is an union, leaving out */
+ int proc_event_data_exit_pid_offset = proc_event_data_offset +
+ offsetof(struct exit_proc_event, process_pid);
+
+ struct sock_filter bpf[] = {
+ /* load the pid value ... */
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, proc_event_data_exit_pid_offset),
+
+ /* ... if it is the pid we're comparing it to ... */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(pid), 0, 1),
+
+ /* ... return success immediately ... */
+ BPF_STMT(BPF_RET | BPF_K, 0xffffffff),
+
+ /* ... otherwise proceed to the next comparison or exit. */
+ };
+
+ mrp_debug("adding pid %d to filter\n", pid);
+
+ memcpy(p, bpf, 3*sizeof(struct sock_filter));
+}
+
+
+static int filter_update(pid_t pids[], int len_pids)
+{
+ int nl_type_offset = offsetof(struct nlmsghdr, nlmsg_type);
+ int cn_offset = NLMSG_LENGTH(0);
+ int cn_id_offset = cn_offset + offsetof(struct cn_msg, id);
+ int cn_idx_offset = cn_id_offset + offsetof(struct cb_id, idx);
+ int cn_val_offset = cn_id_offset + offsetof(struct cb_id, val);
+ int proc_offset = cn_offset + offsetof(struct cn_msg, data);
+ int proc_what_offset = proc_offset + offsetof(struct proc_event, what);
+
+ struct sock_fprog fp;
+ struct sock_filter *bpf, *iter;
+
+ struct sock_filter bpf_header[] = {
+
+ /* check that the message has only one part or is an error
+ * ( NLMSG_DONE || NLMSG_ERROR) -- return error immediately */
+
+ /* load the message type */
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, nl_type_offset),
+
+ /* check the error type */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(NLMSG_ERROR), 0, 1),
+
+ /* return if error */
+ BPF_STMT(BPF_RET | BPF_K, 0xffffffff),
+
+ /* check the done type */
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htons(NLMSG_DONE), 1, 0),
+
+ /* filter the packet if not NLMSG_DONE */
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+
+ /* check that the message is from the proc connector
+ * ( CN_IDX_PROC && CN_VAL_PROC ) */
+
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, cn_idx_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(CN_IDX_PROC), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, cn_val_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(CN_VAL_PROC), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+
+ /* check that the message is a PROC_EVENT_EXIT message */
+
+ BPF_STMT(BPF_LD | BPF_W | BPF_ABS, proc_what_offset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, htonl(PROC_EVENT_EXIT), 1, 0),
+ BPF_STMT(BPF_RET | BPF_K, 0x0),
+ };
+
+ struct sock_filter bpf_footer[] = {
+ /* if there was no pid match then filter the packet */
+ BPF_STMT (BPF_RET | BPF_K, 0x0),
+ };
+
+ int len_bpf_header = sizeof(bpf_header);
+ int len_bpf_footer = sizeof(bpf_footer);
+ /* three statements */
+ int len_bpf_pids = sizeof(struct sock_filter) * len_pids * 3;
+ int len = len_bpf_header + len_bpf_pids + len_bpf_footer;
+ int i;
+
+ if (nl_sock <= 0) {
+ mrp_log_error("invalid netlink socket %d", nl_sock);
+ goto error;
+ }
+
+ /* build the filter */
+
+ bpf = (struct sock_filter *) mrp_allocz(len);
+
+ if (!bpf)
+ goto error;
+
+ iter = bpf;
+
+ memcpy(iter, bpf_header, len_bpf_header);
+
+ iter = iter + (len_bpf_header / sizeof(struct sock_filter));
+
+ /* check that the PID is one that we are following */
+ for (i = 0; i < len_pids; i++, iter=iter+3) {
+ filter_add_pid(iter, pids[i], proc_offset);
+ }
+
+ memcpy(iter, bpf_footer, len_bpf_footer);
+
+ memset(&fp, 0, sizeof(struct sock_fprog));
+ fp.filter = bpf;
+ fp.len = len / sizeof(struct sock_filter);
+
+ if (setsockopt(nl_sock, SOL_SOCKET, SO_ATTACH_FILTER, &fp,
+ sizeof(struct sock_fprog)) < 0)
+ mrp_log_error("setting socket filter failed: %s", strerror(errno));
+
+ mrp_free(bpf);
+
+error:
+ return -1;
+}
+
+
+struct key_data_s {
+ int index;
+ pid_t *pids;
+};
+
+
+static int gather_pids_cb(void *key, void *object, void *user_data)
+{
+ struct key_data_s *kd = (struct key_data_s *) user_data;
+ nl_pid_watch_t *w = (nl_pid_watch_t *) object;
+
+ MRP_UNUSED(key);
+
+ kd->pids[kd->index] = w->pid;
+ kd->index++;
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static int pid_filter_update()
+{
+ pid_t pids[nl_n_pid_watches];
+
+ struct key_data_s kd;
+
+ kd.index = 0;
+ kd.pids = pids;
+
+ mrp_htbl_foreach(nl_watches, gather_pids_cb, &kd);
+
+ return filter_update(pids, kd.index);
+}
+
+
+mrp_process_state_t mrp_process_query_state(const char *id)
+{
+ char *path;
+ FILE *f;
+
+ if (initialize_dir() < 0)
+ return MRP_PROCESS_STATE_UNKNOWN;
+
+ path = path_from_id(id);
+
+ if (!path)
+ return MRP_PROCESS_STATE_UNKNOWN;
+
+ f = fopen(path, "r");
+
+ mrp_free(path);
+
+ if (f) {
+ fclose(f);
+ return MRP_PROCESS_STATE_READY;
+ }
+
+ return MRP_PROCESS_STATE_NOT_READY;
+}
+
+
+mrp_process_state_t mrp_pid_query_state(pid_t pid)
+{
+ char path[64];
+ struct stat s;
+ mrp_process_state_t state = MRP_PROCESS_STATE_UNKNOWN;
+ int ret;
+
+ ret = snprintf(path, sizeof(path), "/proc/%u", (unsigned int) pid);
+
+ if (ret < 0 || ret >= (int) sizeof(path))
+ goto end;
+
+ ret = stat(path, &s);
+
+ if (ret < 0 && (errno == ENOENT || errno == ENOTDIR)) {
+ state = MRP_PROCESS_STATE_NOT_READY;
+ }
+ else if (ret == 0 && S_ISDIR(s.st_mode)) {
+ state = MRP_PROCESS_STATE_READY;
+ }
+
+end:
+ return state;
+}
+
+
+int mrp_process_set_watch(const char *id, mrp_mainloop_t *ml,
+ mrp_process_watch_handler_t cb, void *userdata)
+{
+ i_watch_t *w = NULL;
+
+ if (initialize_process(ml) < 0)
+ goto error;
+
+ w = (i_watch_t *) mrp_allocz(sizeof(i_watch_t));
+
+ if (!w)
+ goto error;
+
+ w->inotify_cb = i_wd;
+ w->userdata = userdata;
+ w->process_cb = cb;
+
+ w->path = mrp_strdup(id);
+ if (!w->path)
+ goto error;
+
+ w->filename = path_from_id(id);
+ if (!w->filename)
+ goto error;
+
+ if (mrp_htbl_insert(i_watches, w->filename, w) < 0)
+ goto error;
+
+ i_n_process_watches++;
+
+ return 0;
+
+error:
+ if (w) {
+ mrp_free(w->path);
+ mrp_free(w->filename);
+ mrp_free(w);
+ }
+
+ return -1;
+}
+
+
+mrp_pid_watch_t *mrp_pid_set_watch(pid_t pid, mrp_mainloop_t *ml,
+ mrp_pid_watch_handler_t cb, void *userdata)
+{
+ nl_pid_watch_t *nl_w = NULL;
+ nl_pid_client_t *client = NULL;
+ char pid_s[16];
+ bool already_inserted = TRUE;
+ int ret;
+
+ if (initialize_pid(ml) < 0)
+ goto error;
+
+ ret = snprintf(pid_s, sizeof(pid_s), "%u", (unsigned int) pid);
+
+ if (ret < 0 || ret >= (int) sizeof(pid_s))
+ goto error;
+
+ nl_w = (nl_pid_watch_t *) mrp_htbl_lookup(nl_watches, pid_s);
+
+ if (!nl_w) {
+
+ nl_w = (nl_pid_watch_t *) mrp_allocz(sizeof(nl_pid_watch_t));
+
+ if (!nl_w)
+ goto error;
+
+ mrp_list_init(&nl_w->clients);
+ nl_w->pid = pid;
+ memcpy(nl_w->pid_s, pid_s, sizeof(nl_w->pid_s));
+
+ already_inserted = FALSE;
+ }
+
+ client = (nl_pid_client_t *) mrp_allocz(sizeof(nl_pid_client_t));
+
+ if (!client) {
+ if (!already_inserted)
+ mrp_free(nl_w);
+ goto error;
+ }
+
+ client->cb = cb;
+ client->user_data = userdata;
+ client->w = (mrp_pid_watch_t *) mrp_allocz(sizeof(mrp_pid_watch_t));
+
+ if (!client->w) {
+ mrp_free(nl_w);
+ goto error;
+ }
+
+ client->w->pid = pid;
+
+ mrp_list_init(&client->hook);
+ mrp_list_append(&nl_w->clients, &client->hook);
+ nl_w->n_clients++;
+
+ if (!already_inserted) {
+ if (mrp_htbl_insert(nl_watches, nl_w->pid_s, nl_w) < 0) {
+ mrp_list_delete(&client->hook);
+ mrp_free(nl_w);
+ goto error;
+ }
+
+ nl_n_pid_watches++;
+ }
+
+ pid_filter_update();
+
+ if (!subscribed)
+ subscribe_proc_events();
+
+ /* check that the pid is still there -- return error if not */
+
+ if (mrp_pid_query_state(pid) != MRP_PROCESS_STATE_READY)
+ goto error_process;
+
+ return client->w;
+
+error_process:
+ mrp_pid_remove_watch(client->w);
+ client = NULL;
+
+error:
+ if (client) {
+ mrp_free(client);
+ mrp_free(client->w);
+ }
+
+ return NULL;
+}
+
+
+static void update_map()
+{
+ if (i_n_process_watches <= 0) {
+ if (i_fd > 0 && dir_fd > 0) {
+ inotify_rm_watch(i_fd, dir_fd);
+ dir_fd = -1;
+ }
+ i_n_process_watches = 0;
+ }
+
+ if ((i_n_process_watches) == 0) {
+ mrp_htbl_destroy(i_watches, TRUE);
+ i_watches = NULL;
+ }
+}
+
+
+int mrp_process_remove_watch(const char *id)
+{
+ i_watch_t *w;
+ char *filename;
+
+ if (!i_watches)
+ return -1;
+
+ filename = path_from_id(id);
+
+ w = (i_watch_t *) mrp_htbl_lookup(i_watches, (void *)filename);
+
+ if (!w) {
+ mrp_free(filename);
+ return -1;
+ }
+
+ mrp_htbl_remove(i_watches, (void *) filename, TRUE);
+ i_n_process_watches--;
+
+ update_map();
+
+ mrp_free(filename);
+ return 0;
+}
+
+
+int mrp_pid_remove_watch(mrp_pid_watch_t *w)
+{
+ nl_pid_watch_t *nl_w = NULL;
+ nl_pid_client_t *client = NULL;
+ char pid_s[16];
+ mrp_list_hook_t *p, *n;
+ bool found = FALSE;
+ int ret;
+
+ if (!w)
+ goto error;
+
+ ret = snprintf(pid_s, sizeof(pid_s), "%u", (unsigned int) w->pid);
+
+ if (ret < 0 || ret >= (int) sizeof(pid_s))
+ goto error;
+
+ nl_w = (nl_pid_watch_t *) mrp_htbl_lookup(nl_watches, pid_s);
+
+ if (!nl_w) {
+ mrp_log_error("no corresponding pid watch found");
+ goto error;
+ }
+
+ mrp_list_foreach(&nl_w->clients, p, n) {
+ client = mrp_list_entry(p, typeof(*client), hook);
+ if (client->w == w) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ mrp_log_error("not registered to watch the pid");
+ goto error;
+ }
+
+ mrp_list_delete(&client->hook);
+ mrp_free(client);
+ nl_w->n_clients--;
+
+ if (nl_w->n_clients == 0) {
+ /* no-one is interested in this pid anymore */
+ mrp_htbl_remove(nl_watches, pid_s, TRUE);
+ nl_n_pid_watches--;
+
+ pid_filter_update();
+
+ if (nl_n_pid_watches == 0) {
+ /* no-one is following pids anymore */
+ if (subscribed)
+ unsubscribe_proc_events();
+ }
+ }
+
+ mrp_free(w);
+ return 0;
+
+error:
+ mrp_free(w);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PROCESS_H__
+#define __MURPHY_PROCESS_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+/* functions for the murphy family of processes */
+
+typedef enum {
+ MRP_PROCESS_STATE_UNKNOWN,
+ MRP_PROCESS_STATE_READY,
+ MRP_PROCESS_STATE_NOT_READY
+} mrp_process_state_t;
+
+typedef void (*mrp_process_watch_handler_t)(const char *,
+ mrp_process_state_t, void *);
+
+int mrp_process_set_state(const char *id, mrp_process_state_t state);
+
+mrp_process_state_t mrp_process_query_state(const char *id);
+
+int mrp_process_set_watch(const char *id, mrp_mainloop_t *ml,
+ mrp_process_watch_handler_t cb, void *userdata);
+
+int mrp_process_remove_watch(const char *id);
+
+/* functions to track external processes by pid */
+
+typedef struct mrp_pid_watch_s mrp_pid_watch_t;
+
+typedef void (*mrp_pid_watch_handler_t)(pid_t,
+ mrp_process_state_t, void *);
+
+mrp_process_state_t mrp_pid_query_state(pid_t pid);
+
+mrp_pid_watch_t *mrp_pid_set_watch(pid_t pid, mrp_mainloop_t *ml,
+ mrp_pid_watch_handler_t cb, void *userdata);
+
+int mrp_pid_remove_watch(mrp_pid_watch_t *w);
+
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_PROCESS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#include <pulse/mainloop-api.h>
+#include <pulse/timeval.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+#include <murphy/common/pulse-glue.h>
+
+
+typedef struct {
+ pa_mainloop_api *pa;
+} pulse_glue_t;
+
+
+typedef struct {
+ pa_io_event *pa_io;
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ void *user_data;
+ void *glue_data;
+} io_t;
+
+
+typedef struct {
+ pa_time_event *pa_t;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} tmr_t;
+
+
+typedef struct {
+ pa_defer_event *pa_d;
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+ void *glue_data;
+} dfr_t;
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data);
+static void del_io(void *glue_data, void *id);
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_timer(void *glue_data, void *id);
+static void mod_timer(void *glue_data, void *id, unsigned int msecs);
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data);
+static void del_defer(void *glue_data, void *id);
+static void mod_defer(void *glue_data, void *id, int enabled);
+
+
+static void io_cb(pa_mainloop_api *pa, pa_io_event *e, int fd,
+ pa_io_event_flags_t mask, void *user_data)
+{
+ io_t *io = (io_t *)user_data;
+ mrp_io_event_t events = MRP_IO_EVENT_NONE;
+
+ MRP_UNUSED(pa);
+ MRP_UNUSED(e);
+
+ if (mask & PA_IO_EVENT_INPUT) events |= MRP_IO_EVENT_IN;
+ if (mask & PA_IO_EVENT_OUTPUT) events |= MRP_IO_EVENT_OUT;
+ if (mask & PA_IO_EVENT_HANGUP) events |= MRP_IO_EVENT_HUP;
+ if (mask & PA_IO_EVENT_ERROR) events |= MRP_IO_EVENT_ERR;
+
+ io->cb(io->glue_data, io, fd, events, io->user_data);
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ pa_io_event_flags_t mask = PA_IO_EVENT_NULL;
+ io_t *io;
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io != NULL) {
+ if (events & MRP_IO_EVENT_IN) mask |= PA_IO_EVENT_INPUT;
+ if (events & MRP_IO_EVENT_OUT) mask |= PA_IO_EVENT_OUTPUT;
+ if (events & MRP_IO_EVENT_HUP) mask |= PA_IO_EVENT_HANGUP;
+ if (events & MRP_IO_EVENT_ERR) mask |= PA_IO_EVENT_ERROR;
+
+ io->pa_io = pa->io_new(pa, fd, mask, io_cb, io);
+
+ if (io->pa_io != NULL) {
+ io->cb = cb;
+ io->user_data = user_data;
+ io->glue_data = glue_data;
+
+ return io;
+ }
+ else
+ mrp_free(io);
+ }
+
+ return NULL;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ io_t *io = (io_t *)id;
+
+ pa->io_free(io->pa_io);
+ mrp_free(io);
+}
+
+
+static void timer_cb(pa_mainloop_api *pa, pa_time_event *e,
+ const struct timeval *tv, void *user_data)
+{
+ tmr_t *t = (tmr_t *)user_data;
+
+ MRP_UNUSED(pa);
+ MRP_UNUSED(e);
+ MRP_UNUSED(tv);
+
+ t->cb(t->glue_data, t, t->user_data);
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ struct timeval tv;
+ tmr_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ pa_gettimeofday(&tv);
+
+ tv.tv_sec += msecs / 1000;
+ tv.tv_usec += 1000 * (msecs % 1000);
+
+ t->pa_t = pa->time_new(pa, &tv, timer_cb, t);
+
+ if (t->pa_t != NULL) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->glue_data = glue_data;
+
+ return t;
+ }
+ else
+ mrp_free(t);
+ }
+
+ return NULL;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ tmr_t *t = (tmr_t *)id;
+
+ pa->time_free(t->pa_t);
+ mrp_free(t);
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ tmr_t *t = (tmr_t *)id;
+ struct timeval tv;
+
+ if (t != NULL) {
+ pa_gettimeofday(&tv);
+
+ tv.tv_sec += msecs / 1000;
+ tv.tv_usec += 1000 * (msecs % 1000);
+
+ pa->time_restart(t->pa_t, &tv);
+ }
+}
+
+
+static void defer_cb(pa_mainloop_api *pa, pa_defer_event *e, void *user_data)
+{
+ dfr_t *d = (dfr_t *)user_data;
+
+ MRP_UNUSED(pa);
+ MRP_UNUSED(e);
+
+ d->cb(d->glue_data, d, d->user_data);
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ dfr_t *d;
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d != NULL) {
+ d->pa_d = pa->defer_new(pa, defer_cb, d);
+
+ if (d->pa_d != NULL) {
+ d->cb = cb;
+ d->user_data = user_data;
+ d->glue_data = glue_data;
+
+ return d;
+ }
+ else
+ mrp_free(d);
+ }
+
+ return NULL;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ dfr_t *d = (dfr_t *)id;
+
+ pa->defer_free(d->pa_d);
+ mrp_free(d);
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)glue_data;
+ pa_mainloop_api *pa = glue->pa;
+ dfr_t *d = (dfr_t *)id;
+
+ pa->defer_enable(d->pa_d, !!enabled);
+}
+
+
+static void unregister(void *data)
+{
+ pulse_glue_t *glue = (pulse_glue_t *)data;
+
+ mrp_free(glue);
+}
+
+
+static mrp_superloop_ops_t pa_ops = {
+ .add_io = add_io,
+ .del_io = del_io,
+ .add_timer = add_timer,
+ .del_timer = del_timer,
+ .mod_timer = mod_timer,
+ .add_defer = add_defer,
+ .del_defer = del_defer,
+ .mod_defer = mod_defer,
+ .unregister = unregister,
+};
+
+
+int mrp_mainloop_register_with_pulse(mrp_mainloop_t *ml, pa_mainloop_api *pa)
+{
+ pulse_glue_t *glue;
+
+ glue = mrp_allocz(sizeof(*glue));
+
+ if (glue != NULL) {
+ glue->pa = pa;
+
+ if (mrp_set_superloop(ml, &pa_ops, glue))
+ return TRUE;
+ else
+ mrp_free(glue);
+ }
+
+ return FALSE;
+}
+
+
+int mrp_mainloop_unregister_from_pulse(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+
+static mrp_mainloop_t *pulse_ml;
+
+mrp_mainloop_t *mrp_mainloop_pulse_get(pa_mainloop_api *pa)
+{
+ if (pulse_ml == NULL) {
+ pulse_ml = mrp_mainloop_create();
+
+ if (pulse_ml != NULL) {
+ if (!mrp_mainloop_register_with_pulse(pulse_ml, pa)) {
+ mrp_mainloop_destroy(pulse_ml);
+ pulse_ml = NULL;
+ }
+ }
+ }
+
+ return pulse_ml;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PULSE_H__
+#define __MURPHY_PULSE_H__
+
+#include <murphy/common/mainloop.h>
+#include <pulse/mainloop.h>
+
+/** Register the given murphy mainloop with the given pulse mainloop. */
+int mrp_mainloop_register_with_pulse(mrp_mainloop_t *ml, pa_mainloop_api *pa);
+
+/** Unrgister the given murphy mainloop from the given pulse mainloop. */
+int mrp_mainloop_unregister_from_pulse(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the given pulse mainloop. */
+mrp_mainloop_t *mrp_mainloop_pulse_get(pa_mainloop_api *pa);
+
+#endif /* __MURPHY_PULSE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/pulse-subloop.h>
+
+
+struct pa_murphy_mainloop {
+ mrp_mainloop_t *ml;
+ pa_mainloop_api api;
+ mrp_list_hook_t io_events;
+ mrp_list_hook_t time_events;
+ mrp_list_hook_t defer_events;
+ mrp_list_hook_t io_dead;
+ mrp_list_hook_t time_dead;
+ mrp_list_hook_t defer_dead;
+};
+
+
+struct pa_io_event {
+ pa_murphy_mainloop *m;
+ int fd;
+ mrp_io_watch_t *w;
+ pa_io_event_cb_t cb;
+ pa_io_event_destroy_cb_t destroy;
+ void *userdata;
+ mrp_list_hook_t hook;
+ int busy : 1;
+ int dead : 1;
+};
+
+
+struct pa_time_event {
+ pa_murphy_mainloop *m;
+ mrp_timer_t *t;
+ struct timeval tv;
+ pa_time_event_cb_t cb;
+ pa_time_event_destroy_cb_t destroy;
+ void *userdata;
+ mrp_list_hook_t hook;
+ int busy : 1;
+ int dead : 1;
+};
+
+
+struct pa_defer_event {
+ pa_murphy_mainloop *m;
+ mrp_deferred_t *d;
+ pa_defer_event_cb_t cb;
+ pa_defer_event_destroy_cb_t destroy;
+ void *userdata;
+ mrp_list_hook_t hook;
+ int busy : 1;
+ int dead : 1;
+};
+
+
+pa_murphy_mainloop *pa_murphy_mainloop_new(mrp_mainloop_t *ml)
+{
+ pa_murphy_mainloop *m;
+
+ if (ml == NULL)
+ return NULL;
+
+ m = mrp_allocz(sizeof(*m));
+
+ if (m == NULL)
+ return NULL;
+
+ m->ml = ml;
+ mrp_list_init(&m->io_events);
+ mrp_list_init(&m->time_events);
+ mrp_list_init(&m->defer_events);
+ mrp_list_init(&m->io_dead);
+ mrp_list_init(&m->time_dead);
+ mrp_list_init(&m->defer_dead);
+
+ return m;
+}
+
+
+static void cleanup_io_events(pa_murphy_mainloop *m)
+{
+ mrp_list_hook_t *p, *n;
+ pa_io_event *io;
+
+ mrp_list_foreach(&m->io_events, p, n) {
+ io = mrp_list_entry(p, typeof(*io), hook);
+
+ mrp_list_delete(&io->hook);
+ mrp_del_io_watch(io->w);
+ io->w = NULL;
+
+ if (io->destroy != NULL) {
+ io->dead = true;
+ io->destroy(&io->m->api, io, io->userdata);
+ }
+
+ mrp_free(io);
+ }
+
+ mrp_list_foreach(&m->io_dead, p, n) {
+ io = mrp_list_entry(p, typeof(*io), hook);
+ mrp_list_delete(&io->hook);
+ mrp_free(io);
+ }
+}
+
+
+static void cleanup_time_events(pa_murphy_mainloop *m)
+{
+ mrp_list_hook_t *p, *n;
+ pa_time_event *t;
+
+ mrp_list_foreach(&m->time_events, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ mrp_list_delete(&t->hook);
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ if (t->destroy != NULL) {
+ t->dead = true;
+ t->destroy(&t->m->api, t, t->userdata);
+ }
+
+ mrp_free(t);
+ }
+
+ mrp_list_foreach(&m->time_dead, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+ mrp_list_delete(&t->hook);
+ mrp_free(t);
+ }
+}
+
+
+static void cleanup_defer_events(pa_murphy_mainloop *m)
+{
+ mrp_list_hook_t *p, *n;
+ pa_defer_event *d;
+
+ mrp_list_foreach(&m->defer_events, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+
+ mrp_list_delete(&d->hook);
+ mrp_del_deferred(d->d);
+ d->d = NULL;
+
+ if (d->destroy != NULL) {
+ d->dead = true;
+ d->destroy(&d->m->api, d, d->userdata);
+ }
+
+ mrp_free(d);
+ }
+
+ mrp_list_foreach(&m->defer_dead, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ mrp_list_delete(&d->hook);
+ mrp_free(d);
+ }
+}
+
+
+void pa_murphy_mainloop_free(pa_murphy_mainloop *m)
+{
+ if (m == NULL)
+ return;
+
+ cleanup_io_events(m);
+ cleanup_time_events(m);
+ cleanup_defer_events(m);
+}
+
+
+static void io_event_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *userdata)
+{
+ pa_io_event *io = (pa_io_event *)userdata;
+ pa_io_event_flags_t flags = 0;
+
+ MRP_UNUSED(w);
+
+ mrp_debug("PA I/O event 0x%x for watch %p (fd %d)", events, io, fd);
+
+ if (events & MRP_IO_EVENT_IN) flags |= PA_IO_EVENT_INPUT;
+ if (events & MRP_IO_EVENT_OUT) flags |= PA_IO_EVENT_OUTPUT;
+ if (events & MRP_IO_EVENT_HUP) flags |= PA_IO_EVENT_HANGUP;
+ if (events & MRP_IO_EVENT_ERR) flags |= PA_IO_EVENT_ERROR;
+
+ io->busy = true;
+ io->cb(&io->m->api, io, fd, flags, io->userdata);
+ io->busy = false;
+
+ if (io->dead) {
+ mrp_list_delete(&io->hook);
+ mrp_free(io);
+ }
+}
+
+
+static pa_io_event *io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t e,
+ pa_io_event_cb_t cb, void *userdata)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+ mrp_io_event_t events = 0;
+ pa_io_event *io;
+
+ mrp_debug("PA create I/O watch for fd %d, events 0x%x", fd, e);
+
+ io = mrp_allocz(sizeof(*io));
+
+ if (io == NULL)
+ return NULL;
+
+ mrp_list_init(&io->hook);
+
+ if (e & PA_IO_EVENT_INPUT) events |= MRP_IO_EVENT_IN;
+ if (e & PA_IO_EVENT_OUTPUT) events |= MRP_IO_EVENT_OUT;
+ if (e & PA_IO_EVENT_HANGUP) events |= MRP_IO_EVENT_HUP; /* RDHUP ? */
+ if (e & PA_IO_EVENT_ERROR) events |= MRP_IO_EVENT_ERR;
+
+ io->m = m;
+ io->fd = fd;
+ io->cb = cb;
+ io->userdata = userdata;
+ io->w = mrp_add_io_watch(m->ml, fd, events, io_event_cb, io);
+
+ if (io->w != NULL)
+ mrp_list_append(&m->io_events, &io->hook);
+ else {
+ mrp_free(io);
+ io = NULL;
+ }
+
+ return io;
+}
+
+
+static void io_enable(pa_io_event *io, pa_io_event_flags_t e)
+{
+ pa_murphy_mainloop *m = io->m;
+ mrp_io_event_t events = 0;
+
+ mrp_debug("PA enable events 0x%x for I/O watch %p (fd %d)", e, io, io->fd);
+
+ mrp_del_io_watch(io->w);
+ io->w = NULL;
+
+ if (e & PA_IO_EVENT_INPUT) events |= MRP_IO_EVENT_IN;
+ if (e & PA_IO_EVENT_OUTPUT) events |= MRP_IO_EVENT_OUT;
+ if (e & PA_IO_EVENT_HANGUP) events |= MRP_IO_EVENT_HUP; /* RDHUP ? */
+ if (e & PA_IO_EVENT_ERROR) events |= MRP_IO_EVENT_ERR;
+
+ io->w = mrp_add_io_watch(m->ml, io->fd, events, io_event_cb, io);
+}
+
+
+static void io_free(pa_io_event *io)
+{
+ pa_murphy_mainloop *m = io->m;
+
+ mrp_debug("PA free I/O watch %p (fd %d)", io, io->fd);
+
+ mrp_list_delete(&io->hook);
+ mrp_del_io_watch(io->w);
+ io->w = NULL;
+
+ io->dead = true;
+
+ if (!io->busy && !io->dead) {
+ io->busy = true;
+ if (io->destroy != NULL)
+ io->destroy(&io->m->api, io, io->userdata);
+ mrp_free(io);
+ }
+ else
+ mrp_list_append(&m->io_dead, &io->hook);
+}
+
+
+static void io_set_destroy(pa_io_event *io, pa_io_event_destroy_cb_t cb)
+{
+ mrp_debug("PA set I/O watch destroy callback for %p (fd %d) to %p",
+ io, io->fd, cb);
+
+ io->destroy = cb;
+}
+
+
+
+
+static void time_event_cb(mrp_timer_t *tmr, void *userdata)
+{
+ pa_time_event *t = (pa_time_event *)userdata;
+
+ MRP_UNUSED(tmr);
+
+ mrp_debug("PA time event for timer %p", t);
+
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ t->busy = true;
+ t->cb(&t->m->api, t, &t->tv, t->userdata);
+ t->busy = false;
+
+ if (t->dead) {
+ mrp_del_timer(t->t);
+ mrp_list_delete(&t->hook);
+ mrp_free(t);
+ }
+}
+
+
+static unsigned int timeval_diff(const struct timeval *from,
+ const struct timeval *to)
+{
+ int msecs, musecs, diff;
+
+ msecs = (to->tv_sec - from->tv_sec) * 1000;
+ musecs = ((int)to->tv_usec - (int)from->tv_usec) / 1000;
+
+ diff = msecs + musecs;
+
+ if (diff >= 0)
+ return (unsigned int)diff;
+ else
+ return 0;
+}
+
+
+static pa_time_event *time_new(pa_mainloop_api *api, const struct timeval *tv,
+ pa_time_event_cb_t cb, void *userdata)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+ pa_time_event *t;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ mrp_debug("PA create timer for %u msecs", timeval_diff(&now, tv));
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t == NULL)
+ return NULL;
+
+ mrp_list_init(&t->hook);
+
+ t->m = m;
+ t->cb = cb;
+ t->userdata = userdata;
+ t->t = mrp_add_timer(m->ml, timeval_diff(&now, tv), time_event_cb, t);
+
+ if (t->t != NULL)
+ mrp_list_append(&m->time_events, &t->hook);
+ else {
+ mrp_free(t);
+ t = NULL;
+ }
+
+ return t;
+}
+
+
+static void time_restart(pa_time_event *t, const struct timeval *tv)
+{
+ pa_murphy_mainloop *m = t->m;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ mrp_debug("PA restart timer %p with %u msecs", t, timeval_diff(&now, tv));
+
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ t->t = mrp_add_timer(m->ml, timeval_diff(&now, tv), time_event_cb, t);
+}
+
+
+static void time_free(pa_time_event *t)
+{
+ pa_murphy_mainloop *m = t->m;
+
+ mrp_debug("PA free timer %p", t);
+
+ mrp_list_delete(&t->hook);
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ t->dead = true;
+
+ if (!t->busy && !t->dead) {
+ t->busy = true;
+ if (t->destroy != NULL)
+ t->destroy(&t->m->api, t, t->userdata);
+ mrp_free(t);
+ }
+ else
+ mrp_list_append(&m->time_dead, &t->hook);
+}
+
+
+static void time_set_destroy(pa_time_event *t, pa_time_event_destroy_cb_t cb)
+{
+ mrp_debug("PA set timer destroy callback for %p to %p", t, cb);
+
+ t->destroy = cb;
+}
+
+
+
+
+static void defer_event_cb(mrp_deferred_t *def, void *userdata)
+{
+ pa_defer_event *d = (pa_defer_event *)userdata;
+
+ MRP_UNUSED(def);
+
+ mrp_debug("PA defer event for %p", d);
+
+ d->busy = true;
+ d->cb(&d->m->api, d, d->userdata);
+ d->busy = false;
+
+ if (d->dead) {
+ mrp_del_deferred(d->d);
+ mrp_list_delete(&d->hook);
+ mrp_free(d);
+ }
+}
+
+
+static pa_defer_event *defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb,
+ void *userdata)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+ pa_defer_event *d;
+
+ mrp_debug("PA create defer event");
+
+ d = mrp_allocz(sizeof(*d));
+
+ if (d == NULL)
+ return NULL;
+
+ mrp_list_init(&d->hook);
+
+ d->m = m;
+ d->cb = cb;
+ d->userdata = userdata;
+ d->d = mrp_add_deferred(m->ml, defer_event_cb, d);
+
+ if (d->d != NULL)
+ mrp_list_append(&m->defer_events, &d->hook);
+ else {
+ mrp_free(d);
+ d = NULL;
+ }
+
+ return d;
+}
+
+
+static void defer_enable(pa_defer_event *d, int enable)
+{
+ mrp_debug("PA %s defer event %p", enable ? "enable" : "disable", d);
+
+ if (enable)
+ mrp_enable_deferred(d->d);
+ else
+ mrp_disable_deferred(d->d);
+}
+
+
+static void defer_free(pa_defer_event *d)
+{
+ pa_murphy_mainloop *m = d->m;
+
+ mrp_debug("PA free defer event %p", d);
+
+ mrp_list_delete(&d->hook);
+ mrp_del_deferred(d->d);
+ d->d = NULL;
+
+ d->dead = true;
+
+ if (!d->busy && !d->dead) {
+ d->busy = true;
+ if (d->destroy != NULL)
+ d->destroy(&d->m->api, d, d->userdata);
+ mrp_free(d);
+ }
+ else
+ mrp_list_append(&m->defer_dead, &d->hook);
+}
+
+
+static void defer_set_destroy(pa_defer_event *d, pa_defer_event_destroy_cb_t cb)
+{
+ mrp_debug("PA set defer event destroy callback for %p to %p", d, cb);
+
+ d->destroy = cb;
+}
+
+
+static void quit(pa_mainloop_api *api, int retval)
+{
+ pa_murphy_mainloop *m = (pa_murphy_mainloop *)api->userdata;
+
+ mrp_mainloop_quit(m->ml, retval);
+}
+
+
+
+pa_mainloop_api *pa_murphy_mainloop_get_api(pa_murphy_mainloop *m)
+{
+ pa_mainloop_api api = {
+ .userdata = m,
+ .io_new = io_new,
+ .io_enable = io_enable,
+ .io_free = io_free,
+ .io_set_destroy = io_set_destroy,
+ .time_new = time_new,
+ .time_restart = time_restart,
+ .time_free = time_free,
+ .time_set_destroy = time_set_destroy,
+ .defer_new = defer_new,
+ .defer_enable = defer_enable,
+ .defer_free = defer_free,
+ .defer_set_destroy = defer_set_destroy,
+ .quit = quit
+ };
+
+ m->api = api;
+
+ return &m->api;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PULSE_SUBLOOP_H__
+#define __MURPHY_PULSE_SUBLOOP_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+#include <pulse/mainloop-api.h>
+
+MRP_CDECL_BEGIN
+
+/** An opaque Murphy main loop object. */
+typedef struct pa_murphy_mainloop pa_murphy_mainloop;
+
+/** Create a new Murphy PA main loop object for the specified main loop. */
+pa_murphy_mainloop *pa_murphy_mainloop_new(mrp_mainloop_t *ml);
+
+/** Free the Murphy PA main loop object. */
+void pa_murphy_mainloop_free(pa_murphy_mainloop *m);
+
+/** Return the abstract main loop API for the PA Murphy main loop object. */
+pa_mainloop_api *pa_murphy_mainloop_get_api(pa_murphy_mainloop *m);
+
+MRP_CDECL_END
+
+#endif /* __PULSE_SUBLOOP_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MURPHY_QT_GLUE_PRIV_H_
+#define __MURPHY_QT_GLUE_PRIV_H_
+
+#include <murphy/config.h>
+
+#ifdef QT_ENABLED
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <QSocketNotifier>
+#include <QTimer>
+#include <murphy/common/mainloop.h>
+
+class QtGlue : public QObject
+{
+Q_OBJECT
+
+public:
+ QtGlue(QObject *parent = NULL);
+};
+
+class QtIO : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum Event {
+ Read = 0x01,
+ Write = 0x02,
+ Exception = 0x04
+ };
+ Q_DECLARE_FLAGS(EventMask, Event)
+
+ QtIO (int fd, EventMask events, QObject *parent = NULL);
+ ~QtIO();
+
+public Q_SLOTS:
+ void readyRead (int fd);
+ void readyWrite (int fd);
+ void exception (int fd);
+
+private:
+ EventMask m_events;
+ QSocketNotifier *m_fdIn;
+ QSocketNotifier *m_fdOut;
+ QSocketNotifier *m_fdExcep;
+
+public:
+ void (*cb)(void *glue_data,
+ void *id, int fd, mrp_io_event_t events,
+ void *user_data);
+ void *user_data;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS (QtIO::EventMask)
+
+class QtTimer : public QObject
+{
+Q_OBJECT
+
+public:
+ QtTimer (int msecs, QObject *parent = NULL);
+ ~QtTimer ();
+
+ void setInterval (int msecs);
+ void start ();
+ void stop ();
+ void enable ();
+ void disable();
+
+private Q_SLOTS:
+ void timedout();
+
+private:
+ QTimer *m_timer;
+ int m_interval;
+ bool m_disabled;
+
+public:
+ void (*cb)(void *glue_data, void *id, void *user_data);
+ void *user_data;
+};
+
+#endif
+
+#endif /* __MURPHY_QT_GLUE_PRIV_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "qt-glue-priv.h"
+#include "qt-glue-priv.moc.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <QObject>
+#include <QSocketNotifier>
+#include <QTimer>
+
+#include <murphy/config.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/qt-glue.h>
+
+static mrp_mainloop_t *qt_ml;
+
+
+/*
+ * QtGlue
+ */
+
+QtGlue::QtGlue(QObject *parent)
+ : QObject(parent)
+{
+}
+
+
+/*
+ * QtIO
+ */
+
+QtIO::QtIO (int fd, EventMask events, QObject *parent)
+ : QObject (parent)
+ , m_events(events), m_fdIn(0), m_fdOut(0), m_fdExcep(0)
+ , cb(0), user_data(0)
+{
+ if (events & Read) {
+ m_fdIn = new QSocketNotifier(fd, QSocketNotifier::Read, this);
+
+ m_fdIn->setEnabled (true);
+
+ QObject::connect (m_fdIn, SIGNAL(activated(int)), this,
+ SLOT(readyRead(int)));
+ }
+ if (events & Write) {
+ m_fdOut = new QSocketNotifier(fd, QSocketNotifier::Write, this);
+
+ m_fdOut->setEnabled (true);
+
+ QObject::connect (m_fdOut, SIGNAL(activated(int)), this,
+ SLOT(readyWrite(int)));
+ }
+ if (events & Exception) {
+ m_fdExcep = new QSocketNotifier(fd, QSocketNotifier::Exception, this);
+
+ m_fdExcep->setEnabled (true);
+
+ QObject::connect (m_fdExcep, SIGNAL(activated(int)), this,
+ SLOT(exception(int)));
+ }
+}
+
+
+QtIO::~QtIO() {
+ if (m_fdIn)
+ delete m_fdIn;
+ if (m_fdOut)
+ delete m_fdOut;
+ if (m_fdExcep)
+ delete m_fdExcep;
+}
+
+
+void QtIO::readyRead (int fd)
+{
+ if(cb)
+ cb(parent(), this, fd, MRP_IO_EVENT_IN, user_data);
+}
+
+
+void QtIO::readyWrite (int fd)
+{
+ if (cb)
+ cb(parent(), this, fd, MRP_IO_EVENT_OUT, user_data);
+}
+
+
+static bool check_hup(int fd)
+{
+ char buf[1];
+ ssize_t n;
+
+ n = recv(fd, buf, sizeof(buf), MSG_DONTWAIT | MSG_PEEK);
+
+ if (n == 0)
+ return true;
+ else
+ return false;
+}
+
+
+void QtIO::exception (int fd)
+{
+ mrp_io_event_t events;
+
+ if (!check_hup(fd))
+ events = MRP_IO_EVENT_HUP;
+ else
+ events = (mrp_io_event_t)(MRP_IO_EVENT_ERR | MRP_IO_EVENT_HUP);
+
+ if (cb)
+ cb(parent(), this, fd, events, user_data);
+}
+
+
+/*
+ * QtTimer
+ */
+
+QtTimer::QtTimer (int msecs, QObject *parent)
+ : QObject (parent)
+ , m_timer(new QTimer(this)), m_interval(msecs >= 0 ? msecs : 0)
+ , cb(0), user_data(0)
+{
+ m_timer->setInterval (m_interval);
+ m_timer->setSingleShot (false);
+
+ QObject::connect (m_timer, SIGNAL(timeout()), this, SLOT(timedout()));
+}
+
+
+QtTimer::~QtTimer ()
+{
+ delete m_timer;
+}
+
+
+void QtTimer::setInterval (int msecs)
+{
+ m_interval = (msecs >= 0 ? msecs : 0);
+
+ m_timer->setInterval (m_interval);
+
+ if (m_timer->isActive()) {
+ m_timer->stop ();
+ m_timer->start ();
+ }
+}
+
+
+void QtTimer::start ()
+{
+ if (!m_timer->isActive())
+ m_timer->start ();
+}
+
+
+void QtTimer::stop ()
+{
+ if (m_timer->isActive())
+ m_timer->stop();
+}
+
+
+void QtTimer::disable()
+{
+ if (!m_disabled) {
+ delete m_timer;
+
+ m_timer = 0;
+ m_disabled = true;
+ }
+}
+
+
+void QtTimer::enable()
+{
+ if (m_disabled) {
+ m_timer = new QTimer(this);
+
+ setInterval (m_interval);
+
+ connect (m_timer, SIGNAL(timeout()), this, SLOT(timedout()));
+
+ m_timer->start ();
+ m_disabled = false;
+ }
+}
+
+
+void QtTimer::timedout()
+{
+ mrp_debug("timer %p latched", this);
+
+ if (cb)
+ cb(parent(), this, user_data);
+}
+
+
+static void *add_io(void *glue_data, int fd, mrp_io_event_t events,
+ void (*cb)(void *glue_data, void *id, int fd,
+ mrp_io_event_t events, void *user_data),
+ void *user_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+ QtIO *io;
+ QtIO::EventMask mask;
+
+ mask = 0;
+ if (events & MRP_IO_EVENT_IN)
+ mask |= QtIO::Read;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= QtIO::Write;
+ if (events & (MRP_IO_EVENT_ERR | MRP_IO_EVENT_HUP))
+ mask |= QtIO::Exception;
+
+ io = new QtIO (fd, mask, qt_glue);
+
+ if (io) {
+ mrp_debug("added I/O watch %p (events 0x%x) on fd %d", io, events, fd);
+
+ io->cb = cb;
+ io->user_data = user_data;
+ }
+
+ return io;
+}
+
+
+static void del_io(void *glue_data, void *id)
+{
+ QtIO *io = (QtIO *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("deleting I/O watch %p", io);
+
+ delete io;
+}
+
+
+static void *add_timer(void *glue_data, unsigned int msecs,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+ QtTimer *t = new QtTimer(msecs, qt_glue);
+
+ mrp_debug("created timer %p with %d msecs interval", t, msecs);
+
+ if (t) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->start();
+ }
+
+ return t;
+}
+
+
+static void del_timer(void *glue_data, void *id)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("deleting timer %p", t);
+
+ delete t;
+}
+
+
+static void mod_timer(void *glue_data, void *id, unsigned int msecs)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("setting timer %p to %d msecs interval", t, msecs);
+
+ if (t != NULL)
+ t->setInterval(msecs);
+}
+
+
+static void *add_defer(void *glue_data,
+ void (*cb)(void *glue_data, void *id, void *user_data),
+ void *user_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+ QtTimer *t = new QtTimer(0, qt_glue);
+
+ mrp_debug("created timer %p", t);
+
+ if (t) {
+ t->cb = cb;
+ t->user_data = user_data;
+ t->start();
+ }
+
+ return t;
+}
+
+
+static void del_defer(void *glue_data, void *id)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("deleting timer %p", t);
+
+ delete t;
+}
+
+
+static void mod_defer(void *glue_data, void *id, int enabled)
+{
+ QtTimer *t = (QtTimer *)id;
+
+ MRP_UNUSED(glue_data);
+
+ mrp_debug("%s timer %p", enabled ? "enabling" : "disabling", t);
+
+ if (enabled)
+ t->enable();
+ else
+ t->disable();
+}
+
+
+static void unregister(void *glue_data)
+{
+ QtGlue *qt_glue = (QtGlue *)glue_data;
+
+ mrp_debug("unregistering mainloop");
+
+ delete qt_glue;
+}
+
+
+int mrp_mainloop_register_with_qt(mrp_mainloop_t *ml)
+{
+ static mrp_superloop_ops_t qt_ops;
+ QtGlue *qt_glue;
+
+ qt_ops.add_io = add_io;
+ qt_ops.del_io = del_io;
+ qt_ops.add_timer = add_timer;
+ qt_ops.del_timer = del_timer;
+ qt_ops.mod_timer = mod_timer;
+ qt_ops.add_defer = add_defer;
+ qt_ops.del_defer = del_defer;
+ qt_ops.mod_defer = mod_defer;
+ qt_ops.unregister = unregister;
+
+ qt_glue = new QtGlue ();
+
+ return mrp_set_superloop(ml, &qt_ops, (void *)qt_glue);
+}
+
+
+int mrp_mainloop_unregister_from_qt(mrp_mainloop_t *ml)
+{
+ return mrp_mainloop_unregister(ml);
+}
+
+
+mrp_mainloop_t *mrp_mainloop_qt_get(void)
+{
+ if (qt_ml == NULL) {
+ qt_ml = mrp_mainloop_create();
+
+ if (qt_ml != NULL) {
+ if (!mrp_mainloop_register_with_qt(qt_ml)) {
+ mrp_mainloop_destroy(qt_ml);
+ qt_ml = NULL;
+ }
+ }
+ }
+
+ return qt_ml;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_QT_H__
+#define __MURPHY_QT_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+/** Register the given murphy mainloop with the qt mainloop. */
+int mrp_mainloop_register_with_qt(mrp_mainloop_t *ml);
+
+/** Unrgister the given murphy mainloop from the qt mainloop. */
+int mrp_mainloop_unregister_from_qt(mrp_mainloop_t *ml);
+
+/** Create a murphy mainloop and set it up with the qt mainloop. */
+mrp_mainloop_t *mrp_mainloop_qt_get(void);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_QT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_REFCNT_H__
+#define __MURPHY_REFCNT_H__
+
+/*
+ * A place/typeholder, so we can switch easily to atomic type
+ * if/when necessary.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+
+#define __MURPHY_REFCNT_CHECK__
+
+MRP_CDECL_BEGIN
+
+typedef int mrp_refcnt_t;
+
+static inline void *_mrp_ref_obj(void *obj, off_t offs)
+{
+ mrp_refcnt_t *refcnt;
+
+ if (obj != NULL) {
+ refcnt = (mrp_refcnt_t *) ((char *) obj + offs);
+ (*refcnt)++;
+ }
+
+ return obj;
+}
+
+static inline int _mrp_unref_obj(void *obj, off_t offs
+#ifdef __MURPHY_REFCNT_CHECK__
+ , const char *file
+ , int line
+ , const char *func
+#endif
+ )
+{
+ mrp_refcnt_t *refcnt;
+
+ if (obj != NULL) {
+ refcnt = (mrp_refcnt_t *) ((char *) obj + offs);
+ --(*refcnt);
+
+ if (*refcnt == 0)
+ return TRUE;
+
+#ifdef __MURPHY_REFCNT_CHECK__
+# define W mrp_log_error
+
+ if (*refcnt < 0) {
+ W("****************** REFCOUNTING BUG WARNING ******************");
+ W("* Reference-counting bug detected. The reference count of");
+ W("* object %p (@offs %d) has dropped to %d.", obj, (int)offs,
+ (int)*refcnt);
+ W("* The offending unref call was made at:");
+ W("* %s@%s:%d", func ? func : "<unkown>",
+ file ? file : "<unknown>", line);
+ W("*************************************************************");
+ }
+
+#undef W
+#endif
+ }
+
+ return FALSE;
+}
+
+
+static inline void mrp_refcnt_init(mrp_refcnt_t *refcnt)
+{
+ *refcnt = 1;
+}
+
+#define mrp_ref_obj(obj, member) \
+ (typeof(obj))_mrp_ref_obj(obj, MRP_OFFSET(typeof(*(obj)), member))
+
+#ifndef __MURPHY_REFCNT_CHECK__
+# define mrp_unref_obj(obj, member) \
+ _mrp_unref_obj(obj, MRP_OFFSET(typeof(*(obj)), member))
+#else
+# define mrp_unref_obj(obj, member) \
+ _mrp_unref_obj(obj, MRP_OFFSET(typeof(*(obj)), member), __LOC__)
+#endif
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_REFCNT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <murphy/common/macros.h>
+
+static int reject_fd = -1;
+
+
+static inline int reserve_reject_fd(void)
+{
+ if (reject_fd < 0)
+ reject_fd = open("/dev/null", O_RDONLY);
+
+ return reject_fd;
+}
+
+
+static void MRP_INIT reserve_reject(void)
+{
+ reserve_reject_fd();
+}
+
+
+int mrp_reject_connection(int sock, struct sockaddr *addr, socklen_t *alen)
+{
+ struct sockaddr *a, buf;
+ socklen_t *l, len;
+ int fd;
+
+ if (addr != NULL) {
+ a = addr;
+ l = alen;
+ }
+ else {
+ len = sizeof(buf);
+ a = &buf;
+ l = &len;
+ }
+
+ fd = accept(sock, a, l);
+
+ if (fd >= 0) {
+ close(fd);
+
+ return 0;
+ }
+
+ if (errno != EMFILE)
+ return -1;
+
+ if (reject_fd < 0) {
+ errno = ENOENT;
+
+ return -1;
+ }
+
+ close(reject_fd);
+ reject_fd = -1;
+
+ fd = accept(sock, a, l);
+
+ if (fd >= 0)
+ close(fd);
+
+ reserve_reject_fd();
+
+ return (fd >= 0 ? 0 : -1);
+}
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SOCKET_UTILS_H__
+#define __MURPHY_SOCKET_UTILS_H__
+
+#include <sys/socket.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+int mrp_reject_connection(int sock, struct sockaddr *addr, socklen_t alen);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_SOCKET_UTILS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/fragbuf.h>
+#include <murphy/common/socket-utils.h>
+#include <murphy/common/transport.h>
+
+#ifndef UNIX_PATH_MAX
+# define UNIX_PATH_MAX sizeof(((struct sockaddr_un *)NULL)->sun_path)
+#endif
+
+#define TCP4 "tcp4"
+#define TCP4L 4
+#define TCP6 "tcp6"
+#define TCP6L 4
+#define UNXS "unxs"
+#define UNXSL 4
+
+#define DEFAULT_SIZE 128 /* default input buffer size */
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ int sock; /* TCP socket */
+ mrp_io_watch_t *iow; /* socket I/O watch */
+ mrp_fragbuf_t *buf; /* fragment buffer */
+} strm_t;
+
+
+static void strm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data);
+static int strm_disconnect(mrp_transport_t *mt);
+static int open_socket(strm_t *t, int family);
+
+
+
+static int parse_address(const char *str, int *familyp, char *nodep,
+ size_t nsize, char **servicep, const char **typep)
+{
+ char *node, *service;
+ const char *type;
+ int family;
+ size_t l, nl;
+
+ node = (char *)str;
+
+ if (!strncmp(node, TCP4":", l=TCP4L+1)) {
+ family = AF_INET;
+ type = TCP4;
+ node += l;
+ }
+ else if (!strncmp(node, TCP6":", l=TCP6L+1)) {
+ family = AF_INET6;
+ type = TCP6;
+ node += l;
+ }
+ else if (!strncmp(node, UNXS":", l=UNXSL+1)) {
+ family = AF_UNIX;
+ type = UNXS;
+ node += l;
+ }
+ else {
+ if (node[0] == '[') family = AF_INET6;
+ else if (node[0] == '/') family = AF_UNIX;
+ else if (node[0] == '@') family = AF_UNIX;
+ else family = AF_UNSPEC;
+
+ type = NULL;
+ }
+
+ switch (family) {
+ case AF_INET:
+ service = strrchr(node, ':');
+ if (service == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node;
+ service++;
+
+ case AF_INET6:
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else
+ nl = service - node;
+
+ service++;
+ break;
+
+ case AF_UNSPEC:
+ if (!strncmp(node, "tcp:", l=4))
+ node += l;
+ service = strrchr(node, ':');
+
+ if (service == NULL || service == node) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (node[0] == '[') {
+ node++;
+ family = AF_INET6;
+
+ if (service[-1] != ']') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ nl = service - node - 1;
+ }
+ else {
+ family = AF_INET;
+ nl = service - node;
+ }
+ service++;
+ break;
+
+ case AF_UNIX:
+ service = NULL;
+ nl = strlen(node);
+ }
+
+ if (nl >= nsize) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ strncpy(nodep, node, nl);
+ nodep[nl] = '\0';
+ *servicep = service;
+ *familyp = family;
+ if (typep != NULL)
+ *typep = type;
+
+ return 0;
+}
+
+
+static socklen_t strm_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ struct addrinfo *ai, hints;
+ struct sockaddr_un *un;
+ char node[UNIX_PATH_MAX], *port;
+ socklen_t len;
+
+ mrp_clear(&hints);
+
+ if (parse_address(str, &hints.ai_family, node, sizeof(node),
+ &port, typep) < 0)
+ return 0;
+
+ switch (hints.ai_family) {
+ case AF_UNIX:
+ un = &addr->unx;
+ len = MRP_OFFSET(typeof(*un), sun_path) + strlen(node) + 1;
+
+ if (size < len)
+ errno = ENOMEM;
+ else {
+ un->sun_family = AF_UNIX;
+ strncpy(un->sun_path, node, UNIX_PATH_MAX-1);
+ if (un->sun_path[0] == '@')
+ un->sun_path[0] = '\0';
+ }
+
+ /* When binding the socket, we don't need the null at the end */
+ len--;
+
+ break;
+
+ case AF_INET:
+ case AF_INET6:
+ default:
+ if (getaddrinfo(node, port, &hints, &ai) == 0) {
+ if (ai->ai_addrlen <= size) {
+ memcpy(addr, ai->ai_addr, ai->ai_addrlen);
+ len = ai->ai_addrlen;
+ }
+ else
+ len = 0;
+
+ freeaddrinfo(ai);
+ }
+ else
+ len = 0;
+ }
+
+ return len;
+}
+
+
+static int strm_open(mrp_transport_t *mt)
+{
+ strm_t *t = (strm_t *)mt;
+
+ t->sock = -1;
+
+ return TRUE;
+}
+
+
+static int set_nonblocking(int sock, int nonblocking)
+{
+ long nb = (nonblocking ? 1 : 0);
+
+ return fcntl(sock, F_SETFL, O_NONBLOCK, nb);
+}
+
+
+static int set_cloexec(int fd, int cloexec)
+{
+ int on = cloexec ? 1 : 0;
+
+ return fcntl(fd, F_SETFL, O_CLOEXEC, on);
+}
+
+
+static int set_reuseaddr(int sock, int reuseaddr)
+{
+ int on;
+
+ if (reuseaddr) {
+ on = 1;
+ return setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ }
+ else
+ return 0;
+}
+
+
+static int strm_createfrom(mrp_transport_t *mt, void *conn)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_io_event_t events;
+
+ t->sock = *(int *)conn;
+
+ if (t->sock >= 0) {
+ if (mt->flags & MRP_TRANSPORT_REUSEADDR)
+ if (set_reuseaddr(t->sock, true) < 0)
+ return FALSE;
+
+ if (mt->flags & MRP_TRANSPORT_NONBLOCK || t->listened)
+ if (set_nonblocking(t->sock, true) < 0)
+ return FALSE;
+
+ if (t->connected || t->listened) {
+ if (!t->connected ||
+ (t->buf = mrp_fragbuf_create(TRUE, 0)) != NULL) {
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events,
+ strm_recv_cb, t);
+
+ if (t->iow != NULL)
+ return TRUE;
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void strm_close(mrp_transport_t *mt)
+{
+ strm_t *t = (strm_t *)mt;
+
+ mrp_debug("closing transport %p", mt);
+
+ mrp_del_io_watch(t->iow);
+ t->iow = NULL;
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+
+ if (t->sock >= 0){
+ close(t->sock);
+ t->sock = -1;
+ }
+}
+
+
+static int strm_bind(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ strm_t *t = (strm_t *)mt;
+
+ if (t->sock != -1 || open_socket(t, addr->any.sa_family)) {
+ if (bind(t->sock, &addr->any, addrlen) == 0) {
+ mrp_debug("transport %p bound", mt);
+ return TRUE;
+ }
+ }
+
+ mrp_debug("failed to bind transport %p", mt);
+ return FALSE;
+}
+
+
+static int strm_listen(mrp_transport_t *mt, int backlog)
+{
+ strm_t *t = (strm_t *)mt;
+
+ if (t->sock != -1 && t->iow != NULL && t->evt.connection != NULL) {
+ if (set_nonblocking(t->sock, true) < 0)
+ return FALSE;
+
+ if (listen(t->sock, backlog) == 0) {
+ mrp_debug("transport %p listening", mt);
+ t->listened = TRUE;
+ return TRUE;
+ }
+ }
+
+ mrp_debug("transport %p failed to listen", mt);
+ return FALSE;
+}
+
+
+static int strm_accept(mrp_transport_t *mt, mrp_transport_t *mlt)
+{
+ strm_t *t, *lt;
+ mrp_sockaddr_t addr;
+ socklen_t addrlen;
+ mrp_io_event_t events;
+
+ t = (strm_t *)mt;
+ lt = (strm_t *)mlt;
+
+ if (lt->sock < 0) {
+ errno = EBADF;
+
+ return FALSE;
+ }
+
+ addrlen = sizeof(addr);
+ t->sock = accept(lt->sock, &addr.any, &addrlen);
+
+ if (t->sock >= 0) {
+ if (mt->flags & MRP_TRANSPORT_REUSEADDR)
+ if (set_reuseaddr(t->sock, true) < 0)
+ goto reject;
+
+ if (mt->flags & MRP_TRANSPORT_NONBLOCK)
+ if (set_nonblocking(t->sock, true) < 0)
+ goto reject;
+
+ if (mt->flags & MRP_TRANSPORT_CLOEXEC)
+ if (set_cloexec(t->sock, true) < 0)
+ goto reject;
+
+ t->buf = mrp_fragbuf_create(TRUE, 0);
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events, strm_recv_cb, t);
+
+ if (t->iow != NULL && t->buf != NULL) {
+ mrp_debug("accepted connection on transport %p/%p", mlt, mt);
+ return TRUE;
+ }
+ else {
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+ close(t->sock);
+ t->sock = -1;
+ }
+ }
+ else {
+ reject:
+ if (mrp_reject_connection(lt->sock, NULL, 0) < 0) {
+ mrp_log_error("%s(): accept failed, closing transport %p (%d: %s).",
+ __FUNCTION__, mlt, errno, strerror(errno));
+ strm_close(mlt);
+
+ /* Notes:
+ * Unfortunately we cannot safely emit a closed event here.
+ * The closed event is semantically attached to an accepted
+ * tranport being closed and there is no equivalent for a
+ * listening transport (we should have had a generic error
+ * event). There for the transport owner expects and treats
+ * (IOW casts) the associated user_data accordingly. That
+ * would end up in a disaster... Once we cleanup/rework the
+ * transport infra, this needs to be done better.
+ */
+ }
+ else
+ mrp_log_error("%s(): rejected connection for transport %p (%d: %s).",
+ __FUNCTION__, mlt, errno, strerror(errno));
+ }
+
+ return FALSE;
+}
+
+
+static void strm_recv_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ strm_t *t = (strm_t *)user_data;
+ mrp_transport_t *mt = (mrp_transport_t *)t;
+ void *data, *buf;
+ uint32_t pending;
+ size_t size;
+ ssize_t n;
+ int error;
+
+ MRP_UNUSED(w);
+
+ mrp_debug("event 0x%x for transport %p", events, t);
+
+ if (events & MRP_IO_EVENT_IN) {
+ if (MRP_UNLIKELY(mt->listened != 0)) {
+ MRP_TRANSPORT_BUSY(mt, {
+ mrp_debug("connection event on transport %p", mt);
+ mt->evt.connection(mt, mt->user_data);
+ });
+
+ t->check_destroy(mt);
+ return;
+ }
+
+ while (ioctl(fd, FIONREAD, &pending) == 0 && pending > 0) {
+ buf = mrp_fragbuf_alloc(t->buf, pending);
+
+ if (buf == NULL) {
+ error = ENOMEM;
+ fatal_error:
+ mrp_debug("transport %p closed with error %d", mt, error);
+ closed:
+ strm_disconnect(mt);
+
+ if (t->evt.closed != NULL)
+ MRP_TRANSPORT_BUSY(mt, {
+ mt->evt.closed(mt, error, mt->user_data);
+ });
+
+ t->check_destroy(mt);
+ return;
+ }
+
+ n = read(fd, buf, pending);
+
+ if (n >= 0) {
+ if (n < (ssize_t)pending)
+ mrp_fragbuf_trim(t->buf, buf, pending, n);
+ }
+
+ if (n < 0 && errno != EAGAIN) {
+ error = EIO;
+ goto fatal_error;
+ }
+ }
+
+ data = NULL;
+ size = 0;
+ while (mrp_fragbuf_pull(t->buf, &data, &size)) {
+ if (t->mode != MRP_TRANSPORT_MODE_JSON)
+ error = t->recv_data(mt, data, size, NULL, 0);
+ else {
+ mrp_json_t *msg = mrp_json_string_to_object(data, size);
+
+ if (msg != NULL) {
+ error = t->recv_data((mrp_transport_t *)t, msg, 0, NULL, 0);
+ mrp_json_unref(msg);
+ }
+ else
+ error = EILSEQ;
+ }
+
+ if (error)
+ goto fatal_error;
+
+ if (t->check_destroy(mt))
+ return;
+ }
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ mrp_debug("transport %p closed by peer", mt);
+ error = 0;
+ goto closed;
+ }
+}
+
+
+static int open_socket(strm_t *t, int family)
+{
+ mrp_io_event_t events;
+
+ t->sock = socket(family, SOCK_STREAM, 0);
+
+ if (t->sock != -1) {
+ if (t->flags & MRP_TRANSPORT_REUSEADDR)
+ if (set_reuseaddr(t->sock, true) < 0)
+ goto fail;
+
+ if (t->flags & MRP_TRANSPORT_NONBLOCK)
+ if (set_nonblocking(t->sock, true) < 0)
+ goto fail;
+
+ if (t->flags & MRP_TRANSPORT_CLOEXEC)
+ if (set_cloexec(t->sock, true) < 0)
+ goto fail;
+
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events, strm_recv_cb, t);
+
+ if (t->iow != NULL)
+ return TRUE;
+ else {
+ fail:
+ close(t->sock);
+ t->sock = -1;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_connect(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_io_event_t events;
+
+ t->sock = socket(addr->any.sa_family, SOCK_STREAM, 0);
+
+ if (t->sock < 0)
+ goto fail;
+
+ if (connect(t->sock, &addr->any, addrlen) == 0) {
+ if (set_reuseaddr(t->sock, true) < 0 ||
+ set_nonblocking(t->sock, true) < 0)
+ goto close_and_fail;
+
+ t->buf = mrp_fragbuf_create(TRUE, 0);
+
+ if (t->buf != NULL) {
+ events = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+ t->iow = mrp_add_io_watch(t->ml, t->sock, events, strm_recv_cb, t);
+
+ if (t->iow != NULL) {
+ mrp_debug("connected transport %p", mt);
+
+ return TRUE;
+ }
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+ }
+ }
+
+ if (t->sock != -1) {
+ close_and_fail:
+ close(t->sock);
+ t->sock = -1;
+ }
+
+ fail:
+ mrp_debug("failed to connect transport %p", mt);
+
+ return FALSE;
+}
+
+
+static int strm_disconnect(mrp_transport_t *mt)
+{
+ strm_t *t = (strm_t *)mt;
+
+ if (t->connected/* || t->iow != NULL*/) {
+ mrp_del_io_watch(t->iow);
+ t->iow = NULL;
+
+ shutdown(t->sock, SHUT_RDWR);
+
+ mrp_fragbuf_destroy(t->buf);
+ t->buf = NULL;
+
+ mrp_debug("disconnected transport %p", mt);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int strm_send(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ strm_t *t = (strm_t *)mt;
+ struct iovec iov[2];
+ void *buf;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (t->connected) {
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (size >= 0) {
+ len = htobe32(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = size;
+
+ n = writev(t->sock, iov, 2);
+ mrp_free(buf);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ strm_t *t = (strm_t *)mt;
+ ssize_t n;
+
+ if (t->connected) {
+ n = write(t->sock, data, size);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_data_descr_t *type;
+ ssize_t n;
+ void *buf;
+ size_t size, reserve, len;
+ uint32_t *lenp;
+ uint16_t *tagp;
+
+ if (t->connected) {
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ reserve = sizeof(*lenp) + sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size > 0) {
+ lenp = buf;
+ len = size - sizeof(*lenp);
+ tagp = buf + sizeof(*lenp);
+ *lenp = htobe32(len);
+ *tagp = htobe16(tag);
+
+ n = write(t->sock, buf, len + sizeof(*lenp));
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)(len + sizeof(*lenp)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add"
+ " output queueing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_sendnative(mrp_transport_t *mt, void *data, uint32_t type_id)
+{
+ strm_t *t = (strm_t *)mt;
+ mrp_typemap_t *map = t->map;
+ void *buf;
+ size_t size, reserve;
+ uint32_t *lenp;
+ ssize_t n;
+
+ if (t->connected) {
+ reserve = sizeof(*lenp);
+
+ if (mrp_encode_native(data, type_id, reserve, &buf, &size, map) == 0) {
+ lenp = buf;
+ *lenp = htobe32(size - sizeof(*lenp));
+
+ n = write(t->sock, buf, size);
+
+ mrp_free(buf);
+
+ if (n == (ssize_t)size)
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add"
+ " output queueing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int strm_sendjson(mrp_transport_t *mt, mrp_json_t *msg)
+{
+ strm_t *t = (strm_t *)mt;
+ struct iovec iov[2];
+ const char *s;
+ ssize_t size, n;
+ uint32_t len;
+
+ if (t->connected && (s = mrp_json_object_to_string(msg)) != NULL) {
+ size = strlen(s);
+ len = htobe32(size);
+ iov[0].iov_base = &len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = (void *)s;
+ iov[1].iov_len = size;
+
+ n = writev(t->sock, iov, 2);
+
+ if (n == (ssize_t)(size + sizeof(len)))
+ return TRUE;
+ else {
+ if (n == -1 && errno == EAGAIN) {
+ mrp_log_error("%s(): XXX TODO: this sucks, need to add "
+ "output queuing for strm-transport.",
+ __FUNCTION__);
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+MRP_REGISTER_TRANSPORT(tcp4, TCP4, strm_t, strm_resolve,
+ strm_open, strm_createfrom, strm_close, NULL,
+ strm_bind, strm_listen, strm_accept,
+ strm_connect, strm_disconnect,
+ strm_send, NULL,
+ strm_sendraw, NULL,
+ strm_senddata, NULL,
+ NULL, NULL,
+ strm_sendnative, NULL,
+ strm_sendjson, NULL);
+
+MRP_REGISTER_TRANSPORT(tcp6, TCP6, strm_t, strm_resolve,
+ strm_open, strm_createfrom, strm_close, NULL,
+ strm_bind, strm_listen, strm_accept,
+ strm_connect, strm_disconnect,
+ strm_send, NULL,
+ strm_sendraw, NULL,
+ strm_senddata, NULL,
+ NULL, NULL,
+ strm_sendnative, NULL,
+ strm_sendjson, NULL);
+
+MRP_REGISTER_TRANSPORT(unxstrm, UNXS, strm_t, strm_resolve,
+ strm_open, strm_createfrom, strm_close, NULL,
+ strm_bind, strm_listen, strm_accept,
+ strm_connect, strm_disconnect,
+ strm_send, NULL,
+ strm_sendraw, NULL,
+ strm_senddata, NULL,
+ NULL, NULL,
+ strm_sendnative, NULL,
+ strm_sendjson, NULL);
--- /dev/null
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
+
+noinst_PROGRAMS = mm-test hash-test hash12-test msg-test transport-test \
+ internal-transport-test process-watch-test native-test \
+ mkdir-test path-test mask-test
+
+if LIBDBUS_ENABLED
+noinst_PROGRAMS += mainloop-test dbus-test
+endif
+
+noinst_PROGRAMS += fragbuf-test
+
+# memory management test
+mm_test_SOURCES = mm-test.c
+mm_test_CFLAGS = $(AM_CFLAGS)
+mm_test_LDADD = ../../libmurphy-common.la
+
+# hash table test
+hash_test_SOURCES = hash-test.c
+hash_test_CFLAGS = $(AM_CFLAGS)
+hash_test_LDADD = ../../libmurphy-common.la
+
+# hash12-test
+hash12_test_SOURCES = hash12-test.c
+hash12_test_CFLAGS = $(AM_CFLAGS)
+hash12_test_LDADD = ../../libmurphy-common.la
+
+# mainloop test
+mainloop_test_SOURCES = mainloop-test.c
+mainloop_test_CFLAGS = $(AM_CFLAGS) $(GLIB_CFLAGS) $(LIBDBUS_CFLAGS)
+mainloop_test_LDADD = ../../libmurphy-common.la $(GLIB_LIBS) $(LIBDBUS_LIBS)
+if PULSE_ENABLED
+mainloop_test_CFLAGS += $(PULSE_CFLAGS)
+mainloop_test_LDADD += ../../libmurphy-pulse.la $(PULSE_LIBS)
+endif
+if ECORE_ENABLED
+mainloop_test_CFLAGS += $(ECORE_CFLAGS)
+mainloop_test_LDADD += ../../libmurphy-ecore.la $(ECORE_LIBS)
+endif
+if GLIB_ENABLED
+mainloop_test_CFLAGS += $(GLIB_CFLAGS)
+mainloop_test_LDADD += ../../libmurphy-glib.la $(GLIB_LIBS)
+endif
+
+if QT_ENABLED
+noinst_LTLIBRARIES = libmainloop-qt-test.la
+libmainloop_qt_test_la_SOURCES = mainloop-qt-test.cpp
+libmainloop_qt_test_la_CPPFLAGS = $(AM_CFLAGS) $(QTCORE_CFLAGS)
+libmainloop_qt_test_la_LIBADD = ../../libmurphy-common.la \
+ ../../libmurphy-qt.la $(QTCORE_LIBS)
+mainloop_test_LDADD += libmainloop-qt-test.la $(QTCORE_LIBS) -lstdc++
+endif
+
+# msg test
+msg_test_SOURCES = msg-test.c
+msg_test_CFLAGS = $(AM_CFLAGS)
+msg_test_LDADD = ../../libmurphy-common.la
+
+# native type test
+native_test_SOURCES = native-test.c
+native_test_CFLAGS = $(AM_CFLAGS)
+native_test_LDADD = ../../libmurphy-common.la
+
+# transport test
+transport_test_SOURCES = transport-test.c
+transport_test_CFLAGS = $(AM_CFLAGS)
+transport_test_LDADD = ../../libmurphy-common.la
+
+# internal transport test
+internal_transport_test_SOURCES = internal-transport-test.c
+internal_transport_test_CFLAGS = $(AM_CFLAGS)
+internal_transport_test_LDADD = ../../libmurphy-common.la
+
+# process watch test
+process_watch_test_SOURCES = process-test.c
+process_watch_test_CFLAGS = $(AM_CFLAGS)
+process_watch_test_LDADD = ../../libmurphy-common.la
+
+if LIBDBUS_ENABLED
+transport_test_LDADD += ../../libmurphy-libdbus.la
+
+noinst_PROGRAMS += mainloop-test
+
+# DBUS tests
+noinst_PROGRAMS += dbus-test
+dbus_test_SOURCES = dbus-test.c
+dbus_test_CFLAGS = $(AM_CFLAGS) $(LIBDBUS_CFLAGS)
+dbus_test_LDADD = ../../libmurphy-libdbus.la ../../libmurphy-common.la $(LIBDBUS_LIBS)
+
+noinst_PROGRAMS += libdbus-test libdbus-transport-test
+libdbus_test_SOURCES = libdbus-test.c
+libdbus_test_CFLAGS = $(AM_CFLAGS) $(LIBDBUS_CFLAGS)
+libdbus_test_LDADD = ../../libmurphy-dbus-libdbus.la ../../libmurphy-common.la $(LIBDBUS_LIBS)
+
+libdbus_transport_test_SOURCES = libdbus-transport-test.c
+libdbus_transport_test_CFLAGS = $(AM_CFLAGS)
+libdbus_transport_test_LDADD = ../../libmurphy-common.la \
+ ../../libmurphy-dbus-libdbus.la
+endif
+
+if SDBUS_ENABLED
+noinst_PROGRAMS += sdbus-test dbus-sdbus-test sdbus-transport-test sdbus-error-message
+
+sdbus_test_SOURCES = sdbus-test.c
+sdbus_test_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS)
+sdbus_test_LDADD = ../../libmurphy-common.la $(SDBUS_LIBS)
+
+dbus_sdbus_test_SOURCES = dbus-sdbus-test.c
+dbus_sdbus_test_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS)
+dbus_sdbus_test_LDADD = \
+ ../../libmurphy-common.la \
+ ../../libmurphy-dbus-sdbus.la
+
+sdbus_transport_test_SOURCES = libdbus-transport-test.c
+sdbus_transport_test_CFLAGS = $(AM_CFLAGS)
+sdbus_transport_test_LDADD = \
+ ../../libmurphy-common.la \
+ ../../libmurphy-dbus-sdbus.la
+
+sdbus_error_message_SOURCES = sdbus-error-message.c
+sdbus_error_message_CFLAGS = $(AM_CFLAGS) $(SDBUS_CFLAGS)
+sdbus_error_message_LDADD = \
+ ../../libmurphy-common.la \
+ ../../libmurphy-dbus-sdbus.la
+endif
+
+# fragbuf test
+fragbuf_test_SOURCES = fragbuf-test.c
+fragbuf_test_CFLAGS = $(AM_CFLAGS)
+fragbuf_test_LDADD = ../../libmurphy-common.la
+
+# mkdir-test
+mkdir_test_SOURCES = mkdir-test.c
+mkdir_test_CFLAGS = $(AM_CFLAGS) -I.
+mkdir_test_LDADD = ../../libmurphy-common.la
+
+# path-test
+path_test_SOURCES = path-test.c
+path_test_CFLAGS = $(AM_CFLAGS) -I.
+path_test_LDADD = ../../libmurphy-common.la
+
+mask_test_SOURCES = mask-test.c
+mask_test_CFLAGS = $(AM_CFLAGS) -I.
+mask_test_LDADD = ../../libmurphy-common.la
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_setup_dbus_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/common/dbus-sdbus.h>
+
+#define SERVER_NAME "org.test.murphy-server"
+#define SERVER_PATH "/server"
+#define SERVER_INTERFACE "Murphy.Server"
+#define PING "ping"
+#define CLIENT_NAME "org.test.murphy-client"
+#define CLIENT_PATH "/client"
+#define CLIENT_INTERFACE "Murphy.Client"
+#define PONG "pong"
+
+
+typedef struct {
+ char *busaddr;
+ char *srvname;
+ int server;
+ int log_mask;
+ const char *log_target;
+ mrp_mainloop_t *ml;
+ mrp_timer_t *timer;
+ uint32_t seqno;
+ mrp_dbus_t *dbus;
+ const char *name;
+ int32_t cid;
+ int server_up;
+ int all_pongs;
+} context_t;
+
+
+static mrp_dbus_msg_t *create_pong_signal(mrp_dbus_t *dbus, const char *dest,
+ uint32_t seq)
+{
+ const char *sig = "u";
+ mrp_dbus_msg_t *msg;
+
+ msg = mrp_dbus_msg_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG);
+
+ if (msg != NULL) {
+ if (mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, sig) &&
+ mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT32, &seq) &&
+ mrp_dbus_msg_close_container(msg))
+ return msg;
+ else
+ mrp_dbus_msg_unref(msg);
+ }
+
+ return NULL;
+}
+
+
+static uint32_t parse_pong_signal(mrp_dbus_msg_t *msg)
+{
+ const char *sig = "u";
+ uint32_t seq;
+
+ if (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, sig) &&
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq) &&
+ mrp_dbus_msg_exit_container(msg))
+ return seq;
+ else
+ return (uint32_t)-1;
+}
+
+
+static int ping_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_dbus_msg_t *pong;
+ uint32_t seq;
+ const char *dest;
+
+ MRP_UNUSED(c);
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping request #%u", seq);
+ else
+ mrp_log_error("-> malformed ping request");
+
+ if (mrp_dbus_reply(dbus, msg, MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID))
+ mrp_log_info("<- ping reply #%u", seq);
+ else
+ mrp_log_error("Failed to send ping reply #%u.", seq);
+
+ if (seq & 0x1)
+ dest = mrp_dbus_msg_sender(msg);
+ else
+ dest = NULL;
+
+ if ((pong = create_pong_signal(dbus, dest, seq)) != NULL) {
+ if (mrp_dbus_send_msg(dbus, pong))
+ mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq);
+ else
+ mrp_log_error("Failed to send pong signal #%u.", seq);
+
+ mrp_dbus_msg_unref(pong);
+ }
+ else
+ mrp_log_error("Failed to create pong signal #%u.", seq);
+
+ return TRUE;
+}
+
+
+static int name_owner_changed(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
+ void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ const char *name, *prev, *next;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &name) &&
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &prev) &&
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &next))
+ mrp_log_info("Name %s was reassigned from %s to %s...", name,
+ prev, next);
+ else
+ mrp_log_error("Failed to parse NameOwnerChanged signal.");
+
+ return TRUE;
+}
+
+
+static void server_setup(context_t *c)
+{
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ if (c->srvname && *c->srvname) {
+ if (!mrp_dbus_acquire_name(c->dbus, c->srvname, NULL)) {
+ mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.",
+ c->srvname, c->busaddr);
+ exit(1);
+ }
+ }
+
+ if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c)) {
+ mrp_log_error("Failed to export D-BUS method '%s'.", PING);
+ exit(1);
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, name_owner_changed, c,
+ "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "NameOwnerChanged",
+ NULL)) {
+ mrp_log_error("Failed to subscribe to NameOwnerChanged signals.");
+ exit(1);
+ }
+}
+
+
+void server_cleanup(context_t *c)
+{
+ if (c->srvname && *c->srvname)
+ mrp_dbus_release_name(c->dbus, c->srvname, NULL);
+
+ mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void ping_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(user_data);
+
+ if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_ERROR) {
+ mrp_log_error("Received errorping reply.");
+
+ return;
+ }
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping reply #%u", seq);
+ else
+ mrp_log_error("Received malformedping reply.");
+
+ c->cid = 0;
+}
+
+
+static void ping_request(context_t *c)
+{
+ uint32_t seq;
+
+ if (c->cid != 0) {
+ mrp_log_warning("Previous ping request still unanswered...");
+ return;
+ }
+
+ seq = c->seqno++;
+ c->cid = mrp_dbus_call(c->dbus,
+ c->srvname, SERVER_PATH, SERVER_INTERFACE,
+ PING, 500, ping_reply, c,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID);
+
+ if (c->cid > 0)
+ mrp_log_info("<- ping request #%u", seq);
+ else
+ mrp_log_warning("Failed to send ping request #%u.", seq);
+}
+
+
+static void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ ping_request(c);
+}
+
+
+static int pong_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if ((seq = parse_pong_signal(msg)) != (uint32_t)-1)
+ mrp_log_info("-> pong signal #%u", seq);
+ else
+ mrp_log_error("-> malformed pong signal");
+
+ return TRUE;
+}
+
+
+static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ mrp_log_info("%s came up (as %s)", name, owner);
+
+ if (c->timer == NULL) {
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+ }
+ }
+ else {
+ mrp_log_info("%s went down", name);
+
+ if (c->timer != NULL) {
+ mrp_del_timer(c->timer);
+ c->timer = NULL;
+ }
+ }
+}
+
+
+static void client_setup(context_t *c)
+{
+ const char *dest;
+
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c);
+
+ if (c->all_pongs) {
+ mrp_log_info("Subscribing for all pong signals...");
+ dest = NULL;
+ }
+ else {
+ mrp_log_info("Subscribing only for pong signals to us...");
+ dest = c->name;
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ dest, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL)) {
+ mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH,
+ SERVER_INTERFACE, PONG);
+ exit(1);
+ }
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+}
+
+
+static void client_cleanup(context_t *c)
+{
+ mrp_dbus_forget_name(c->dbus, c->srvname, server_status_cb, c);
+ mrp_del_timer(c->timer);
+ mrp_dbus_unsubscribe_signal(c->dbus, pong_handler, c,
+ c->name, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -b, --bus connect the given D-BUS\n"
+ " If omitted, defaults to the session bus.\n"
+ " -a, --all-pongs subscribe for all pong signals\n"
+ " If omitted, only pong with the client address are handled.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug site enable debug message for <site>\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->busaddr = "session";
+ ctx->srvname = SERVER_NAME;
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "sab:n:l:t:vd:h"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "bus" , required_argument, NULL, 'b' },
+ { "name" , required_argument, NULL, 'n' },
+ { "all-pongs" , no_argument , NULL, 'a' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'b':
+ ctx->busaddr = optarg;
+ break;
+
+ case 'n':
+ ctx->srvname = optarg;
+ break;
+
+ case 'a':
+ ctx->all_pongs = TRUE;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(c);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr);
+ else
+ mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr);
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c);
+
+ if (c.server)
+ server_setup(&c);
+ else
+ client_setup(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ if (c.server)
+ server_cleanup(&c);
+ else
+ client_cleanup(&c);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/common/libdbus.h>
+
+#define SERVER_NAME "org.test.murphy-server"
+#define SERVER_PATH "/server"
+#define SERVER_INTERFACE "Murphy.Server"
+#define PING "ping"
+#define CLIENT_NAME "org.test.murphy-client"
+#define CLIENT_PATH "/client"
+#define CLIENT_INTERFACE "Murphy.Client"
+#define PONG "pong"
+
+
+typedef struct {
+ char *busaddr;
+ char *srvname;
+ int server;
+ int log_mask;
+ const char *log_target;
+ mrp_mainloop_t *ml;
+ mrp_timer_t *timer;
+ uint32_t seqno;
+ mrp_dbus_t *dbus;
+ const char *name;
+ int32_t cid;
+ int server_up;
+ int all_pongs;
+} context_t;
+
+
+static int ping_handler(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+ const char *dest;
+
+ MRP_UNUSED(c);
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_METHOD_CALL &&
+ dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_info("-> ping request #%u", seq);
+ else
+ mrp_log_error("-> malformed ping request");
+
+ if (!mrp_dbus_reply(dbus, msg,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send ping reply #%u.", seq);
+ else
+ mrp_log_info("<- ping reply #%u", seq);
+
+ if (seq & 0x1)
+ dest = dbus_message_get_sender(msg);
+ else
+ dest = NULL;
+
+ if (!mrp_dbus_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send pong signal #%u.", seq);
+ else
+ mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq);
+
+ return TRUE;
+}
+
+
+static void server_setup(context_t *c)
+{
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ if (c->srvname && *c->srvname) {
+ if (!mrp_dbus_acquire_name(c->dbus, c->srvname, NULL)) {
+ mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.",
+ c->srvname, c->busaddr);
+ exit(1);
+ }
+ }
+
+ if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c)) {
+ mrp_log_error("Failed to export D-BUS method '%s'.", PING);
+ exit(1);
+ }
+}
+
+
+void server_cleanup(context_t *c)
+{
+ if (c->srvname && *c->srvname)
+ mrp_dbus_release_name(c->dbus, c->srvname, NULL);
+ mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void ping_reply(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(user_data);
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR) {
+ const char *ename, *emsg;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &ename,
+ DBUS_TYPE_STRING, &emsg,
+ DBUS_TYPE_INVALID)) {
+ ename = "<unknown>";
+ emsg = "<unknown>";
+ }
+
+ mrp_log_error("Received error reply (%s, %s) to ping.", ename, emsg);
+
+ c->cid = 0;
+ return;
+ }
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_info("-> ping reply #%u", seq);
+ else
+ mrp_log_error("Received malformed ping reply.");
+
+ c->cid = 0;
+}
+
+
+static void ping_request(context_t *c)
+{
+ uint32_t seq;
+
+ if (c->cid != 0) {
+ mrp_log_warning("Previous ping request still unanswered...");
+ return;
+ }
+
+ seq = c->seqno++;
+ c->cid = mrp_dbus_call(c->dbus,
+ c->srvname, SERVER_PATH, SERVER_INTERFACE,
+ PING, 500, ping_reply, c,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID);
+
+ if (c->cid > 0)
+ mrp_log_info("<- ping request #%u", seq);
+ else
+ mrp_log_warning("Failed to send ping request #%u.", seq);
+}
+
+
+static void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ ping_request(c);
+}
+
+
+static int pong_handler(mrp_dbus_t *dbus, DBusMessage *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_SIGNAL &&
+ dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &seq,
+ DBUS_TYPE_INVALID))
+ mrp_log_info("-> pong signal #%u", seq);
+ else
+ mrp_log_error("-> malformed pong signal");
+
+ return TRUE;
+}
+
+
+static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ mrp_log_info("%s came up (as %s)", name, owner);
+
+ if (c->timer == NULL) {
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+ }
+ }
+ else {
+ mrp_log_info("%s went down", name);
+
+ if (c->timer != NULL) {
+ mrp_del_timer(c->timer);
+ c->timer = NULL;
+ }
+ }
+}
+
+
+static void client_setup(context_t *c)
+{
+ const char *dest;
+
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c);
+
+ if (c->all_pongs) {
+ mrp_log_info("Subscribing for all pong signals...");
+ dest = NULL;
+ }
+ else {
+ mrp_log_info("Subscribing only for pong signals to us...");
+ dest = c->name;
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ dest, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL)) {
+ mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH,
+ SERVER_INTERFACE, PONG);
+ exit(1);
+ }
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+}
+
+
+static void client_cleanup(context_t *c)
+{
+ mrp_dbus_follow_name(c->dbus, c->srvname, server_status_cb, c);
+ mrp_del_timer(c->timer);
+ mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ c->name, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -b, --bus connect the given D-BUS\n"
+ " If omitted, defaults to the session bus.\n"
+ " -a, --all-pongs subscribe for all pong signals\n"
+ " If omitted, only pong with the client address are handled.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->busaddr = "session";
+ ctx->srvname = SERVER_NAME;
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "sab:n:l:t:vdh"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "bus" , required_argument, NULL, 'b' },
+ { "name" , required_argument, NULL, 'n' },
+ { "all-pongs" , no_argument , NULL, 'a' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , no_argument , NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, debug;
+
+ debug = FALSE;
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'b':
+ ctx->busaddr = optarg;
+ break;
+
+ case 'n':
+ ctx->srvname = optarg;
+ break;
+
+ case 'a':
+ ctx->all_pongs = TRUE;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (debug)
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+
+ return TRUE;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(c);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr);
+ else
+ mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr);
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c);
+
+ if (c.server)
+ server_setup(&c);
+ else
+ client_setup(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ if (c.server)
+ server_cleanup(&c);
+ else
+ client_cleanup(&c);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <getopt.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/fragbuf.h>
+
+
+#define fatal(fmt, args...) do { \
+ mrp_log_error(fmt, ## args); \
+ exit(1); \
+ } while (0)
+
+
+typedef struct {
+ int log_mask;
+ const char *log_target;
+ int framed;
+} context_t;
+
+context_t ctx;
+
+void check_message(void *data, size_t size, char **messages,
+ int *chk, int *offs)
+{
+ char *p, *d;
+ int l;
+
+ if (ctx.framed) {
+ if (!strncmp(messages[*chk], data, size) && !messages[*chk][size])
+ mrp_debug("message check: OK");
+ else
+ fatal("message check: failed");
+
+ *chk += 1;
+ }
+ else {
+ d = data;
+ while (size > 0) {
+ p = messages[*chk] + *offs;
+ l = strlen(p);
+
+ if (l > (int)size)
+ l = (int)size;
+
+ if (strncmp(p, d, l))
+ fatal("message check: failed");
+
+ *offs += l;
+ size -= l;
+ d += l;
+
+ if (messages[*chk][*offs] == '\0') {
+ *chk += 1;
+ *offs = 0;
+ }
+ }
+ mrp_debug("message check: OK");
+ }
+}
+
+
+void dump_buffer(mrp_fragbuf_t *buf, char **messages, int *chk, int *offs)
+{
+ void *data;
+ size_t size;
+ int cnt;
+
+ data = NULL;
+ size = 0;
+ cnt = 0;
+
+ while (mrp_fragbuf_pull(buf, &data, &size)) {
+ mrp_log_info("got message: (%zd bytes) [%*.*s]", size,
+ (int)size, (int)size, (char *)data);
+
+ check_message(data, size, messages, chk, offs);
+
+ cnt++;
+ }
+
+ if (!cnt)
+ mrp_debug("no full messages in buffer");
+ else
+ mrp_debug("pulled %d messages from buffer...", cnt);
+}
+
+
+int test(mrp_fragbuf_t *buf, size_t *chunks, int dump_interval)
+{
+ char *messages[] = {
+ "Ticking away the moments",
+ "That make up a dull day",
+ "Fritter and waste the hours",
+ "In an off-hand way",
+ "Kicking around on a piece of ground",
+ "In your home town",
+ "Waiting for someone or something",
+ "To show you the way",
+ "Tired of lying in the sunshine",
+ "Staying home to watch the rain",
+ "You are young and life is long",
+ "And there is time to kill today",
+ "And then the one day you find",
+ "Ten years have got behind you",
+ "No one told you when to run",
+ "You missed the starting gun",
+ "And you run and you run",
+ "To catch up with the sun",
+ "But it's sinking",
+ "Racing around",
+ "To come up behind you again",
+ "The sun is the same",
+ "In a relative way",
+ "But you're older",
+ "Shorter of breath",
+ "And one day closer to death",
+ "Every year is getting shorter",
+ "Never seem to find the time",
+ "Plans that either come to naught",
+ "Or half a page of scribbled lines",
+ "Hanging on in quiet desperation",
+ "Is the English way",
+ "The time is gone",
+ "The song is over",
+ "Thought I'd something more to say",
+ "Home",
+ "Home again",
+ "I like to be here",
+ "When I can",
+ "When I come home",
+ "Cold and tired",
+ "It's good to warm my bones",
+ "Beside the fire",
+ "Far away",
+ "Across the field",
+ "Tolling on the iron bell",
+ "Calls the faithful to their knees",
+ "To hear the softly spoken magic spell...",
+ "test #1",
+ "test #2",
+ "this is a test #3",
+ "message #4",
+ "message #5",
+ "test message #6",
+ "a test #7",
+ "the quick brown (#8)",
+ "fox (#9)",
+ "jumps over the (#10)",
+ "lazy dog (#11)",
+ "this is another test message (#12)",
+ "and here is one more for you (#13)",
+ "foo (#14)",
+ "bar (#15)",
+ "foobar (#16)",
+ "barfoo (#17)",
+ "xyzzykukkuluuruu (#18)"
+ };
+
+ char *msg, *p;
+ uint32_t size, nbo_size;
+ size_t n, total;
+ int dump, chk, offs, i, j;
+
+ dump = chk = offs = 0;
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(messages); i++) {
+ msg = messages[i];
+ size = strlen(msg);
+
+ total = 0;
+ p = msg;
+
+ if (ctx.framed) {
+ nbo_size = htobe32(size);
+ if (!mrp_fragbuf_push(buf, &nbo_size, sizeof(nbo_size)))
+ fatal("failed to push message size to buffer");
+ }
+
+ for (j = 0; *p != '\0'; j++) {
+ if (!chunks[j])
+ j = 0;
+ n = chunks[j];
+ if (n > strlen(p))
+ n = strlen(p);
+
+ mrp_debug("pushing %zd bytes (%*.*s)...", n, (int)n, (int)n, p);
+
+ if (!mrp_fragbuf_push(buf, p, n))
+ fatal("failed to push %*.*s to buffer", (int)n, (int)n, p);
+
+ p += n;
+ total += n;
+
+ dump++;
+
+ if (!dump_interval ||
+ (dump_interval > 0 && !(dump % dump_interval)))
+ dump_buffer(buf, messages, &chk, &offs);
+ }
+
+ if (dump_interval < -1) {
+ if (i && !(i % -dump_interval))
+ dump_buffer(buf, messages, &chk, &offs);
+ }
+ }
+
+ dump_buffer(buf, messages, &chk, &offs);
+
+ return TRUE;
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ printf("\n");
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -n, --non-framed set buffer to non-framed mode\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(void)
+{
+ mrp_clear(&ctx);
+ ctx.log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
+ ctx.log_target = MRP_LOG_TO_STDOUT;
+ ctx.framed = TRUE;
+}
+
+
+void parse_cmdline(int argc, char **argv)
+{
+# define OPTIONS "l:t:vd:nh"
+ struct option options[] = {
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "non-framed", no_argument , NULL, 'n' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults();
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'v':
+ ctx.log_mask <<= 1;
+ ctx.log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx.log_mask = mrp_log_parse_levels(optarg);
+ if (ctx.log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx.log_target = mrp_log_parse_target(optarg);
+ if (!ctx.log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx.log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case'n':
+ ctx.framed = FALSE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ case '?':
+ if (opterr)
+ print_usage(argv[0], EINVAL, "");
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ mrp_fragbuf_t *buf;
+ size_t chunkstbl[][8] = {
+ { 3, 1, 2, 3, 5, 0, 0, 0 },
+ { 1, 2, 3, 4, 3, 2, 1, 0 },
+ { 1, 5, 3, 4, 2, 1, 1, 0 },
+ { 4, 3, 2, 1, 2, 3, 4, 0 },
+ };
+ size_t *chunks;
+ size_t single[] = { 1, 0 };
+ int intervals[] = { 1, 2, 3, 4, 5, 0, -1 };
+ int i, j, interval;
+
+ parse_cmdline(argc, argv);
+
+ mrp_log_set_mask(ctx.log_mask);
+ mrp_log_set_target(ctx.log_target);
+
+ buf = mrp_fragbuf_create(ctx.framed, 0);
+
+ if (buf == NULL)
+ fatal("failed to create data collecting buffer");
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(intervals); i++) {
+ interval = intervals[i];
+ for (j = 0; j < (int)MRP_ARRAY_SIZE(chunkstbl); j++) {
+ chunks = &chunkstbl[j][0];
+ mrp_log_info("testing with interval %d, chunks #%d", interval, j);
+ test(buf, chunks, interval);
+ test(buf, single, interval);
+ mrp_log_info("testing with interval %d, chunks #%d", -i -2, j);
+ test(buf, chunks, -i - 2);
+ test(buf, single, -i - 2);
+ }
+ }
+
+ mrp_fragbuf_destroy(buf);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+
+/*
+ * A simple glue layer to pump GMainLoop from mrp_mainloop_t. This
+ * will pretty much be turned into a murphy plugin as such...
+ */
+
+
+typedef struct {
+ GMainLoop *ml;
+ GMainContext *mc;
+ gint maxprio;
+ mrp_subloop_t *sl;
+} glib_glue_t;
+
+static glib_glue_t *glib_glue;
+
+
+static int glib_prepare(void *user_data)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_prepare(glue->mc, &glue->maxprio);
+}
+
+
+static int glib_query(void *user_data, struct pollfd *fds, int nfd,
+ int *timeout)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_query(glue->mc, glue->maxprio, timeout,
+ (GPollFD *)fds, nfd);
+}
+
+
+static int glib_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_check(glue->mc, glue->maxprio, (GPollFD *)fds, nfd);
+
+}
+
+
+static void glib_dispatch(void *user_data)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ g_main_context_dispatch(glue->mc);
+
+}
+
+
+static int glib_pump_setup(mrp_mainloop_t *ml)
+{
+ static mrp_subloop_ops_t glib_ops = {
+ .prepare = glib_prepare,
+ .query = glib_query,
+ .check = glib_check,
+ .dispatch = glib_dispatch
+ };
+
+ GMainContext *main_context;
+ GMainLoop *main_loop;
+
+ if (sizeof(GPollFD) != sizeof(struct pollfd)) {
+ mrp_log_error("sizeof(GPollFD:%zd) != sizeof(struct pollfd:%zd)\n",
+ sizeof(GPollFD), sizeof(struct pollfd));
+ return FALSE;
+ }
+
+ main_context = NULL;
+ main_loop = NULL;
+ glib_glue = NULL;
+
+ if ((main_context = g_main_context_default()) != NULL &&
+ (main_loop = g_main_loop_new(main_context, FALSE)) != NULL &&
+ (glib_glue = mrp_allocz(sizeof(*glib_glue))) != NULL) {
+
+ glib_glue->mc = main_context;
+ glib_glue->ml = main_loop;
+ glib_glue->sl = mrp_add_subloop(ml, &glib_ops, glib_glue);
+
+ if (glib_glue->sl != NULL)
+ return TRUE;
+ else
+ mrp_log_error("glib-pump failed to register subloop.");
+ }
+
+ /* all of these handle a NULL argument gracefully... */
+ g_main_loop_unref(main_loop);
+ g_main_context_unref(main_context);
+
+ mrp_free(glib_glue);
+ glib_glue = NULL;
+
+ return FALSE;
+}
+
+
+static void glib_pump_cleanup(void)
+{
+ if (glib_glue != NULL) {
+ mrp_del_subloop(glib_glue->sl);
+
+ g_main_loop_unref(glib_glue->ml);
+ g_main_context_unref(glib_glue->mc);
+
+ mrp_free(glib_glue);
+ glib_glue = NULL;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/macros.h>
+#include <murphy/common/hashtbl.h>
+
+#define MEMBER_OFFSET MRP_OFFSET
+#define ALLOC_ARR(type, n) mrp_allocz(sizeof(type) * (n))
+#define FREE mrp_free
+#define STRDUP mrp_strdup
+
+#define hash_tbl_t mrp_htbl_t
+#define hash_tbl_cfg_t mrp_htbl_config_t
+#define hash_tbl_create mrp_htbl_create
+#define hash_tbl_delete mrp_htbl_destroy
+#define hash_tbl_add mrp_htbl_insert
+#define hash_tbl_del mrp_htbl_remove
+#define hash_tbl_lookup mrp_htbl_lookup
+
+#define list_hook_t mrp_list_hook_t
+#define list_init mrp_list_init
+#define list_append mrp_list_append
+#define list_delete mrp_list_delete
+
+#define NKEY 4
+#define NPHASE 0xff
+
+#define INFO(fmt, args...) do { \
+ printf("[%s] "fmt"\n" , __FUNCTION__ , ## args); \
+ fflush(stdout); \
+ } while (0)
+
+#define ERROR(fmt, args...) do { \
+ printf("[%s] error: "fmt"\n" , __FUNCTION__, ## args); \
+ fflush(stdout); \
+ } while (0)
+
+#define FATAL(fmt, args...) do { \
+ printf("[%s] fatal error: "fmt"\n" , __FUNCTION__, ## args); \
+ fflush(stdout); \
+ exit(1); \
+ } while (0)
+
+#define MKSTR(fmt, args...) ({ \
+ char *_ptr, _buf[64] = ""; \
+ snprintf(_buf, sizeof(_buf), fmt , ## args); \
+ _ptr = STRDUP(_buf); \
+ _ptr; })
+
+#define ENTRY_KEY(entry, idx) ({ \
+ char *_key; \
+ switch ((idx)) { \
+ case 0: _key = (entry)->str1; break; \
+ case 1: _key = (entry)->str2; break; \
+ case 2: _key = (entry)->str3; break; \
+ case 3: _key = (entry)->str4; break; \
+ default: FATAL("invalid key idx %d", (idx)); \
+ } \
+ _key; })
+
+#define PATTERN_BIT(pattern, idx) \
+ (pattern & (1 << ((idx) & ((sizeof(pattern) * 8) - 1))))
+
+typedef struct {
+ char *str1;
+ int int1;
+ char *str2;
+ list_hook_t hook;
+ char *str3;
+ int int2;
+ char *str4;
+} entry_t;
+
+
+typedef struct {
+ hash_tbl_t *ht;
+ size_t size;
+
+ entry_t *entries;
+ int nentry;
+
+ int keyidx;
+ uint32_t pattern;
+} test_t;
+
+
+test_t test;
+
+void
+populate(void)
+{
+ entry_t *entry;
+ char *key;
+ int i;
+
+ INFO("populating...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ key = ENTRY_KEY(entry, test.keyidx);
+
+ if (hash_tbl_add(test.ht, key, entry))
+ INFO("hashed in entry '%s'", key);
+ else
+ FATAL("failed to hash in entry '%s'", key);
+ }
+
+ INFO("done.");
+}
+
+
+void
+evict(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("evicting...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ if (PATTERN_BIT(test.pattern, i)) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_del(test.ht, key, FALSE);
+
+ if (found != entry)
+ FATAL("expected entry to delete '%s' not found (%p != %p)",
+ key, found, entry);
+
+ INFO("removed entry '%s' (%p)", key, found);
+ }
+ }
+
+ INFO("done.");
+}
+
+
+void
+readd(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("re-adding...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ if (PATTERN_BIT(test.pattern, i)) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_lookup(test.ht, key);
+
+ if (found != NULL)
+ FATAL("unexpected entry to re-add '%s' found (%p)", key, found);
+
+ if (!hash_tbl_add(test.ht, key, entry))
+ FATAL("failed to re-add entry '%s'", key);
+
+ INFO("re-added entry '%s'", key);
+ }
+ }
+
+ INFO("done.");
+}
+
+
+void
+check(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("checking...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_lookup(test.ht, key);
+
+ if (!PATTERN_BIT(test.pattern, i)) {
+ if (found != entry)
+ FATAL("expected entry '%s' not found (%p != %p)",
+ key, found, entry);
+ }
+ else {
+ if (found != NULL)
+ FATAL("unexpected entry '%s' found", key);
+ }
+ }
+
+ INFO("done.");
+}
+
+
+void
+empty_cb(char *key, entry_t *entry, void *data)
+{
+ (void)data;
+
+ FATAL("unexpected entry %p (%s) in hash table", entry, key);
+}
+
+
+void
+reset(void)
+{
+ entry_t *entry, *found;
+ char *key;
+ int i;
+
+ INFO("resetting...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ key = ENTRY_KEY(entry, test.keyidx);
+ found = hash_tbl_del(test.ht, key, FALSE);
+
+ if (found != entry)
+ FATAL("expected entry %s not found (%p != %p)",
+ key, found, entry);
+
+ INFO("removed entry '%s' (%p)", key, found);
+ }
+
+ INFO("done.");
+}
+
+
+unsigned int hash_func(const void *key)
+{
+ unsigned int h;
+ const char *p;
+
+ for (h = 0, p = key; *p; p++) {
+ h <<= 1;
+ h ^= *p;
+ }
+
+ return h;
+}
+
+
+int cmp_func(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+
+void
+test_init(void)
+{
+ int i;
+ entry_t *entry;
+
+ INFO("setting up tests...");
+
+ if ((test.entries = ALLOC_ARR(entry_t, test.nentry)) == NULL)
+ FATAL("failed to allocate test set");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ list_init(&entry->hook);
+
+ entry->str1 = MKSTR("entry-string-%d:1", i);
+ entry->int1 = i;
+ entry->str2 = MKSTR("entry-string-%d:2", i);
+ entry->str3 = MKSTR("entry-string-%d:3", i);
+ entry->int2 = i * 2;
+ entry->str4 = MKSTR("entry-string-%d:4", i);
+
+ if (!entry->str1 || !entry->str2 || !entry->str3 || !entry->str4)
+ FATAL("failed to initialize test set");
+ }
+
+ INFO("test setup done.");
+}
+
+
+void
+test_exit(void)
+{
+ entry_t *entry;
+ int i;
+
+ INFO("cleaning up tests...");
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ FREE(entry->str1);
+ FREE(entry->str2);
+ FREE(entry->str3);
+ FREE(entry->str4);
+ }
+
+ FREE(test.entries);
+
+ test.entries = NULL;
+ test.nentry = 0;
+
+ INFO("test cleanup done.");
+}
+
+
+void
+test_run(void)
+{
+ hash_tbl_cfg_t cfg;
+ entry_t *entry;
+ int i, j;
+
+
+ /*
+ * Create a hash table, run a test loop consisting of
+ *
+ * 1) populate table
+ * 2) selectively remove entries
+ * 3) check the table
+ * 4) check the entries (for corruption)
+ * 5) reset the table
+ *
+ * then delete the hash table
+ */
+
+ cfg.nbucket = test.size / 4;
+ cfg.hash = hash_func;
+ cfg.comp = cmp_func;
+ cfg.free = NULL;
+ test.ht = hash_tbl_create(&cfg);
+
+ if (test.ht == NULL)
+ FATAL("failed to create hash table (#%d, size %zd)",
+ test.keyidx, test.size);
+
+ for (i = 0, entry = test.entries; i < test.nentry; i++, entry++) {
+ populate();
+
+ test.pattern = 0;
+ for (j = 0; j < NPHASE; j++) {
+ INFO("Running test phase #%d...", j);
+
+ evict();
+ check();
+ readd();
+
+ test.pattern++;
+
+ INFO("done.");
+ }
+
+ reset();
+ }
+
+ hash_tbl_delete(test.ht, FALSE);
+ test.ht = NULL;
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+
+ memset(&test, 0, sizeof(test));
+
+ if (argc < 2 || (test.nentry = (int)strtoul(argv[1], NULL, 10)) <= 16)
+ test.nentry = 16;
+
+ test_init();
+
+ for (i = 0; i < NKEY; i++) {
+ test.keyidx = i;
+ test.size = test.nentry; test_run();
+ test.size = test.nentry / 2; test_run();
+ test.size = test.nentry / 4; test_run();
+ }
+
+ test_exit();
+
+ return 0;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim:set expandtab shiftwidth=4:
+ */
+
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdbool.h>
+#include <murphy/common.h>
+
+#define LE_STRING "/org/murphy/resource/0/%d"
+
+// Placeholder structure
+typedef struct {
+ void *pointer;
+} test_object;
+
+static void htbl_free_test_object(void *key, void *object) {
+ test_object *obj = object;
+
+ if (key)
+ mrp_free(key);
+
+ if (obj)
+ mrp_free(obj);
+}
+
+int main(int argc, char *argv[]) {
+ mrp_htbl_config_t cfg;
+
+ mrp_htbl_t *table = NULL;
+ test_object *object = NULL;
+
+ char *string = NULL;
+ size_t string_size = 0;
+ int written_count = 0;
+
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ cfg.comp = mrp_string_comp;
+ cfg.hash = mrp_string_hash;
+ cfg.free = htbl_free_test_object;
+ cfg.nbucket = 0; // nentry/4 -> smaller than min -> 8 by def
+ cfg.nentry = 10;
+
+ table = mrp_htbl_create(&cfg);
+ if (!table) {
+ printf("blergh @Â creating initial hash table\n");
+ return 1;
+ }
+
+ // broken range: 12 - 66
+ for (int i = 0; i < 12; i++) {
+ object = mrp_allocz(sizeof(test_object));
+ if (!object) {
+ printf("blergh @Â allocating object %d\n", i);
+ return 1;
+ }
+ // allocz should handle this, but let's just have a test value written there
+ object->pointer = NULL;
+
+ string_size = snprintf(NULL, 0, LE_STRING, i);
+ if (!string_size) {
+ printf("blergh @Â calculating string %d size\n", i);
+ return 1;
+ }
+ // we need the null character as well
+ string_size++;
+
+ string = mrp_allocz(string_size);
+ if (!string) {
+ printf("blergh @Â allocating string %d\n", i);
+ return 1;
+ }
+
+ written_count = snprintf(string, string_size, LE_STRING, i);
+ if (written_count <= 0 || written_count + 1 < (int)string_size) {
+ printf("blergh @Â writing string %d\n", i);
+ return 1;
+ }
+
+ mrp_htbl_insert(table, string, object);
+ mrp_htbl_remove(table, string, TRUE);
+ }
+
+ mrp_htbl_destroy(table, TRUE);
+ printf("Successfully finished the test\n");
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+
+
+/*
+ * tags for generic message fields
+ */
+
+#define TAG_SEQ ((uint16_t)0x1)
+#define TAG_MSG ((uint16_t)0x2)
+#define TAG_U8 ((uint16_t)0x3)
+#define TAG_S8 ((uint16_t)0x4)
+#define TAG_U16 ((uint16_t)0x5)
+#define TAG_S16 ((uint16_t)0x6)
+#define TAG_DBL ((uint16_t)0x7)
+#define TAG_BLN ((uint16_t)0x8)
+#define TAG_ASTR ((uint16_t)0x9)
+#define TAG_AU32 ((uint16_t)0xa)
+#define TAG_RPL ((uint16_t)0xb)
+#define TAG_END MRP_MSG_FIELD_END
+
+#define U32_GUARD (uint32_t)-1
+
+/*
+ * our test custom data type
+ */
+
+#define TAG_CUSTOM 0x1
+
+typedef struct {
+ uint32_t seq;
+ char *msg;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ double dbl;
+ bool bln;
+ char **astr;
+ uint32_t nstr;
+ uint32_t fsck;
+ uint32_t *au32;
+ char *rpl;
+} custom_t;
+
+
+MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+mrp_data_descr_t *data_descr;
+
+typedef struct {
+ mrp_mainloop_t *ml;
+ mrp_transport_t *lt, *st;
+ char *addrstr;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *atype;
+ int server;
+ int sock;
+ mrp_io_watch_t *iow;
+ mrp_timer_t *timer;
+ int custom;
+ int buggy;
+ int connect;
+ int stream;
+ int log_mask;
+ const char *log_target;
+ uint32_t seqno;
+ mrp_list_hook_t clients;
+} context_t;
+
+typedef struct {
+ int id;
+ mrp_transport_t *t;
+ context_t *c;
+ mrp_list_hook_t hook;
+} client_t;
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data);
+
+void recv_custom(mrp_transport_t *t, void *data, uint16_t tag, void *user_data);
+void recvfrom_custom(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+
+
+void dump_msg(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_dump(msg, fp);
+}
+
+
+void srv_recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_msg_field_t *f;
+ uint32_t seq;
+ char buf[256];
+ int status;
+
+ mrp_log_info("received a message");
+ dump_msg(msg, stdout);
+
+ seq = 0;
+ if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_UINT32)
+ seq = f->u32;
+ }
+
+ snprintf(buf, sizeof(buf), "reply to message #%u", seq);
+
+ if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf,
+ TAG_END)) {
+ mrp_log_info("failed to append to received message");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(t, msg);
+ else
+ status = mrp_transport_sendto(t, msg, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ /* message unreffed by transport layer */
+}
+
+
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ MRP_UNUSED(t);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+ MRP_UNUSED(user_data);
+
+ mrp_log_info("client received a message");
+ dump_msg(msg, stdout);
+}
+
+
+void srv_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return srv_recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void dump_custom(custom_t *msg, FILE *fp)
+{
+ uint32_t i;
+
+ mrp_data_dump(msg, data_descr, fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, " seq = %u\n" , msg->seq);
+ fprintf(fp, " msg = '%s'\n", msg->msg);
+ fprintf(fp, " u8 = %u\n" , msg->u8);
+ fprintf(fp, " s8 = %d\n" , msg->s8);
+ fprintf(fp, " u16 = %u\n" , msg->u16);
+ fprintf(fp, " s16 = %d\n" , msg->s16);
+ fprintf(fp, " dbl = %f\n" , msg->dbl);
+ fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false");
+ fprintf(fp, " astr = (%u)\n", msg->nstr);
+ for (i = 0; i < msg->nstr; i++)
+ fprintf(fp, " %s\n", msg->astr[i]);
+ fprintf(fp, " au32 =\n");
+ for (i = 0; msg->au32[i] != U32_GUARD; i++)
+ fprintf(fp, " %u\n", msg->au32[i]);
+ fprintf(fp, " rpl = '%s'\n", msg->rpl);
+ fprintf(fp, "}\n");
+}
+
+
+void free_custom(custom_t *msg)
+{
+ mrp_data_free(msg, data_descr->tag);
+}
+
+
+
+void srv_recvfrom_custom(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ custom_t *msg = (custom_t *)data;
+ custom_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("server received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_senddata(t, &rpl, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(t, &rpl, data_descr->tag,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ free_custom(msg);
+}
+
+void recvfrom_custom(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ custom_t *msg = (custom_t *)data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(data);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+ MRP_UNUSED(user_data);
+
+ mrp_log_info("received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ free_custom(msg);
+}
+
+
+void recv_custom(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ recvfrom_custom(t, data, tag, NULL, 0, user_data);
+}
+
+void srv_recv_custom(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ srv_recvfrom_custom(t, data, tag, NULL, 0, user_data);
+}
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ exit(0);
+ }
+}
+
+
+void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ int flags;
+
+ mrp_log_info("connection event!");
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->st = mrp_transport_accept(lt, c, flags);
+
+ if (c->st == NULL) {
+ mrp_log_error("Failed to accept new connection.");
+ exit(1);
+ }
+}
+
+
+void type_init(context_t *c)
+{
+ if (c->buggy && c->server) {
+ data_descr = &buggy_descr;
+ mrp_log_info("Deliberately using buggy data descriptor...");
+ }
+ else
+ data_descr = &custom_descr;
+
+ if (!mrp_msg_register_type(data_descr)) {
+ mrp_log_error("Failed to register custom data type.");
+ exit(1);
+ }
+}
+
+
+void server_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = connection_evt
+ };
+
+ int flags;
+
+ if (c->custom) {
+ evt.recvdata = srv_recv_custom;
+ evt.recvdatafrom = srv_recvfrom_custom;
+ }
+ else {
+ evt.recvmsg = srv_recv_msg;
+ evt.recvmsgfrom = srv_recvfrom_msg;
+ }
+
+
+ flags = MRP_TRANSPORT_REUSEADDR |
+ (c->custom ? MRP_TRANSPORT_MODE_DATA : 0);
+ c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->lt == NULL) {
+ mrp_log_error("Failed to create listening server transport.");
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) {
+ mrp_log_error("Failed to bind transport to address %s.", c->addrstr);
+ exit(1);
+ }
+
+ if (c->stream) {
+ if (!mrp_transport_listen(c->lt, 0)) {
+ mrp_log_error("Failed to listen on server transport.");
+ exit(1);
+ }
+ }
+}
+
+
+void send_msg(client_t *client)
+{
+ mrp_msg_t *msg;
+ uint32_t seq;
+ char buf[256];
+ char *astr[] = { "this", "is", "an", "array", "of", "strings" };
+ uint32_t au32[] = { 1, 2, 3,
+ 1 << 16, 2 << 16, 3 << 16,
+ 1 << 24, 2 << 24, 3 << 24 };
+ uint32_t nstr = MRP_ARRAY_SIZE(astr);
+ uint32_t nu32 = MRP_ARRAY_SIZE(au32);
+ int status;
+ context_t *c = client->c;
+
+ seq = c->seqno++;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+
+ msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq,
+ TAG_MSG , MRP_MSG_FIELD_STRING, buf,
+ TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf,
+ TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf),
+ TAG_U16 , MRP_MSG_FIELD_UINT16, seq,
+ TAG_S16 , MRP_MSG_FIELD_SINT16, - seq,
+ TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0,
+ TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1,
+ TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr,
+ TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32,
+ TAG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(client->t, msg);
+ else
+ status = mrp_transport_sendto(client->t, msg, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", seq);
+
+ mrp_msg_unref(msg);
+}
+
+
+void send_custom(client_t *client)
+{
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+ context_t *c = client->c;
+ uint32_t seq = c->seqno++;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_senddata(client->t, &msg, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(client->t, &msg, data_descr->tag,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+
+void send_cb(mrp_timer_t *t, void *user_data)
+{
+ client_t *client = (client_t *)user_data;
+ context_t *c = client->c;
+
+ MRP_UNUSED(t);
+
+ if (c->custom)
+ send_custom(client);
+ else
+ send_msg(client);
+}
+
+
+void client_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int flags;
+ client_t *client;
+
+ if (c->custom) {
+ evt.recvdata = recv_custom;
+ evt.recvdatafrom = recvfrom_custom;
+ }
+ else {
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ }
+
+ client = mrp_allocz(sizeof(client_t));
+ mrp_list_init(&client->hook);
+ client->c = c;
+
+ mrp_list_append(&c->clients, &client->hook);
+
+ flags = c->custom ? MRP_TRANSPORT_MODE_DATA : 0;
+ client->t = mrp_transport_create(c->ml, c->atype, &evt, client, flags);
+
+ if (client->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ exit(1);
+ }
+
+ if (!strcmp(c->atype, "unxd")) {
+ char addrstr[] = "unxd:@stream-test-client";
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL);
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", addrstr);
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(client->t, &addr, alen)) {
+ mrp_log_error("Failed to bind to transport address '%s'.", addrstr);
+ exit(1);
+ }
+ }
+
+ if (c->connect) {
+ if (!mrp_transport_connect(client->t, &c->addr, c->alen)) {
+ mrp_log_error("Failed to connect to %s.", c->addrstr);
+ exit(1);
+ }
+ }
+
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, client);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create send timer.");
+ exit(1);
+ }
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -C, --connect connect transport\n"
+ " For connection-oriented transports, this is automatic.\n"
+ " -a, --address address to use\n"
+ " -c, --custom use custom messages\n"
+ " -m, --message use generic messages (default)\n"
+ " -b, --buggy use buggy data descriptors\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->addrstr = "tcp4:127.0.0.1:3000";
+ ctx->server = FALSE;
+ ctx->custom = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "scmbCa:l:t:vdh"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "address" , required_argument, NULL, 'a' },
+ { "custom" , no_argument , NULL, 'c' },
+ { "connect" , no_argument , NULL, 'C' },
+ { "message" , no_argument , NULL, 'm' },
+ { "buggy" , no_argument , NULL, 'b' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , no_argument , NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, debug;
+
+ debug = FALSE;
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'c':
+ ctx->custom = TRUE;
+ break;
+
+ case 'm':
+ ctx->custom = FALSE;
+ break;
+
+ case 'b':
+ ctx->buggy = TRUE;
+ break;
+
+ case 'C':
+ ctx->connect = TRUE;
+ break;
+
+ case 'a':
+ ctx->addrstr = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (debug)
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ mrp_log_info("Using address '%s'...", c.addrstr);
+
+ mrp_list_init(&c.clients);
+
+ if (c.custom)
+ mrp_log_info("Using custom messages...");
+ else
+ mrp_log_info("Using generic messages...");
+
+ if (!strncmp(c.addrstr, "tcp", 3) ||
+ !strncmp(c.addrstr, "unxs", 4)) {
+ c.stream = TRUE;
+ c.connect = TRUE;
+ }
+
+ c.alen = mrp_transport_resolve(NULL, c.addrstr,
+ &c.addr, sizeof(c.addr), &c.atype);
+ if (c.alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr);
+ exit(1);
+ }
+
+ c.ml = mrp_mainloop_create();
+
+ type_init(&c);
+
+ server_init(&c);
+
+ client_init(&c);
+ client_init(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/common/dbus-libdbus.h>
+
+#define SERVER_NAME "org.test.murphy-server"
+#define SERVER_PATH "/server"
+#define SERVER_INTERFACE "Murphy.Server"
+#define PING "ping"
+#define CLIENT_NAME "org.test.murphy-client"
+#define CLIENT_PATH "/client"
+#define CLIENT_INTERFACE "Murphy.Client"
+#define PONG "pong"
+
+
+typedef struct {
+ char *busaddr;
+ int server;
+ int log_mask;
+ const char *log_target;
+ mrp_mainloop_t *ml;
+ mrp_timer_t *timer;
+ uint32_t seqno;
+ mrp_dbus_t *dbus;
+ const char *name;
+ int32_t cid;
+ int server_up;
+ int all_pongs;
+} context_t;
+
+
+static int ping_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+ const char *dest;
+
+ MRP_UNUSED(c);
+
+ if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_METHOD_CALL) {
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping request #%u", seq);
+ else
+ mrp_log_error("-> malformed ping request");
+
+ if (!mrp_dbus_reply(dbus, msg,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send ping reply #%u.", seq);
+ else
+ mrp_log_info("<- ping reply #%u", seq);
+
+ if (seq & 0x1)
+ dest = mrp_dbus_msg_sender(msg);
+ else
+ dest = NULL;
+
+ if (!mrp_dbus_signal(dbus, dest, SERVER_PATH, SERVER_INTERFACE, PONG,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID))
+ mrp_log_error("Failed to send pong signal #%u.", seq);
+ else
+ mrp_log_info("<- pong %s #%u", dest ? "signal" : "broadcast", seq);
+ }
+
+ return TRUE;
+}
+
+
+static void server_setup(context_t *c)
+{
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ if (!mrp_dbus_acquire_name(c->dbus, SERVER_NAME, NULL)) {
+ mrp_log_error("Failed to acquire D-BUS name '%s' on bus '%s'.",
+ SERVER_NAME, c->busaddr);
+ exit(1);
+ }
+
+ if (!mrp_dbus_export_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c)) {
+ mrp_log_error("Failed to export D-BUS method '%s'.", PING);
+ exit(1);
+ }
+}
+
+
+void server_cleanup(context_t *c)
+{
+ mrp_dbus_release_name(c->dbus, SERVER_NAME, NULL);
+ mrp_dbus_remove_method(c->dbus, SERVER_PATH, SERVER_INTERFACE,
+ PING, ping_handler, c);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void ping_reply(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(user_data);
+
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> ping reply #%u", seq);
+ else
+ mrp_log_error("Received malformed ping reply.");
+
+ c->cid = 0;
+}
+
+
+static void ping_request(context_t *c)
+{
+ uint32_t seq;
+
+ if (c->cid != 0) {
+ mrp_log_warning("Previous ping request still unanswered...");
+ return;
+ }
+
+ seq = c->seqno++;
+ c->cid = mrp_dbus_call(c->dbus,
+ SERVER_NAME, SERVER_PATH, SERVER_INTERFACE,
+ PING, 500, ping_reply, c,
+ MRP_DBUS_TYPE_UINT32, &seq,
+ MRP_DBUS_TYPE_INVALID);
+
+ if (c->cid > 0)
+ mrp_log_info("<- ping request #%u", seq);
+ else
+ mrp_log_warning("Failed to send ping request #%u.", seq);
+}
+
+
+static void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ ping_request(c);
+}
+
+
+static int pong_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ uint32_t seq;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(dbus);
+
+ if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_SIGNAL) {
+ if (mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &seq))
+ mrp_log_info("-> pong signal #%u", seq);
+ else
+ mrp_log_error("-> malformed pong signal");
+ }
+
+ return TRUE;
+}
+
+
+static void server_status_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(dbus);
+ MRP_UNUSED(name);
+
+ if (up) {
+ mrp_log_info("%s came up (as %s)", name, owner);
+
+ if (c->timer == NULL) {
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+ }
+ }
+ else {
+ mrp_log_info("%s went down", name);
+
+ if (c->timer != NULL) {
+ mrp_del_timer(c->timer);
+ c->timer = NULL;
+ }
+ }
+}
+
+
+static void client_setup(context_t *c)
+{
+ const char *dest;
+
+ c->dbus = mrp_dbus_connect(c->ml, c->busaddr, NULL);
+
+ if (c->dbus == NULL) {
+ mrp_log_error("Failed to create D-BUS connection to '%s' bus.",
+ c->busaddr);
+ exit(1);
+ }
+
+ c->name = mrp_dbus_get_unique_name(c->dbus);
+ mrp_log_info("Our address is %s on the bus...",
+ c->name ? c->name : "unknown");
+
+ mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c);
+
+ if (c->all_pongs) {
+ mrp_log_info("Subscribing for all pong signals...");
+ dest = NULL;
+ }
+ else {
+ mrp_log_info("Subscribing only for pong signals to us...");
+ dest = c->name;
+ }
+
+ if (!mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ dest, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL)) {
+ mrp_log_error("Failed to subscribe for signal '%s/%s.%s'.", SERVER_PATH,
+ SERVER_INTERFACE, PONG);
+ exit(1);
+ }
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create D-BUS sending timer.");
+ exit(1);
+ }
+}
+
+
+static void client_cleanup(context_t *c)
+{
+ mrp_dbus_follow_name(c->dbus, SERVER_NAME, server_status_cb, c);
+ mrp_del_timer(c->timer);
+ mrp_dbus_subscribe_signal(c->dbus, pong_handler, c,
+ c->name, SERVER_PATH, SERVER_INTERFACE,
+ PONG, NULL);
+ mrp_dbus_unref(c->dbus);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -b, --bus connect the given D-BUS\n"
+ " If omitted, defaults to the session bus.\n"
+ " -a, --all-pongs subscribe for all pong signals\n"
+ " If omitted, only pong with the client address are handled.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->busaddr = "session";
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "sab:l:t:vdh"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "bus" , required_argument, NULL, 'b' },
+ { "all-pongs" , no_argument , NULL, 'a' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , no_argument , NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt, debug;
+
+ debug = FALSE;
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'b':
+ ctx->busaddr = optarg;
+ break;
+
+ case 'a':
+ ctx->all_pongs = TRUE;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ debug = TRUE;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (debug)
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+
+ return TRUE;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(c);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ mrp_clear(&c);
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using D-BUS '%s'...", c.busaddr);
+ else
+ mrp_log_info("Running as client, using D-BUS '%s'...", c.busaddr);
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT , signal_handler, &c);
+
+ if (c.server)
+ server_setup(&c);
+ else
+ client_setup(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ if (c.server)
+ server_cleanup(&c);
+ else
+ client_cleanup(&c);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+
+/*
+ * tags for generic message fields
+ */
+
+#define TAG_SEQ ((uint16_t)0x1)
+#define TAG_MSG ((uint16_t)0x2)
+#define TAG_U8 ((uint16_t)0x3)
+#define TAG_S8 ((uint16_t)0x4)
+#define TAG_U16 ((uint16_t)0x5)
+#define TAG_S16 ((uint16_t)0x6)
+#define TAG_DBL ((uint16_t)0x7)
+#define TAG_BLN ((uint16_t)0x8)
+#define TAG_ASTR ((uint16_t)0x9)
+#define TAG_AU32 ((uint16_t)0xa)
+#define TAG_RPL ((uint16_t)0xb)
+#define TAG_END MRP_MSG_FIELD_END
+
+#define U32_GUARD (uint32_t)-1
+
+/*
+ * our test custom data type
+ */
+
+#define TAG_CUSTOM 0x1
+
+typedef struct {
+ uint32_t seq;
+ char *msg;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ double dbl;
+ bool bln;
+ char **astr;
+ uint32_t nstr;
+ uint32_t fsck;
+ uint32_t *au32;
+ char *rpl;
+} custom_t;
+
+
+MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+mrp_data_descr_t *data_descr;
+
+
+typedef enum {
+ MODE_DEFAULT = 0,
+ MODE_MESSAGE = 1,
+ MODE_DATA = 2,
+ MODE_RAW = 3,
+} msg_mode_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml;
+ mrp_transport_t *lt, *t;
+ char *addrstr;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *atype;
+ int server;
+ int sock;
+ mrp_io_watch_t *iow;
+ mrp_timer_t *timer;
+ int mode;
+ int buggy;
+ int connect;
+ int stream;
+ int log_mask;
+ const char *log_target;
+ uint32_t seqno;
+} context_t;
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data);
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data);
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+void recvraw(mrp_transport_t *t, void *data, size_t size, void *user_data);
+void recvrawfrom(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+
+void dump_msg(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_dump(msg, fp);
+}
+
+
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_msg_field_t *f;
+ uint32_t seq;
+ char buf[256];
+ int status;
+
+ mrp_log_info("received a message");
+ dump_msg(msg, stdout);
+
+ if (c->server) {
+ seq = 0;
+ if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_UINT32)
+ seq = f->u32;
+ }
+
+ snprintf(buf, sizeof(buf), "reply to message #%u", seq);
+
+ if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf,
+ TAG_END)) {
+ mrp_log_info("failed to append to received message");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(t, msg);
+ else
+ status = mrp_transport_sendto(t, msg, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ /* message unreffed by transport layer */
+ }
+}
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void dump_custom(custom_t *msg, FILE *fp)
+{
+ uint32_t i;
+
+ mrp_data_dump(msg, data_descr, fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, " seq = %u\n" , msg->seq);
+ fprintf(fp, " msg = '%s'\n", msg->msg);
+ fprintf(fp, " u8 = %u\n" , msg->u8);
+ fprintf(fp, " s8 = %d\n" , msg->s8);
+ fprintf(fp, " u16 = %u\n" , msg->u16);
+ fprintf(fp, " s16 = %d\n" , msg->s16);
+ fprintf(fp, " dbl = %f\n" , msg->dbl);
+ fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false");
+ fprintf(fp, " astr = (%u)\n", msg->nstr);
+ for (i = 0; i < msg->nstr; i++)
+ fprintf(fp, " %s\n", msg->astr[i]);
+ fprintf(fp, " au32 =\n");
+ for (i = 0; msg->au32[i] != U32_GUARD; i++)
+ fprintf(fp, " %u\n", msg->au32[i]);
+ fprintf(fp, " rpl = '%s'\n", msg->rpl);
+ fprintf(fp, "}\n");
+}
+
+
+void free_custom(custom_t *msg)
+{
+ mrp_data_free(msg, data_descr->tag);
+}
+
+
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ custom_t *msg = (custom_t *)data;
+ custom_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ if (c->server) {
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_senddata(t, &rpl, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(t, &rpl, data_descr->tag,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+
+ free_custom(msg);
+}
+
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ recvfrom_data(t, data, tag, NULL, 0, user_data);
+}
+
+
+void dump_raw(void *data, size_t size, FILE *fp)
+{
+ int len = (int)size;
+
+ fprintf(fp, "[%*.*s]\n", len, len, (char *)data);
+}
+
+
+void recvfrom_raw(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ char rpl[256];
+ size_t rpl_size;
+ int status;
+
+ rpl_size = snprintf(rpl, sizeof(rpl), "reply to message [%*.*s]",
+ (int)size, (int)size, (char *)data);
+
+ mrp_log_info("received raw message");
+ dump_raw(data, size, stdout);
+
+ if (strncmp((char *)data, "reply to ", 9) != 0) {
+ if (c->connect)
+ status = mrp_transport_sendraw(t, rpl, rpl_size);
+ else
+ status = mrp_transport_sendrawto(t, rpl, rpl_size, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+}
+
+
+void recv_raw(mrp_transport_t *t, void *data, size_t size, void *user_data)
+{
+ recvfrom_raw(t, data, size, NULL, 0, user_data);
+}
+
+
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ exit(0);
+ }
+}
+
+
+void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ int flags;
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->t = mrp_transport_accept(lt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to accept new connection.");
+ exit(1);
+ }
+}
+
+
+void type_init(context_t *c)
+{
+ if (c->buggy && c->server) {
+ data_descr = &buggy_descr;
+ mrp_log_info("Deliberately using buggy data descriptor...");
+ }
+ else
+ data_descr = &custom_descr;
+
+ if (!mrp_msg_register_type(data_descr)) {
+ mrp_log_error("Failed to register custom data type.");
+ exit(1);
+ }
+}
+
+
+void server_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = NULL,
+ .connection = NULL,
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ break;
+ case MODE_MESSAGE:
+ default:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ }
+
+ if (c->stream) {
+ evt.connection = connection_evt;
+ evt.closed = closed_evt;
+ }
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+
+ switch (c->mode) {
+ case MODE_DATA: flags |= MRP_TRANSPORT_MODE_DATA; break;
+ case MODE_RAW: flags |= MRP_TRANSPORT_MODE_RAW; break;
+ default:
+ case MODE_MESSAGE: flags |= MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->lt == NULL) {
+ mrp_log_error("Failed to create listening server transport.");
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) {
+ mrp_log_error("Failed to bind transport to address %s.", c->addrstr);
+ exit(1);
+ }
+
+ if (c->stream) {
+ if (!mrp_transport_listen(c->lt, 0)) {
+ mrp_log_error("Failed to listen on server transport.");
+ exit(1);
+ }
+ }
+}
+
+
+void send_msg(context_t *c)
+{
+ mrp_msg_t *msg;
+ uint32_t seq;
+ char buf[256];
+ char *astr[] = { "this", "is", "an", "array", "of", "strings" };
+ uint32_t au32[] = { 1, 2, 3,
+ 1 << 16, 2 << 16, 3 << 16,
+ 1 << 24, 2 << 24, 3 << 24 };
+ uint32_t nstr = MRP_ARRAY_SIZE(astr);
+ uint32_t nu32 = MRP_ARRAY_SIZE(au32);
+ int status;
+
+ seq = c->seqno++;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+
+ msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq,
+ TAG_MSG , MRP_MSG_FIELD_STRING, buf,
+ TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf,
+ TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf),
+ TAG_U16 , MRP_MSG_FIELD_UINT16, seq,
+ TAG_S16 , MRP_MSG_FIELD_SINT16, - seq,
+ TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0,
+ TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1,
+ TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr,
+ TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32,
+ TAG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(c->t, msg);
+ else
+ status = mrp_transport_sendto(c->t, msg, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", seq);
+
+ mrp_msg_unref(msg);
+}
+
+
+void send_data(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_senddata(c->t, &msg, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(c->t, &msg, data_descr->tag,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+void send_raw(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ char msg[256];
+ size_t size;
+ int status;
+
+ size = snprintf(msg, sizeof(msg), "this is message #%u", seq);
+
+ if (c->connect)
+ status = mrp_transport_sendraw(c->t, msg, size);
+ else
+ status = mrp_transport_sendrawto(c->t, msg, size, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send raw message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%u succesfully sent.", seq);
+}
+
+
+
+void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ switch (c->mode) {
+ case MODE_DATA: send_data(c); break;
+ case MODE_RAW: send_raw(c); break;
+ default:
+ case MODE_MESSAGE: send_msg(c);
+ }
+}
+
+
+void client_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ flags = MRP_TRANSPORT_MODE_DATA;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ flags = MRP_TRANSPORT_MODE_RAW;
+ break;
+ default:
+ case MODE_MESSAGE:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ flags = MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->t = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ exit(1);
+ }
+
+ if (!strcmp(c->atype, "unxd")) {
+ char addrstr[] = "unxd:@stream-test-client";
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL);
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", addrstr);
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->t, &addr, alen)) {
+ mrp_log_error("Failed to bind to transport address '%s'.", addrstr);
+ exit(1);
+ }
+ }
+
+ if (c->connect) {
+ if (!mrp_transport_connect(c->t, &c->addr, c->alen)) {
+ mrp_log_error("Failed to connect to %s.", c->addrstr);
+ exit(1);
+ }
+ }
+
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create send timer.");
+ exit(1);
+ }
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -C, --connect connect transport\n"
+ " For connection-oriented transports, this is automatic.\n"
+ " -a, --address address to use\n"
+ " -c, --custom use custom messages\n"
+ " -m, --message use generic messages (default)\n"
+ " -r, --raw use raw messages\n"
+ " -b, --buggy use buggy data descriptors\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->addrstr = "tcp4:127.0.0.1:3000";
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "scmrbCa:l:t:v:d:h"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "address" , required_argument, NULL, 'a' },
+ { "custom" , no_argument , NULL, 'c' },
+ { "message" , no_argument , NULL, 'm' },
+ { "raw" , no_argument , NULL, 'r' },
+ { "connect" , no_argument , NULL, 'C' },
+
+ { "buggy" , no_argument , NULL, 'b' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'c':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_DATA;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'm':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_MESSAGE;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_RAW;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'b':
+ ctx->buggy = TRUE;
+ break;
+
+ case 'C':
+ ctx->connect = TRUE;
+ break;
+
+ case 'a':
+ ctx->addrstr = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using address '%s'...", c.addrstr);
+ else
+ mrp_log_info("Running as client, using address '%s'...", c.addrstr);
+
+ switch (c.mode) {
+ case MODE_DATA: mrp_log_info("Using custom data messages..."); break;
+ case MODE_RAW: mrp_log_info("Using raw messages..."); break;
+ default:
+ case MODE_MESSAGE: mrp_log_info("Using generic messages...");
+ }
+
+ if (!strncmp(c.addrstr, "tcp", 3) || !strncmp(c.addrstr, "unxs", 4) ||
+ !strncmp(c.addrstr, "wsck", 4)) {
+ c.stream = TRUE;
+ c.connect = TRUE;
+ }
+
+ c.alen = mrp_transport_resolve(NULL, c.addrstr,
+ &c.addr, sizeof(c.addr), &c.atype);
+ if (c.alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr);
+ exit(1);
+ }
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.server)
+ server_init(&c);
+ else
+ client_init(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef ECORE_ENABLED
+
+struct ecore_config_s {
+ int dummy;
+};
+
+
+mrp_mainloop_t *ecore_mainloop_create(test_config_t *cfg)
+{
+ cfg->ml = mrp_mainloop_ecore_get();
+
+ return cfg->ml;
+}
+
+
+int ecore_mainloop_run(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ ecore_main_loop_begin();
+
+ return TRUE;
+}
+
+
+int ecore_mainloop_quit(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ ecore_main_loop_quit();
+
+ return TRUE;
+}
+
+
+int ecore_mainloop_cleanup(test_config_t *cfg)
+{
+ mrp_mainloop_unregister(cfg->ml);
+ mrp_mainloop_destroy(cfg->ml);
+
+ cfg->ml = NULL;
+
+ return TRUE;
+}
+
+
+#else
+
+
+mrp_mainloop_t *ecore_mainloop_create(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+int ecore_mainloop_run(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+int ecore_mainloop_quit(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+int ecore_mainloop_cleanup(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("EFL/ecore mainloop support is not available.");
+ exit(1);
+}
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef GLIB_ENABLED
+
+#include <murphy/common/glib-glue.h>
+
+struct glib_config_s {
+ GMainLoop *gml;
+};
+
+
+mrp_mainloop_t *glib_mainloop_create(test_config_t *cfg)
+{
+ glib_config_t *glib;
+ mrp_mainloop_t *ml;
+
+ glib = mrp_allocz(sizeof(*glib));
+
+ if (glib != NULL) {
+ glib->gml = g_main_loop_new(NULL, FALSE);
+ ml = mrp_mainloop_glib_get(glib->gml);
+
+ if (ml != NULL) {
+ cfg->glib = glib;
+ cfg->ml = ml;
+
+ return ml;
+ }
+ else {
+ g_main_loop_unref(glib->gml);
+ mrp_free(glib);
+ }
+ }
+
+ return NULL;
+}
+
+
+int glib_mainloop_run(test_config_t *cfg)
+{
+ if (cfg->glib != NULL) {
+ g_main_loop_run(cfg->glib->gml);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int glib_mainloop_quit(test_config_t *cfg)
+{
+ if (cfg->glib != NULL) {
+ g_main_loop_quit(cfg->glib->gml);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int glib_mainloop_cleanup(test_config_t *cfg)
+{
+ if (cfg->glib != NULL) {
+ mrp_mainloop_unregister(cfg->ml);
+ mrp_mainloop_destroy(cfg->ml);
+ cfg->ml = NULL;
+
+ g_main_loop_unref(cfg->glib->gml);
+ mrp_free(cfg->glib);
+ cfg->glib = NULL;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+#else
+
+
+mrp_mainloop_t *glib_mainloop_create(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+int glib_mainloop_run(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+int glib_mainloop_quit(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+int glib_mainloop_cleanup(test_config_t *cfg)
+{
+ MRP_UNUSED(cfg);
+
+ mrp_log_error("glib mainloop support is not available.");
+ exit(1);
+}
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifdef PULSE_ENABLED
+
+#include <murphy/common/pulse-glue.h>
+
+struct pulse_config_s {
+ pa_mainloop *pa_main;
+ pa_mainloop_api *pa;
+};
+
+
+mrp_mainloop_t *pulse_mainloop_create(test_config_t *cfg)
+{
+ pulse_config_t *pulse;
+ mrp_mainloop_t *ml;
+
+ pulse = mrp_allocz(sizeof(*pulse));
+
+ if (pulse != NULL) {
+ pulse->pa_main = pa_mainloop_new();
+ pulse->pa = pa_mainloop_get_api(pulse->pa_main);
+ ml = mrp_mainloop_pulse_get(pulse->pa);
+
+ if (ml != NULL) {
+ cfg->pulse = pulse;
+ cfg->ml = ml;
+
+ return ml;
+ }
+ else {
+ pa_mainloop_free(pulse->pa_main);
+ mrp_free(pulse);
+ }
+ }
+
+ return NULL;
+}
+
+
+int pulse_mainloop_run(test_config_t *cfg)
+{
+ int retval;
+
+ if (cfg->pulse && cfg->pulse->pa != NULL) {
+ pa_mainloop_run(cfg->pulse->pa_main, &retval);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int pulse_mainloop_quit(test_config_t *cfg)
+{
+ if (cfg->pulse && cfg->pulse->pa) {
+ pa_mainloop_quit(cfg->pulse->pa_main, 0);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int pulse_mainloop_cleanup(test_config_t *cfg)
+{
+ if (cfg->pulse != NULL) {
+ mrp_mainloop_unregister(cfg->ml);
+ mrp_mainloop_destroy(cfg->ml);
+ cfg->ml = NULL;
+
+ pa_mainloop_free(cfg->pulse->pa_main);
+ mrp_free(cfg->pulse);
+ cfg->pulse = NULL;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+#else
+
+
+mrp_mainloop_t *pulse_mainloop_create(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+int pulse_mainloop_run(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+int pulse_mainloop_quit(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+int pulse_mainloop_cleanup(test_config_t *cfg)
+{
+ mrp_log_error("PulseAudio mainloop support is not available.");
+ exit(1);
+}
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/config.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+#include "mainloop-qt-test.h"
+
+#include <QCoreApplication>
+#include <murphy/common/qt-glue.h>
+
+
+typedef struct qt_config_s {
+ QCoreApplication *app;
+ mrp_mainloop_t *ml;
+} qt_config_t ;
+
+static qt_config_t *qt;
+
+mrp_mainloop_t *qt_mainloop_create()
+{
+ mrp_mainloop_t *ml = NULL;
+
+ if (qt == NULL) {
+ int argc = 0;
+ char **argv = 0;
+
+ qt = (qt_config_t *)mrp_allocz(sizeof(*qt));
+ if (!qt) return NULL;
+
+ qt->app = new QCoreApplication(argc, argv);
+ ml = mrp_mainloop_qt_get();
+
+ if (ml == NULL) {
+ delete qt->app;
+ mrp_free(qt);
+ qt = NULL;
+ }
+ }
+ else {
+ return qt->ml;
+ }
+
+ return ml;
+}
+
+int qt_mainloop_run()
+{
+ if (qt != NULL) {
+ QCoreApplication::exec();
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int qt_mainloop_quit()
+{
+ if (qt != NULL) {
+ QCoreApplication::quit();
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+int qt_mainloop_cleanup(mrp_mainloop_t *ml)
+{
+ if (qt != NULL) {
+ mrp_mainloop_unregister(ml);
+ mrp_mainloop_destroy(ml);
+
+ delete qt->app;
+ mrp_free(qt);
+ qt = NULL;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_QT_TEST_H_
+#define __MURPHY_QT_TEST_H_
+
+#include <murphy/config.h>
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+mrp_mainloop_t *qt_mainloop_create(void);
+int qt_mainloop_run(void);
+int qt_mainloop_quit(void);
+int qt_mainloop_cleanup(mrp_mainloop_t *ml);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_QT_TEST_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <dbus/dbus.h>
+
+#include <murphy/config.h>
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+#ifdef PULSE_ENABLED
+# include <pulse/mainloop.h>
+# include <murphy/common/pulse-glue.h>
+#endif
+
+#ifdef ECORE_ENABLED
+# include <Ecore.h>
+# include <murphy/common/ecore-glue.h>
+#endif
+
+#ifdef GLIB_ENABLED
+# include <glib.h>
+# include "glib-pump.c"
+# include <murphy/common/glib-glue.h>
+#endif
+
+#ifdef QT_ENABLED
+#include <murphy/common/qt-glue.h>
+#endif
+
+#define info(fmt, args...) do { \
+ fprintf(stdout, "I: "fmt"\n" , ## args); \
+ fflush(stdout); \
+ } while (0)
+
+#define warning(fmt, args...) do { \
+ fprintf(stderr, "W: "fmt"\n" , ## args); \
+ fflush(stderr); \
+ } while (0)
+
+#define error(fmt, args...) do { \
+ fprintf(stderr, "E: "fmt"\n" , ## args); \
+ fflush(stderr); \
+ } while (0)
+
+#define fatal(fmt, args...) do { \
+ fprintf(stderr, "C: "fmt"\n" , ## args); \
+ fflush(stderr); \
+ exit(1); \
+ } while (0)
+
+#define USECS_PER_SEC (1000 * 1000)
+
+#define DEFAULT_RUNTIME 30 /* run for 30 seconds */
+
+
+enum {
+ MAINLOOP_NATIVE,
+ MAINLOOP_PULSE,
+ MAINLOOP_ECORE,
+ MAINLOOP_GLIB,
+ MAINLOOP_QT
+};
+
+
+struct pulse_config_s;
+typedef struct pulse_config_s pulse_config_t;
+
+struct ecore_config_s;
+typedef struct ecore_config_s ecore_config_t;
+
+struct glib_config_s;
+typedef struct glib_config_s glib_config_t;
+
+
+
+typedef struct {
+ int nio;
+ int ntimer;
+ int deferred;
+ int nsignal;
+
+ int ngio;
+ int ngtimer;
+
+ int ndbus_method;
+ int ndbus_signal;
+
+ int log_mask;
+ const char *log_target;
+
+ int mainloop_type;
+
+ mrp_mainloop_t *ml;
+ pulse_config_t *pulse;
+ ecore_config_t *ecore;
+ glib_config_t *glib;
+
+ int nrunning;
+ int runtime;
+
+ pid_t child;
+ unsigned int wlpf;
+ unsigned int wfrc;
+} test_config_t;
+
+
+#include "mainloop-pulse-test.c"
+#include "mainloop-ecore-test.c"
+#include "mainloop-glib-test.c"
+#include "mainloop-qt-test.h"
+
+static test_config_t cfg;
+
+
+static mrp_mainloop_t *mainloop_create(test_config_t *cfg);
+static void mainloop_run(test_config_t *cfg);
+static void mainloop_quit(test_config_t *cfg);
+static void mainloop_cleanup(test_config_t *cfg);
+
+
+/*
+ * native timers
+ */
+
+#define TIMER_INTERVALS 1, 2, 3, 4, 6, 8, 1, 3, 12, 15, 18, 21, 24
+
+
+typedef struct {
+ int id;
+ mrp_timer_t *timer;
+ int interval;
+ int count;
+ int target;
+ struct timeval prev;
+} test_timer_t;
+
+
+static test_timer_t *timers;
+
+static mrp_wakeup_t *wakeup;
+static mrp_wakeup_t *wuplim;
+
+
+
+static int timeval_diff(struct timeval *tv1, struct timeval *tv2)
+{
+ int64_t u1, u2;
+
+ u1 = tv1->tv_sec * USECS_PER_SEC + tv1->tv_usec;
+ u2 = tv2->tv_sec * USECS_PER_SEC + tv2->tv_usec;
+
+ return (int)(u1 - u2);
+}
+
+
+static void timeval_now(struct timeval *tv)
+{
+ gettimeofday(tv, NULL);
+}
+
+
+void timer_cb(mrp_timer_t *timer, void *user_data)
+{
+ test_timer_t *t = (test_timer_t *)user_data;
+ struct timeval now;
+ double diff, error;
+
+ MRP_UNUSED(timer);
+
+ timeval_now(&now);
+ diff = timeval_diff(&now, &t->prev) / 1000.0;
+ error = diff - t->interval;
+ if (error < 0.0)
+ error = -error;
+
+ info("MRPH timer #%d: %d/%d, diff %.2f (lag %.2f, %.3f %%)",
+ t->id, t->count, t->target, diff, error, 100 * error / diff);
+
+ t->count++;
+ t->prev = now;
+
+ if (t->count >= t->target) {
+ info("MRPH timer #%d has finished.", t->id);
+
+ mrp_del_timer(t->timer);
+ t->timer = NULL;
+ cfg.nrunning--;
+ }
+}
+
+
+static void setup_timers(mrp_mainloop_t *ml)
+{
+ test_timer_t *t;
+ int intervals[] = { TIMER_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+ if ((timers = mrp_allocz_array(test_timer_t, cfg.ntimer)) != NULL) {
+ for (i = 0, t = timers; i < cfg.ntimer; i++, t++) {
+ t->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->interval = msecs;
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ timeval_now(&t->prev);
+ t->timer = mrp_add_timer(ml, t->interval, timer_cb, t);
+
+ if (t->timer != NULL)
+ info("MRPH timer #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+ else
+ fatal("MRPH timer #%d: failed to create", t->id);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.ntimer > 0)
+ fatal("could not allocate %d timers", cfg.ntimer);
+}
+
+
+static void check_timers(void)
+{
+ test_timer_t *t;
+ int i;
+
+ for (i = 0, t = timers; i < cfg.ntimer; i++, t++) {
+ if (t->target != 0 && t->count != t->target)
+ warning("MRPH timer #%d: FAIL (only %d/%d)", t->id, t->count,
+ t->target);
+ else
+ info("MRPH timer #%d: OK (%d/%d)", t->id, t->count, t->target);
+ }
+}
+
+
+/*
+ * native I/O
+ */
+
+#define IO_INTERVALS 1, 3, 5, 9, 12, 15, 18, 21
+
+typedef struct {
+ int id;
+ int pipe[2];
+ mrp_io_watch_t *watch;
+ mrp_timer_t *timer;
+ int target;
+ int sent;
+ int received;
+} test_io_t;
+
+
+static test_io_t *ios;
+
+
+static void send_io(mrp_timer_t *timer, void *user_data)
+{
+ test_io_t *w = (test_io_t *)user_data;
+ char buf[1024];
+ int plural, size;
+
+ MRP_UNUSED(timer);
+
+ plural = (w->target - w->sent) != 1;
+ size = snprintf(buf, sizeof(buf),
+ "I/O #%d: %d message%s remain%s.", w->id,
+ w->target - w->sent,
+ plural ? "s" : "", plural ? "" : "s");
+
+ if (write(w->pipe[1], buf, size) < 0) {
+ /* just ignore it... */
+ }
+ w->sent++;
+
+ info("MRPH I/O #%d: sent message %d/%d.", w->id, w->sent, w->target);
+
+ if (w->sent >= w->target) {
+ info("MRPH I/O #%d: sending done.", w->id);
+
+ close(w->pipe[1]);
+ mrp_del_timer(timer);
+ w->timer = NULL;
+
+ cfg.nrunning--;
+ }
+}
+
+
+static void recv_io(mrp_io_watch_t *watch, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ test_io_t *w = (test_io_t *)user_data;
+ char buf[1024];
+ int size;
+
+ MRP_UNUSED(watch);
+
+ if (watch != w->watch)
+ fatal("MRPH I/O #%d called with incorrect data.", w->id);
+
+ if (events & MRP_IO_EVENT_IN) {
+ size = read(fd, buf, sizeof(buf) - 1);
+
+ if (size > 0) {
+ w->received++;
+ buf[size] = '\0';
+ info("MRPH I/O #%d: received message [%s]", w->id, buf);
+ }
+ else
+ warning("MRPH I/O #%d: got empty message", w->id);
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ info("MRPH I/O #%d: receiver done (got %d/%d)", w->id, w->received,
+ w->sent);
+ close(w->pipe[0]);
+ mrp_del_io_watch(watch);
+ }
+}
+
+
+void setup_io(mrp_mainloop_t *ml)
+{
+ test_io_t *w;
+ mrp_io_event_t mask;
+ int intervals[] = { IO_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+
+ if ((ios = mrp_allocz_array(test_io_t, cfg.nio)) != NULL) {
+ mask = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP;
+
+ for (i = 0, w = ios; i < cfg.nio; i++, w++) {
+ w->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ w->target = 1000 * cfg.runtime / msecs;
+ if (!w->target)
+ continue;
+
+ if (pipe(w->pipe) != 0)
+ fatal("MRPH I/O #%d: could not create pipe", w->id);
+
+ w->watch = mrp_add_io_watch(ml, w->pipe[0], mask, recv_io, w);
+ w->timer = mrp_add_timer(ml, msecs, send_io, w);
+
+ if (w->timer == NULL)
+ fatal("MRPH I/O #%d: could not create I/O timer", w->id);
+
+ if (w->watch == NULL)
+ fatal("MRPH I/O #%d: could not create I/O watch", w->id);
+ else
+ info("MRPH I/O #%d: interval=%d, target=%d", w->id, *iv,
+ w->target);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.nio > 0)
+ fatal("could not allocate %d I/O watches", cfg.nio);
+}
+
+
+static void check_io(void)
+{
+ test_io_t *w;
+ int i;
+
+ for (i = 0, w = ios; i < cfg.nio; i++, w++) {
+ if (w->target != 0 && w->sent != w->received)
+ warning("MRPH I/O #%d: FAIL (only %d/%d)", w->id, w->received,
+ w->sent);
+ else
+ info("MRPH I/O #%d: OK (%d/%d)", w->id, w->received, w->sent);
+ }
+}
+
+
+/*
+ * native deferred/idle callbacks
+ */
+
+
+static void setup_deferred(void)
+{
+ return;
+}
+
+
+
+/*
+ * native signals
+ */
+
+#define SIG_INTERVALS 1, 5, 9, 3, 6, 12
+#define SIGNUMS { SIGUSR1, SIGUSR2, SIGTERM, SIGCONT, SIGQUIT, 0 }
+
+static const char *signames[] = {
+ [SIGINT] = "SIGINT", [SIGTERM] = "SIGTERM", [SIGQUIT] = "SIGQUIT",
+ [SIGCONT] = "SIGCONT", [SIGUSR1] = "SIGUSR1", [SIGUSR2] = "SIGUSR2",
+ [SIGCHLD] = "SIGCHLD"
+};
+
+
+typedef struct {
+ int id;
+ int signum;
+ mrp_sighandler_t *watch;
+ mrp_timer_t *timer;
+ int target;
+ int sent;
+ int received;
+} test_signal_t;
+
+test_signal_t *signals;
+
+
+static void send_signal(mrp_timer_t *timer, void *user_data)
+{
+ test_signal_t *t = (test_signal_t *)user_data;
+
+ MRP_UNUSED(timer);
+
+ if (t->sent >= t->target)
+ return;
+
+ kill(getpid(), t->signum);
+ t->sent++;
+ info("MRPH signal #%d: sent signal %d/%d of %s", t->id,
+ t->sent, t->target, strsignal(t->signum));
+
+ if (t->sent >= t->target) {
+ info("MRPH signal #%d: sending done", t->id);
+ mrp_del_timer(t->timer);
+ t->timer = NULL;
+ }
+}
+
+
+static void recv_signal(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ test_signal_t *t = (test_signal_t *)user_data;
+
+ MRP_UNUSED(h);
+
+ if (h != t->watch)
+ fatal("MRPH signal #%d called with incorrect data", t->id);
+
+ t->received++;
+ info("MRPH signal #%d: received signal %d/%d of %s", t->id,
+ t->received, t->target, signames[signum]);
+
+ if (t->sent >= t->target) {
+ info("MRPH signal #%d: receiving done", t->id);
+ cfg.nrunning--;
+ }
+}
+
+
+static void setup_signals(mrp_mainloop_t *ml)
+{
+ test_signal_t *t;
+ int intervals[] = { SIG_INTERVALS, 0 }, *iv = intervals;
+ int signums[] = SIGNUMS, *s = signums;
+ int msecs, i;
+
+ if ((signals = mrp_allocz_array(test_signal_t, cfg.nsignal)) != NULL) {
+ for (i = 0, t = signals; i < cfg.nsignal; i++, t++) {
+ t->id = i;
+ t->signum = *s;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ t->watch = mrp_add_sighandler(ml, *s, recv_signal, t);
+ t->timer = mrp_add_timer(ml, msecs, send_signal, t);
+
+ if (t->timer == NULL)
+ fatal("MRPH signal #%d: could not create timer", t->id);
+
+ if (t->watch == NULL)
+ fatal("MRPH signal #%d: could not create watch", t->id);
+ else
+ info("MRPH signal #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+
+ s++;
+ if (!*s)
+ s = signums;
+ }
+ }
+ else
+ if (cfg.nsignal > 0)
+ fatal("could not allocate %d signal watches", cfg.nsignal);
+}
+
+
+static void check_signals(void)
+{
+ test_signal_t *t;
+ int i;
+
+ for (i = 0, t = signals; i < cfg.nsignal; i++, t++) {
+ if (t->sent < t->received)
+ warning("MRPH signal #%d: FAIL (only %d/%d", t->id,
+ t->received, t->sent);
+ else
+ info("MRPH signal #%d: OK (%d/%d)", t->id, t->received, t->sent);
+ }
+}
+
+
+static void wakeup_cb(mrp_wakeup_t *w, mrp_wakeup_event_t event,
+ void *user_data)
+{
+ static struct timeval prev[2] = { {0, 0}, {0, 0} };
+ const char *evt;
+ struct timeval now;
+ double diff;
+ int id;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(user_data);
+
+ timeval_now(&now);
+
+ switch (event) {
+ case MRP_WAKEUP_EVENT_TIMER: evt = "timer"; break;
+ case MRP_WAKEUP_EVENT_IO: evt = "I/O (or signal)"; break;
+ case MRP_WAKEUP_EVENT_LIMIT: evt = "limit"; break;
+ default: evt = "???";
+ }
+
+ id = user_data ? 1 : 0;
+
+ if (MRP_LIKELY(prev[id].tv_usec != 0)) {
+ diff = timeval_diff(&now, &prev[id]) / 1000.0;
+ info("woken up #%d by %s, %.2f msecs since previous", id, evt, diff);
+ }
+
+ prev[id] = now;
+}
+
+
+static void setup_wakeup(mrp_mainloop_t *ml)
+{
+ unsigned int nolim = MRP_WAKEUP_NOLIMIT;
+
+ if (cfg.child == 0)
+ return;
+
+ wakeup = mrp_add_wakeup(ml, MRP_WAKEUP_EVENT_ANY, nolim, nolim,
+ wakeup_cb, (void *)0);
+ wuplim = mrp_add_wakeup(ml, MRP_WAKEUP_EVENT_ANY, cfg.wlpf, cfg.wfrc,
+ wakeup_cb, (void *)1);
+}
+
+
+static void cleanup_wakeup(void)
+{
+ mrp_del_wakeup(wakeup);
+ wakeup = NULL;
+ mrp_del_wakeup(wuplim);
+ wuplim = NULL;
+}
+
+
+static void check_quit(mrp_timer_t *timer, void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ if (cfg.nrunning <= 0) {
+ mrp_del_timer(timer);
+ mainloop_quit(&cfg);
+ }
+}
+
+
+
+#ifdef GLIB_ENABLED
+/*
+ * glib timers
+ */
+
+#define GTIMER_INTERVALS 1, 2, 3, 4, 6, 8, 1, 3, 12, 15, 18, 21, 24
+
+typedef struct {
+ int id;
+ guint gsrc;
+ int interval;
+ int count;
+ int target;
+ struct timeval prev;
+} glib_timer_t;
+
+
+static glib_timer_t *gtimers;
+
+
+static gboolean glib_timer_cb(gpointer user_data)
+{
+ glib_timer_t *t = (glib_timer_t *)user_data;
+ struct timeval now;
+ double diff, error;
+
+ timeval_now(&now);
+ diff = timeval_diff(&now, &t->prev) / 1000.0;
+ error = diff - t->interval;
+ if (error < 0.0)
+ error = -error;
+
+ info("GLIB timer #%d: %d/%d, diff %.2f (lag %.2f, %.3f %%)",
+ t->id, t->count, t->target, diff, error, 100 * error / diff);
+
+ t->count++;
+ t->prev = now;
+
+ if (t->count >= t->target) {
+ info("GLIB timer #%d has finished.", t->id);
+
+ t->gsrc = 0;
+ cfg.nrunning--;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+static void setup_glib_timers(void)
+{
+ glib_timer_t *t;
+ int intervals[] = { GTIMER_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+ if ((gtimers = mrp_allocz_array(glib_timer_t, cfg.ngtimer)) != NULL) {
+ for (i = 0, t = gtimers; i < cfg.ngtimer; i++, t++) {
+ t->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->interval = msecs;
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ timeval_now(&t->prev);
+ t->gsrc = g_timeout_add(msecs, glib_timer_cb, t);
+
+ if (t->gsrc != 0)
+ info("GLIB timer #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+ else
+ fatal("GLIB timer #%d: failed to create", t->id);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.ntimer > 0)
+ fatal("could not allocate %d GLIB timers", cfg.ngtimer);
+}
+
+
+static void check_glib_timers(void)
+{
+ glib_timer_t *t;
+ int i;
+
+ for (i = 0, t = gtimers; i < cfg.ngtimer; i++, t++) {
+ if (t->target != 0 && t->count != t->target)
+ warning("GLIB timer #%d: FAIL (only %d/%d)", t->id, t->count,
+ t->target);
+ else
+ info("GLIB timer #%d: OK (%d/%d)", t->id, t->count, t->target);
+ }
+}
+
+
+/*
+ * glib I/O
+ */
+
+#define GIO_INTERVALS 1, 3, 4, 5, 6, 7, 9, 12, 15, 18, 21
+
+typedef struct {
+ int id;
+ int pipe[2];
+ GIOChannel *gioc;
+ guint gsrc;
+ guint timer;
+ int target;
+ int sent;
+ int received;
+} glib_io_t;
+
+
+static glib_io_t *gios;
+
+
+static gboolean glib_send_io(gpointer user_data)
+{
+ glib_io_t *t = (glib_io_t *)user_data;
+ char buf[1024];
+ int plural, size;
+
+ plural = (t->target - t->sent) != 1;
+ size = snprintf(buf, sizeof(buf),
+ "I/O #%d: %d message%s remain%s.", t->id,
+ t->target - t->sent,
+ plural ? "s" : "", plural ? "" : "s");
+
+ if (write(t->pipe[1], buf, size) < 0) {
+ /* just ignore it... */
+ }
+ t->sent++;
+
+ info("GLIB I/O #%d: sent message %d/%d.", t->id, t->sent, t->target);
+
+ if (t->sent >= t->target) {
+ info("GLIB I/O #%d: sending done.", t->id);
+
+ close(t->pipe[1]);
+ t->timer = 0;
+
+ cfg.nrunning--;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+static gboolean glib_recv_io(GIOChannel *ioc, GIOCondition cond,
+ gpointer user_data)
+{
+ glib_io_t *t = (glib_io_t *)user_data;
+ int fd = g_io_channel_unix_get_fd(ioc);
+ char buf[1024];
+ int size;
+
+ if (cond & G_IO_IN) {
+ size = read(fd, buf, sizeof(buf) - 1);
+
+ if (size > 0) {
+ t->received++;
+ buf[size] = '\0';
+ info("GLIB I/O #%d: received message [%s]", t->id, buf);
+ }
+ else
+ warning("GLIB I/O #%d: got empty message", t->id);
+ }
+
+ if (cond & G_IO_HUP) {
+ info("GLIB I/O #%d: receiver done (got %d/%d)", t->id, t->received,
+ t->sent);
+ close(fd);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+void setup_glib_io(void)
+{
+ glib_io_t *t;
+ GIOCondition cond;
+ int intervals[] = { GIO_INTERVALS, 0 }, *iv = intervals;
+ int msecs, i;
+
+ if ((gios = mrp_allocz_array(glib_io_t, cfg.ngio)) != NULL) {
+ cond = G_IO_IN | G_IO_HUP;
+
+ for (i = 0, t = gios; i < cfg.ngio; i++, t++) {
+ t->id = i;
+
+ msecs = *iv;
+ while (cfg.runtime / msecs < 1 && msecs > 0)
+ msecs /= 2;
+ msecs *= 1000;
+ if (!msecs)
+ msecs = 500;
+
+ t->target = 1000 * cfg.runtime / msecs;
+ if (!t->target)
+ continue;
+
+ if (pipe(t->pipe) != 0)
+ fatal("GLIB I/O #%d: could not create pipe", t->id);
+
+ t->gioc = g_io_channel_unix_new(t->pipe[0]);
+ if (t->gioc == NULL)
+ fatal("GLIB I/O #%d: failed to create I/O channel", t->id);
+
+ t->gsrc = g_io_add_watch(t->gioc, cond, glib_recv_io, t);
+ if (t->gsrc == 0)
+ fatal("GLIB I/O #%d: failed to add I/O watch", t->id);
+
+ t->timer = g_timeout_add(msecs, glib_send_io, t);
+ if (t->timer == 0)
+ fatal("GLIB I/O #%d: could not create I/O timer", t->id);
+
+ info("GLIB I/O #%d: interval=%d, target=%d", t->id, *iv,
+ t->target);
+
+ cfg.nrunning++;
+ iv++;
+ if (!*iv)
+ iv = intervals;
+ }
+ }
+ else
+ if (cfg.ngio > 0)
+ fatal("could not allocate %d glib I/O watches", cfg.ngio);
+}
+
+
+static void check_glib_io(void)
+{
+ glib_io_t *t;
+ int i;
+
+ for (i = 0, t = gios; i < cfg.ngio; i++, t++) {
+ if (t->target != 0 && t->sent != t->received) {
+ warning("GLIB I/O #%d (fd %d): FAIL (only %d/%d)",
+ t->id, t->pipe[0], t->received, t->sent);
+ }
+
+ else
+ info("GLIB I/O #%d (fd %d): OK (%d/%d)", t->id, t->pipe[0],
+ t->received, t->sent);
+ }
+}
+
+static void glib_pump_cleanup(void);
+#endif
+
+
+/*
+ * DBUS tests (quite a mess the whole shebang...)
+ */
+
+#define DBUS_PATH "/"
+#define DBUS_IFACE "org.murphy.test"
+#define DBUS_METHOD "message"
+#define DBUS_SIGNAL "signal"
+
+typedef struct {
+ int pipe[2];
+ pid_t client;
+ char address[256];
+
+ mrp_mainloop_t *ml;
+ DBusConnection *conn;
+ mrp_timer_t *sigtimer;
+
+ int nmethod;
+ int nack;
+ int nsignal;
+} dbus_test_t;
+
+
+static dbus_test_t dbus_test = { pipe: { -1, -1 } };
+
+
+
+
+int mrp_setup_dbus_connection(mrp_mainloop_t *ml, DBusConnection *conn);
+
+
+static void open_dbus_pipe(void)
+{
+ if (pipe(dbus_test.pipe) < 0)
+ fatal("failed to opend pipe for DBUS tests");
+}
+
+
+static void close_dbus_pipe(char *dir)
+{
+ while (*dir) {
+ switch (*dir++) {
+ case 'r':
+ if (dbus_test.pipe[0] != -1) {
+ close(dbus_test.pipe[0]);
+ dbus_test.pipe[0] = -1;
+ }
+ break;
+
+ case 'w':
+ if (dbus_test.pipe[1] != -1) {
+ close(dbus_test.pipe[1]);
+ dbus_test.pipe[1] = -1;
+ }
+ break;
+ }
+ }
+}
+
+
+static void recv_dbus_reply(DBusPendingCall *pending, void *user_data)
+{
+ DBusMessage *msg;
+ char *reply;
+
+ MRP_UNUSED(user_data);
+
+ if ((msg = dbus_pending_call_steal_reply(pending)) != NULL) {
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING,
+ &reply, DBUS_TYPE_INVALID)) {
+ info("DBUS test: got reply #%d '%s'", dbus_test.nack, reply);
+ dbus_test.nack++;
+ }
+
+ dbus_message_unref(msg);
+ }
+
+ dbus_pending_call_unref(pending);
+
+ if (dbus_test.nack >= cfg.ndbus_method) {
+ char dummy[256];
+
+ cfg.nrunning--;
+
+ /* block until the client is done */
+ if (read(dbus_test.pipe[0], dummy, sizeof(dummy)) < 0) {
+ /* just ignore it... */
+ }
+ }
+}
+
+
+static int send_dbus_message(DBusConnection *conn, char *addr, char *buf)
+{
+ DBusMessage *msg;
+ DBusPendingCall *pending;
+
+ msg = dbus_message_new_method_call(addr, DBUS_PATH,
+ DBUS_IFACE, DBUS_METHOD);
+
+ if (msg == NULL)
+ fatal("failed to create DBUS message");
+
+ if (!dbus_message_append_args(msg,
+ DBUS_TYPE_STRING, &buf, DBUS_TYPE_INVALID))
+ fatal("failed to add arguments to DBUS method call");
+
+ if (!dbus_connection_send_with_reply(conn, msg, &pending, 5000))
+ fatal("failed to send DBUS message");
+
+ if (!dbus_pending_call_set_notify(pending, recv_dbus_reply, NULL, NULL))
+ fatal("failed to set pending call notification callback");
+
+ dbus_message_unref(msg);
+
+ return TRUE;
+}
+
+
+static int send_dbus_reply(DBusConnection *conn, DBusMessage *msg, char *buf)
+{
+ DBusMessage *reply;
+
+ if ((reply = dbus_message_new_method_return(msg)) != NULL) {
+ if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &buf,
+ DBUS_TYPE_INVALID))
+ fatal("failed to add arguments to DBUS method reply");
+
+ if (!dbus_connection_send(conn, reply, NULL))
+ fatal("failed to send DBUS reply");
+
+ dbus_message_unref(reply);
+ }
+
+ dbus_test.nmethod++;
+ if (dbus_test.nmethod >= cfg.ndbus_method)
+ cfg.nrunning--;
+
+ return TRUE;
+}
+
+
+static DBusConnection *connect_to_dbus(char *name)
+{
+ DBusConnection *conn;
+ DBusError error;
+ unsigned int flags;
+ int status;
+
+ dbus_error_init(&error);
+
+ if ((conn = dbus_bus_get(DBUS_BUS_SESSION, NULL)) != NULL) {
+ if (!name || !*name)
+ return conn;
+
+ flags = DBUS_NAME_FLAG_REPLACE_EXISTING | DBUS_NAME_FLAG_DO_NOT_QUEUE;
+ status = dbus_bus_request_name(conn, name, flags, &error);
+
+ if (status == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ return conn;
+ else
+ error("failed to get name '%s' on DBUS (error: %s)", name,
+ error.message ? error.message : "unknown");
+ }
+
+ return NULL;
+}
+
+
+static void client_send_msg(mrp_timer_t *t, void *user_data)
+{
+ char buf[1024];
+
+ MRP_UNUSED(user_data);
+
+ if (dbus_test.nmethod < cfg.ndbus_method) {
+ snprintf(buf, sizeof(buf), "DBUS message #%d", dbus_test.nmethod);
+ send_dbus_message(dbus_test.conn, dbus_test.address, buf);
+
+ info("DBUS client: sent #%d message", dbus_test.nmethod);
+
+ dbus_test.nmethod++;
+ }
+
+ if (dbus_test.nmethod >= cfg.ndbus_method) {
+ mrp_del_timer(t);
+ if (cfg.ndbus_method == 0)
+ cfg.nrunning--;
+ else {
+ /* cfg.nrunning updated only once we've received the last reply */
+ }
+ }
+}
+
+
+static void setup_dbus_client(mrp_mainloop_t *ml)
+{
+ DBusConnection *conn;
+ int i, nmethod, nsignal;
+ size_t size;
+ ssize_t amount_read;
+
+ nmethod = cfg.ndbus_method;
+ nsignal = cfg.ndbus_signal;
+ mrp_clear(&cfg);
+ cfg.ndbus_method = nmethod;
+ cfg.ndbus_signal = nsignal;
+
+ mrp_mainloop_quit(ml, 0);
+#ifdef GLIB_ENABLED
+ glib_pump_cleanup();
+#endif
+ mrp_mainloop_destroy(ml);
+
+ for (i = 3; i < 1024; i++)
+ if (i != dbus_test.pipe[0])
+ close(i);
+
+ size = sizeof(dbus_test.address) - 1;
+ amount_read = read(dbus_test.pipe[0], dbus_test.address, size);
+ if (amount_read > 0) {
+ dbus_test.address[amount_read] = '\0';
+ info("DBUS test: got address '%s'", dbus_test.address);
+ }
+
+ /*sleep(5);*/
+
+ if ((ml = dbus_test.ml = mrp_mainloop_create()) == NULL)
+ fatal("failed to create mainloop");
+
+ cfg.ml = ml;
+
+ if ((conn = dbus_test.conn = connect_to_dbus(NULL)) == NULL)
+ fatal("failed to connect to DBUS");
+
+ if (!mrp_setup_dbus_connection(ml, conn))
+ fatal("failed to setup DBUS connection with mainloop");
+
+ if (mrp_add_timer(ml, 1000, client_send_msg, NULL) == NULL)
+ fatal("failed to create DBUS message sending timer");
+
+ if (mrp_add_timer(ml, 1000, check_quit, NULL) == NULL)
+ fatal("failed to create quit-check timer");
+
+ cfg.nrunning++;
+}
+
+
+static DBusHandlerResult dispatch_method(DBusConnection *c,
+ DBusMessage *msg, void *data)
+{
+#define SAFESTR(str) (str ? str : "<none>")
+ const char *path = dbus_message_get_path(msg);
+ const char *interface = dbus_message_get_interface(msg);
+ const char *member = dbus_message_get_member(msg);
+ const char *message;
+ char reply[1024];
+
+ MRP_UNUSED(data);
+
+ if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL || !member)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (strcmp(path, DBUS_PATH) ||
+ strcmp(interface, DBUS_IFACE) ||
+ strcmp(member, DBUS_METHOD))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /*info("DBUS server: got call: path='%s', interface='%s', member='%s')...",
+ SAFESTR(path), SAFESTR(interface), SAFESTR(member));*/
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &message, DBUS_TYPE_INVALID)) {
+ snprintf(reply, sizeof(reply), "ACK: got '%s'", message);
+ if (!send_dbus_reply(c, msg, reply))
+ fatal("failed to sent reply to DBUS message");
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+
+
+static void setup_dbus_server(mrp_mainloop_t *ml)
+{
+ static struct DBusObjectPathVTable vtable = {
+ .message_function = dispatch_method
+ };
+
+ char *addr = "org.murphy.test";
+
+ MRP_UNUSED(ml);
+
+ if ((dbus_test.conn = connect_to_dbus(addr)) == NULL)
+ fatal("failed to connect to DBUS");
+
+ if (!mrp_setup_dbus_connection(ml, dbus_test.conn))
+ fatal("failed to setup DBUS connection with mainloop");
+
+ if (!dbus_connection_register_fallback(dbus_test.conn, "/", &vtable, NULL))
+ fatal("failed to set up method dispatching");
+
+ if (write(dbus_test.pipe[1], addr, strlen(addr) + 1) < 0) {
+ /* just ignore it... */
+ }
+
+ cfg.nrunning++;
+}
+
+
+
+static void fork_dbus_client(mrp_mainloop_t *ml)
+{
+ dbus_test.client = cfg.child = fork();
+
+ switch (dbus_test.client) {
+ case -1:
+ fatal("failed to fork DBUS test client");
+ break;
+
+ case 0:
+ setup_dbus_client(ml);
+ break;
+
+ default:
+ info("DBUS test: child pid %u", dbus_test.client);
+ close(0);
+ /*sleep(10);*/
+ setup_dbus_server(ml);
+ }
+}
+
+
+static void sigchild_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ int status;
+
+ MRP_UNUSED(user_data);
+
+ info("DBUS test: received signal %d (%s)", signum, signames[signum]);
+
+ if (dbus_test.client != 0) {
+ if (waitpid(dbus_test.client, &status, WNOHANG) == dbus_test.client) {
+ info("DBUS test: client exited with status %d.", status);
+ dbus_test.client = 0;
+ close_dbus_pipe("w");
+ mrp_del_sighandler(h);
+ cfg.nrunning--;
+ }
+ else
+ error("waitpid failed for pid %u", dbus_test.client);
+ }
+}
+
+
+static void setup_dbus_tests(mrp_mainloop_t *ml)
+{
+ mrp_sighandler_t *h;
+
+ if (cfg.ndbus_method == 0 && cfg.ndbus_signal == 0)
+ return;
+
+ if ((h = mrp_add_sighandler(ml, SIGCHLD, sigchild_handler, NULL)) != NULL) {
+ open_dbus_pipe();
+ fork_dbus_client(ml);
+ }
+ else
+ fatal("failed create SIGCHLD handler");
+}
+
+
+static void check_dbus(void)
+{
+ if (cfg.ndbus_method == 0 && cfg.ndbus_signal == 0)
+ return;
+
+ if (dbus_test.client != 0) {
+ if (dbus_test.nmethod == cfg.ndbus_method)
+ info("DBUS test: method calls: OK (%d/%d)",
+ dbus_test.nmethod, cfg.ndbus_method);
+ else
+ error("DBUS test: method calls: FAILED (%d/%d)",
+ dbus_test.nmethod, cfg.ndbus_method);
+ }
+ else {
+ if (dbus_test.nack == cfg.ndbus_method)
+ info("DBUS test: method replies: OK (%d/%d)",
+ dbus_test.nack, cfg.ndbus_method);
+ else
+ error("DBUS test: method replies: FAILED (%d/%d)",
+ dbus_test.nack, cfg.ndbus_method);
+ }
+}
+
+
+
+#include "dbus-pump.c"
+
+
+
+static void config_set_defaults(test_config_t *cfg)
+{
+ mrp_clear(cfg);
+
+ cfg->nio = 5;
+ cfg->ntimer = 10;
+ cfg->nsignal = 5;
+ cfg->ngio = 5;
+ cfg->ngtimer = 10;
+
+ cfg->ndbus_method = 10;
+ cfg->ndbus_signal = 10;
+
+ cfg->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ cfg->log_target = MRP_LOG_TO_STDERR;
+
+ cfg->wlpf = 1750;
+ cfg->wfrc = 5000;
+
+ cfg->runtime = DEFAULT_RUNTIME;
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -r, --runtime how many seconds to run tests\n"
+ " -i, --ios number of I/O watches\n"
+ " -t, --timers number of timers\n"
+ " -s, --signals number of POSIX signals\n"
+ " -I, --glib-ios number of glib I/O watches\n"
+ " -T, --glib-timers number of glib timers\n"
+ " -S, --dbus-signals number of D-Bus signals\n"
+ " -M, --dbus-methods number of D-Bus methods\n"
+ " -o, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug site enable debug messages for <site>\n"
+#ifdef PULSE_ENABLED
+ " -p, --pulse use pulse mainloop\n"
+#endif
+#ifdef ECORE_ENABLED
+ " -e, --ecore use ecore mainloop\n"
+#endif
+#ifdef GLIB_ENABLED
+ " -g, --glib use glib mainloop\n"
+#endif
+#ifdef QT_ENABLED
+ " -q, --qt use qt mainloop\n"
+#endif
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+int parse_cmdline(test_config_t *cfg, int argc, char **argv)
+{
+#ifdef PULSE_ENABLED
+# define PULSE_OPTION "p"
+#else
+# define PULSE_OPTION ""
+#endif
+#ifdef ECORE_ENABLED
+# define ECORE_OPTION "e"
+#else
+# define ECORE_OPTION ""
+#endif
+#ifdef GLIB_ENABLED
+# define GLIB_OPTION "g"
+#else
+# define GLIB_OPTION ""
+#endif
+#ifdef QT_ENABLED
+# define QT_OPTION "q"
+#else
+# define QT_OPTION ""
+#endif
+
+
+# define OPTIONS "r:i:t:s:I:T:S:M:l:w:W:o:vd:h" \
+ PULSE_OPTION""ECORE_OPTION""GLIB_OPTION""QT_OPTION
+ struct option options[] = {
+ { "runtime" , required_argument, NULL, 'r' },
+ { "ios" , required_argument, NULL, 'i' },
+ { "timers" , required_argument, NULL, 't' },
+ { "signals" , required_argument, NULL, 's' },
+ { "glib-ios" , required_argument, NULL, 'I' },
+ { "glib-timers" , required_argument, NULL, 'T' },
+ { "dbus-signals", required_argument, NULL, 'S' },
+ { "dbus-methods", required_argument, NULL, 'M' },
+#ifdef PULSE_ENABLED
+ { "pulse" , no_argument , NULL, 'p' },
+#endif
+#ifdef ECORE_ENABLED
+ { "ecore" , no_argument , NULL, 'e' },
+#endif
+#ifdef GLIB_ENABLED
+ { "glib" , no_argument , NULL, 'g' },
+#endif
+#ifdef QT_ENABLED
+ { "qt" , no_argument , NULL, 'q' },
+#endif
+ { "wakeup-lpf" , required_argument, NULL, 'w' },
+ { "wakeup-force", required_argument, NULL, 'W' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target" , required_argument, NULL, 'o' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+ char *end;
+ int opt;
+
+ config_set_defaults(cfg);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'r':
+ cfg->runtime = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid runtime length '%s'.", optarg);
+ break;
+
+ case 'i':
+ cfg->nio = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of I/O watches '%s'.", optarg);
+ break;
+
+ case 't':
+ cfg->ntimer = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of timers '%s'.", optarg);
+ break;
+
+ case 's':
+ cfg->nsignal = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of signals '%s'.", optarg);
+ break;
+
+ case 'I':
+ cfg->ngio = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of glib I/O watches '%s'.", optarg);
+ break;
+
+ case 'T':
+ cfg->ngtimer = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of glib timers '%s'.", optarg);
+ break;
+
+ case 'S':
+ cfg->ndbus_signal = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of DBUS signals '%s'.", optarg);
+ break;
+
+ case 'M':
+ cfg->ndbus_method = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid number of DBUS methods '%s'.", optarg);
+ break;
+
+#ifdef PULSE_ENABLED
+ case 'p':
+ cfg->mainloop_type = MAINLOOP_PULSE;
+ break;
+#endif
+
+#ifdef ECORE_ENABLED
+ case 'e':
+ cfg->mainloop_type = MAINLOOP_ECORE;
+ break;
+#endif
+
+#ifdef GLIB_ENABLED
+ case 'g':
+ cfg->mainloop_type = MAINLOOP_GLIB;
+ break;
+#endif
+
+#ifdef QT_ENABLED
+ case 'q':
+ cfg->mainloop_type = MAINLOOP_QT;
+ break;
+#endif
+
+ case 'w':
+ cfg->wlpf = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid wakeup low-pass filter limit '%s'.",
+ optarg);
+ break;
+
+ case 'W':
+ cfg->wfrc = (int)strtoul(optarg, &end, 10);
+ if (end && *end)
+ print_usage(argv[0], EINVAL,
+ "invalid wakeup force trigger limit '%s'.",
+ optarg);
+ break;
+
+ case 'v':
+ cfg->log_mask <<= 1;
+ cfg->log_mask |= 1;
+ break;
+
+ case 'l':
+ cfg->log_mask = mrp_log_parse_levels(optarg);
+ if (cfg->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 'o':
+ cfg->log_target = mrp_log_parse_target(optarg);
+ if (!cfg->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ cfg->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+static mrp_mainloop_t *mainloop_create(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ cfg->ml = mrp_mainloop_create();
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_create(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_create(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_create(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ cfg->ml = qt_mainloop_create();
+ break;
+#endif
+
+ default:
+ mrp_log_error("Invalid mainloop type 0x%x.", cfg->mainloop_type);
+ exit(1);
+ }
+
+ if (cfg->ml == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ return cfg->ml;
+}
+
+
+static void mainloop_run(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ mrp_mainloop_run(cfg->ml);
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_run(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_run(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_run(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ qt_mainloop_run();
+ break;
+#endif
+
+ default:
+ mrp_log_error("Invalid mainloop type 0x%x.", cfg->mainloop_type);
+ exit(1);
+ }
+}
+
+
+static void mainloop_quit(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ mrp_mainloop_quit(cfg->ml, 0);
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_quit(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_quit(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_quit(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ qt_mainloop_quit();
+ break;
+#endif
+
+ default:
+ mrp_log_error("Invalid mainloop type 0x%x.", cfg->mainloop_type);
+ exit(1);
+ }
+}
+
+
+void mainloop_cleanup(test_config_t *cfg)
+{
+ switch (cfg->mainloop_type) {
+ case MAINLOOP_NATIVE:
+ break;
+
+ case MAINLOOP_PULSE:
+ pulse_mainloop_cleanup(cfg);
+ break;
+
+ case MAINLOOP_ECORE:
+ ecore_mainloop_cleanup(cfg);
+ break;
+
+ case MAINLOOP_GLIB:
+ glib_mainloop_cleanup(cfg);
+ break;
+
+#ifdef QT_ENABLED
+ case MAINLOOP_QT:
+ qt_mainloop_cleanup(cfg->ml);
+ cfg->ml = NULL;
+ break;
+#endif
+
+ default:
+ mrp_log_error("Unknown mainloop type (0x%x).", cfg->mainloop_type);
+ exit(1);
+ }
+}
+
+
+int main(int argc, char *argv[])
+{
+ mrp_mainloop_t *ml;
+
+ mrp_clear(&cfg);
+ parse_cmdline(&cfg, argc, argv);
+
+ mrp_log_set_mask(cfg.log_mask);
+ mrp_log_set_target(cfg.log_target);
+
+ ml = mainloop_create(&cfg);
+
+ if (ml == NULL)
+ fatal("failed to create main loop.");
+
+ dbus_test.ml = ml;
+ setup_dbus_tests(ml);
+ ml = dbus_test.ml;
+
+ setup_timers(ml);
+ setup_io(ml);
+ setup_signals(ml);
+ MRP_UNUSED(setup_deferred); /* XXX TODO: add deferred tests... */
+
+#ifdef GLIB_ENABLED
+ if (cfg.mainloop_type != MAINLOOP_GLIB && cfg.mainloop_type != MAINLOOP_QT) {
+ if (cfg.ngio > 0 || cfg.ngtimer > 0)
+ glib_pump_setup(ml);
+ }
+
+ setup_glib_io();
+ setup_glib_timers();
+#endif
+
+ if (mrp_add_timer(ml, 1000, check_quit, NULL) == NULL)
+ fatal("failed to create quit-check timer");
+
+ setup_wakeup(ml);
+
+ mainloop_run(&cfg);
+
+ check_io();
+ check_timers();
+ check_signals();
+
+#ifdef GLIB_ENABLED
+ check_glib_io();
+ check_glib_timers();
+#endif
+
+ if (dbus_test.client != 0)
+ close(dbus_test.pipe[1]); /* let the client continue */
+
+ check_dbus();
+
+#ifdef GLIB_ENABLED
+ if (cfg.mainloop_type != MAINLOOP_GLIB) {
+ if (cfg.ngio > 0 || cfg.ngtimer > 0)
+ glib_pump_cleanup();
+ }
+#endif
+
+ cleanup_wakeup();
+
+ mainloop_cleanup(&cfg);
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/types.h>
+
+#include <murphy/common/mask.h>
+
+int main(int argc, char *argv[])
+{
+ uint64_t bits;
+ int i, j, prev, set, n, cnt, clr, bit;
+ mrp_mask_t m = MRP_MASK_EMPTY, m1;
+ int b[] = { 0, 1, 5, 16, 32, 48, 97, 112, 113, 114, 295, 313, -1 };
+
+ cnt = argc > 1 ? strtoul(argv[1], NULL, 10) : 100;
+
+ srand((unsigned int)time(NULL) ^ (unsigned int)getpid());
+
+ bits = 0x17;
+ bits <<= 35;
+ n = mrp_ffsll(bits);
+ printf("ffsl(0x%lx) = %d\n", bits, n);
+
+ for (i = 0; i < cnt; i++) {
+ bits = (unsigned long)rand();
+ n = mrp_ffsll(bits);
+ clr = ~((((unsigned long)-1) >> (n - 1)) << (n - 1));
+
+ if (n > 1) {
+ if ((bits & clr) != 0) {
+ fail:
+ printf("ffs(0x%lx) = %d: FAIL\n", bits, n);
+ exit(1);
+ }
+ else
+ printf("ffs(0x%lx) = %d: OK\n", bits, n);
+ }
+
+ if (n != __builtin_ffsl(bits))
+ goto fail;
+
+ }
+
+ for (i = 0; b[i] != -1; i++) {
+ printf("setting bit %d...\n", b[i]);
+ mrp_mask_set(&m, b[i]);
+ if (!mrp_mask_test(&m, b[i])) {
+ printf("testing bit %d: FAILED\n", b[i]);
+ exit(1);
+ }
+ }
+
+
+ prev = 0;
+ for (i = 0; b[i] != -1; i++) {
+ for (j = prev + 1; j < b[i]; j++) {
+ set = mrp_mask_test(&m, j);
+ if (set) {
+ printf("negative mask_test(%d): FAILED\n", j);
+ exit(1);
+ }
+ }
+
+ set = mrp_mask_test(&m, b[i]);
+
+ if (!set) {
+ printf("mask_test(%d): FAILED\n", b[i]);
+ exit(1);
+ }
+
+ prev = b[i];
+ }
+
+ printf("mask tests: OK\n");
+
+ MRP_MASK_FOREACH_SET(&m, bit, 0) {
+ printf("next bit set: %d\n", bit);
+ }
+
+ MRP_MASK_FOREACH_CLEAR(&m, bit, 150) {
+ printf("next bit clear: %d\n", bit);
+ }
+
+ mrp_mask_neg(&m);
+ MRP_MASK_FOREACH_CLEAR(&m, bit, 150) {
+ printf("next bit clear: %d\n", bit);
+ }
+
+ MRP_MASK_FOREACH_CLEAR(&m, bit, 0) {
+ printf("next bit clear (negated): %d\n", bit);
+ }
+
+ mrp_mask_neg(&m);
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_neg(&m1);
+ mrp_mask_or(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (or'd): %d\n", bit);
+ }
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_neg(&m1);
+ mrp_mask_xor(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (neg'd+xor'd): %d\n", bit);
+ }
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_neg(&m1);
+ mrp_mask_and(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (neg'd+and'd): %d\n", bit);
+ }
+
+ mrp_mask_copy(&m1, &m);
+ mrp_mask_and(&m1, &m);
+
+ MRP_MASK_FOREACH_SET(&m1, bit, 0) {
+ printf("next bit set (and'd): %d\n", bit);
+ }
+
+ mrp_mask_reset(&m);
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/file-utils.h>
+
+int main(int argc, char *argv[])
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ printf("Trying to create directory '%s'..\n", argv[i]);
+ if (mrp_mkdir(argv[i], 0755) < 0)
+ printf("failed (%d: %s)\n", errno, strerror(errno));
+ else
+ printf("ok\n");
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <murphy/common/mm.h>
+
+#define fatal(fmt, args...) do { \
+ fprintf(stderr, "fatal error: "fmt"\n" , ## args); \
+ exit(1); \
+ } while (0)
+
+#define error(fmt, args...) do { \
+ fprintf(stdout, "error: "fmt"\n" , ## args); \
+ } while (0)
+
+#define info(fmt, args...) do { \
+ fprintf(stdout, fmt"\n" , ## args); \
+ } while (0)
+
+
+
+static int basic_tests(int n)
+{
+ void **ptrs;
+ char buf[1024], *p;
+ int i;
+
+ mrp_mm_config(MRP_MM_DEBUG);
+
+ ptrs = mrp_allocz(n * sizeof(*ptrs));
+
+ if (ptrs == NULL)
+ fatal("Failed to allocate pointer table.");
+
+ for (i = 0; i < n; i++) {
+ snprintf(buf, sizeof(buf), "#%d: message number %d (0x%x)", i, i, i);
+
+ p = ptrs[i] = mrp_strdup(buf);
+
+ if (p != NULL) {
+ if (!strcmp(buf, p)) {
+ printf("'%s' was duplicated as '%s'\n", buf, p);
+ }
+ else {
+ printf("'%s' was incorrectly duplicated as '%s'\n", buf, p);
+ return FALSE;
+ }
+ }
+ else {
+ printf("failed to duplicate '%s'\n", buf);
+ return FALSE;
+ }
+ }
+
+ mrp_mm_check(stdout);
+
+ for (i = 0; i < n; i += 2) {
+ mrp_free(ptrs[i]);
+ ptrs[i] = NULL;
+ }
+
+ mrp_mm_check(stdout);
+
+ for (i = 0; i < n; i++) {
+ mrp_free(ptrs[i]);
+ ptrs[i] = NULL;
+ }
+
+ mrp_mm_check(stdout);
+
+ mrp_free(ptrs);
+
+ mrp_mm_check(stdout);
+
+ return TRUE;
+}
+
+
+typedef struct {
+ char name[32];
+ int i;
+ double d;
+ char *s;
+ void *p;
+} obj_t;
+
+
+#define NAME_FORMAT "#%d test object"
+#define POISON 0xf3
+
+static int obj_setup(void *ptr)
+{
+ static int idx = 0;
+ obj_t *obj = ptr;
+
+ snprintf(obj->name, sizeof(obj->name), NAME_FORMAT, idx);
+ obj->i = idx;
+ obj->d = 2.0 * idx;
+ obj->s = mrp_strdup(obj->name);
+ obj->p = ptr;
+
+ return TRUE;
+}
+
+
+static void obj_cleanup(void *ptr)
+{
+ obj_t *obj = ptr;
+
+ mrp_free(obj->s);
+}
+
+
+static int obj_check(obj_t *obj, int alloced)
+{
+ char name[32];
+
+ if (alloced) {
+ snprintf(name, sizeof(name), NAME_FORMAT, obj->i);
+
+ return (!strcmp(name, obj->name) && !strcmp(name, obj->s) &&
+ obj->d == 2 * obj->i && obj->p == obj);
+ }
+ else {
+ char check[sizeof(obj_t)];
+
+ memset(check, POISON, sizeof(check));
+ if (memcmp(check, obj, sizeof(*obj)))
+ error("Object %p not properly poisoned.", obj);
+ }
+
+ return TRUE;
+}
+
+
+static int pool_tests(void)
+{
+ mrp_objpool_config_t cfg;
+ mrp_objpool_t *pool;
+ obj_t **ptrs;
+ int limit, prealloc, i, max;
+ int success;
+
+ limit = 0;
+ prealloc = 512;
+ max = 8382;
+ ptrs = mrp_allocz(max * sizeof(obj_t));
+
+ if (ptrs == NULL) {
+ error("Failed to allocate check pointer table.");
+ return FALSE;
+ }
+
+ cfg.name = "test pool";
+ cfg.limit = limit;
+ cfg.objsize = sizeof(obj_t);
+ cfg.prealloc = prealloc;
+ cfg.setup = obj_setup;
+ cfg.cleanup = obj_cleanup;
+ cfg.poison = POISON;
+ cfg.flags = MRP_OBJPOOL_FLAG_POISON;
+
+ info("Creating object pool...");
+ pool = mrp_objpool_create(&cfg);
+
+ if (pool == NULL) {
+ error("Failed to create test object pool.");
+ return FALSE;
+ }
+
+ info("Allocating objects...");
+ for (i = 0; i < max; i++) {
+ ptrs[i] = mrp_objpool_alloc(pool);
+
+ if (ptrs[i] == NULL) {
+ error("Failed to allocate test object #%d.", i);
+ success = FALSE;
+ goto out;
+ }
+
+ if (!obj_check(ptrs[i], TRUE)) {
+ error("Object check failed for %p.", ptrs[i]);
+ success = FALSE;
+ }
+ }
+
+ info("Freeing objects...");
+ for (i = 0; i < max; i += 2) {
+ mrp_objpool_free(ptrs[i]);
+ obj_check(ptrs[i], FALSE);
+ ptrs[i] = NULL;
+ }
+
+ info("Reallocating objects...");
+ for (i = 0; i < max; i += 2) {
+ ptrs[i] = mrp_objpool_alloc(pool);
+
+ if (ptrs[i] == NULL) {
+ error("Failed to re-allocate test object #%d.", i);
+ success = FALSE;
+ goto out;
+ }
+
+ if (!obj_check(ptrs[i], TRUE)) {
+ error("Object check failed for %p.", ptrs[i]);
+ success = FALSE;
+ }
+
+ }
+
+ info("Freeing objects...");
+ for (i = 0; i < max; i++) {
+ mrp_objpool_free(ptrs[i]);
+ ptrs[i] = NULL;
+ }
+
+ info("Reallocating again objects...");
+ for (i = 0; i < max; i++) {
+ ptrs[i] = mrp_objpool_alloc(pool);
+
+ if (ptrs[i] == NULL) {
+ error("Failed to re-allocate test object #%d.", i);
+ success = FALSE;
+ goto out;
+ }
+
+ if (!obj_check(ptrs[i], TRUE)) {
+ error("Object check failed for %p.", ptrs[i]);
+ success = FALSE;
+ }
+ }
+
+ out:
+ mrp_free(ptrs);
+ info("Destroying object pool...");
+ mrp_objpool_destroy(pool);
+
+ return success;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int max;
+
+ if (argc > 1)
+ max = (int)strtol(argv[1], NULL, 10);
+ else
+ max = 256;
+
+ info("Running basic tests...");
+ basic_tests(max);
+
+ info("Running object pool tests...");
+ pool_tests();
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common.h>
+
+#include <murphy/common/msg.h>
+#include <murphy/common/msg.c>
+
+#define TYPE(type, name) [MRP_MSG_FIELD_##type] = name
+const char *types[] = {
+ TYPE(INVALID, "invalid"),
+ TYPE(STRING , "string" ),
+ TYPE(BOOL , "bool" ),
+ TYPE(SINT8 , "sint8" ),
+ TYPE(UINT8 , "uint8" ),
+ TYPE(SINT16 , "sint16" ),
+ TYPE(UINT16 , "uint16" ),
+ TYPE(SINT32 , "sint32" ),
+ TYPE(UINT32 , "uint32" ),
+ TYPE(SINT64 , "sint64" ),
+ TYPE(UINT64 , "uint64" ),
+ TYPE(DOUBLE , "double" ),
+ TYPE(BLOB , "blob" ),
+ NULL,
+};
+#undef TYPE
+
+
+uint16_t get_type(const char **types, const char *name)
+{
+ const char **t;
+
+ for (t = types; *t != NULL; t++) {
+ if (!strcmp(*t, name))
+ return (uint16_t)(t - types);
+ }
+
+ return MRP_MSG_FIELD_INVALID;
+}
+
+
+void test_default_encode_decode(int argc, char **argv)
+{
+ mrp_msg_t *msg, *decoded;
+ void *encoded;
+ ssize_t size;
+ uint16_t tag, type, prev_tag;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ uint64_t u64;
+ int64_t s64;
+ double dbl;
+ bool bln;
+ char *val, *end;
+ int i, ok;
+
+ if ((msg = mrp_msg_create_empty()) == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ prev_tag = 0;
+ i = 1;
+ while (i < argc) {
+
+ if ('0' <= *argv[i] && *argv[i] <= '9') {
+ if (argc <= i + 2) {
+ mrp_log_error("Missing field type or value.");
+ exit(1);
+ }
+
+ tag = prev_tag = (uint16_t)strtoul(argv[i++], &end, 0);
+ if (end && *end) {
+ mrp_log_error("Invalid field tag '%s'.", argv[i]);
+ exit(1);
+ }
+ }
+ else {
+ if (argc <= i + 1) {
+ mrp_log_error("Missing field type or value.");
+ exit(1);
+ }
+
+ tag = ++prev_tag;
+ }
+
+ type = get_type(types, argv[i++]);
+ val = argv[i++];
+
+ if (type == MRP_MSG_FIELD_INVALID) {
+ mrp_log_error("Invalid field type '%s'.", argv[i + 1]);
+ exit(1);
+ }
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ ok = mrp_msg_append(msg, tag, type, val);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ if (!strcasecmp(val, "true"))
+ bln = TRUE;
+ else if (!strcasecmp(val, "false"))
+ bln = FALSE;
+ else {
+ mrp_log_error("Invalid boolean value '%s'.", val);
+ exit(1);
+ }
+ ok = mrp_msg_append(msg, tag, type, bln);
+ break;
+
+#define HANDLE_INT(_bits, _uget, _sget) \
+ case MRP_MSG_FIELD_UINT##_bits: \
+ u##_bits = (uint##_bits##_t)strtoul(val, &end, 0); \
+ if (end && *end) { \
+ mrp_log_error("Invalid uint%d value '%s'.", _bits, val); \
+ exit(1); \
+ } \
+ ok = mrp_msg_append(msg, tag, type, u##_bits); \
+ break; \
+ case MRP_MSG_FIELD_SINT##_bits: \
+ s##_bits = (int##_bits##_t)strtol(val, &end, 0); \
+ if (end && *end) { \
+ mrp_log_error("Invalid sint%d value '%s'.", _bits, val); \
+ exit(1); \
+ } \
+ ok = mrp_msg_append(msg, tag, type, s##_bits); \
+ break
+
+ HANDLE_INT(8 , strtol , strtoul);
+ HANDLE_INT(16, strtol , strtoul);
+ HANDLE_INT(32, strtol , strtoul);
+ HANDLE_INT(64, strtoll, strtoull);
+
+ case MRP_MSG_FIELD_DOUBLE:
+ dbl = strtod(val, &end);
+ if (end && *end) {
+ mrp_log_error("Invalid double value '%s'.", val);
+ exit(1);
+ }
+ ok = mrp_msg_append(msg, tag, type, dbl);
+ break;
+
+ default:
+ mrp_log_error("Invalid (or unimplemented) type 0x%x (%s).",
+ type, argv[i + 1]);
+ ok = FALSE;
+ }
+
+ if (!ok) {
+ mrp_log_error("Failed to add field to message.");
+ exit(1);
+ }
+ }
+
+ mrp_msg_dump(msg, stdout);
+
+ size = mrp_msg_default_encode(msg, &encoded);
+ if (size <= 0) {
+ mrp_log_error("Failed to encode message with default encoder.");
+ exit(1);
+ }
+
+ mrp_log_info("encoded message size: %d", (int)size);
+
+ decoded = mrp_msg_default_decode(encoded, size);
+ if (decoded == NULL) {
+ mrp_log_error("Failed to decode message with default decoder.");
+ exit(1);
+ }
+
+ mrp_msg_dump(decoded, stdout);
+
+ mrp_msg_unref(msg);
+ mrp_msg_unref(decoded);
+}
+
+
+typedef struct {
+ char *str1;
+ uint16_t u16;
+ int32_t s32;
+ char *str2;
+ double dbl1;
+ bool bln1;
+ double dbl2;
+ char *str3;
+ bool bln2;
+} data1_t;
+
+typedef struct {
+ char *str;
+ uint8_t u8;
+ bool bln;
+} data2_t;
+
+typedef struct {
+ char *str;
+ uint16_t u16;
+ int32_t s32;
+ double dbl;
+} data3_t;
+
+#if 0
+typedef struct {
+ uint16_t offs; /* member offset within structure */
+ uint16_t tag; /* tag for member */
+ uint16_t type; /* type of this member */
+} mrp_msg_member_t;
+
+typedef struct {
+ uint16_t tag; /* structure tag */
+ size_t size; /* structure size */
+ int nfield; /* number of members */
+ mrp_msg_member_t *fields; /* member descriptor */
+} mrp_msg_descr_t;
+#endif
+
+#define DUMP_FIELD(memb, fmt) printf(" %s: "fmt"\n", #memb, d->memb)
+
+int cmp_data1(data1_t *d1, data1_t *d2)
+{
+ return
+ !strcmp(d1->str1, d2->str1) &&
+ !strcmp(d1->str2, d2->str2) &&
+ !strcmp(d1->str3, d2->str3) &&
+ d1->u16 == d2->u16 &&
+ d1->s32 == d2->s32 &&
+ d1->dbl1 == d2->dbl1 &&
+ d1->bln1 == d2->bln1 &&
+ d1->dbl2 == d2->dbl2 &&
+ d1->bln2 == d2->bln2;
+}
+
+int cmp_data2(data2_t *d1, data2_t *d2)
+{
+ return
+ !strcmp(d1->str, d2->str) &&
+ d1->u8 == d2->u8 &&
+ d1->bln == d2->bln;
+}
+
+int cmp_data3(data3_t *d1, data3_t *d2)
+{
+ return
+ !strcmp(d1->str, d2->str) &&
+ d1->u16 == d2->u16 &&
+ d1->s32 == d2->s32 &&
+ d1->dbl == d2->dbl;
+}
+
+void dump_data1(char *prefix, data1_t *d)
+{
+ printf("%s{\n", prefix);
+ DUMP_FIELD(str1, "%s");
+ DUMP_FIELD(u16 , "%u");
+ DUMP_FIELD(s32 , "%d");
+ DUMP_FIELD(str2, "%s");
+ DUMP_FIELD(dbl1, "%f");
+ DUMP_FIELD(bln1, "%d");
+ DUMP_FIELD(dbl2, "%f");
+ DUMP_FIELD(str2, "%s");
+ DUMP_FIELD(bln2, "%d");
+ printf("}\n");
+
+}
+
+void dump_data2(char *prefix, data2_t *d)
+{
+ printf("%s{\n", prefix);
+ DUMP_FIELD(str, "%s");
+ DUMP_FIELD(u8 , "%u");
+ DUMP_FIELD(bln, "%d");
+ printf("}\n");
+}
+
+void dump_data3(char *prefix, data3_t *d)
+{
+ printf("%s{\n", prefix);
+ DUMP_FIELD(str, "%s");
+ DUMP_FIELD(u16, "%u");
+ DUMP_FIELD(s32, "%d");
+ DUMP_FIELD(dbl, "%f");
+ printf("}\n");
+}
+
+#undef DUMP_FIELD
+
+static size_t mrp_msg_encode(void **bufp, void *data,
+ mrp_data_member_t *fields, int nfield);
+
+static void *mrp_msg_decode(void **bufp, size_t *sizep, size_t data_size,
+ mrp_data_member_t *fields, int nfield);
+
+void test_custom_encode_decode(void)
+{
+#define DESCRIBE(_type, _memb, _tag, _ftype) { \
+ .offs = MRP_OFFSET(_type, _memb), \
+ .tag = _tag, \
+ .type = MRP_MSG_FIELD_##_ftype, \
+ .guard = FALSE, \
+ { NULL }, \
+ .hook = { NULL, NULL } \
+ }
+
+ mrp_data_member_t data1_descr[] = {
+ DESCRIBE(data1_t, str1, 0x1, STRING),
+ DESCRIBE(data1_t, u16, 0x2, UINT16),
+ DESCRIBE(data1_t, str1, 0x1, STRING),
+ DESCRIBE(data1_t, u16 , 0x2, UINT16),
+ DESCRIBE(data1_t, s32 , 0x3, SINT32),
+ DESCRIBE(data1_t, str2, 0x4, STRING),
+ DESCRIBE(data1_t, dbl1, 0x5, DOUBLE),
+ DESCRIBE(data1_t, bln1, 0x6, BOOL ),
+ DESCRIBE(data1_t, dbl2, 0x7, DOUBLE),
+ DESCRIBE(data1_t, str3, 0x8, STRING),
+ DESCRIBE(data1_t, bln2, 0x9, BOOL ),
+ };
+ int data1_nfield = MRP_ARRAY_SIZE(data1_descr);
+
+ mrp_data_member_t data2_descr[] = {
+ DESCRIBE(data2_t, str, 0x1, STRING),
+ DESCRIBE(data2_t, u8 , 0x2, UINT8 ),
+ DESCRIBE(data2_t, bln, 0x3, BOOL ),
+ };
+ int data2_nfield = MRP_ARRAY_SIZE(data2_descr);
+
+ mrp_data_member_t data3_descr[] = {
+ DESCRIBE(data3_t, str, 0x1, STRING),
+ DESCRIBE(data3_t, u16, 0x2, UINT16),
+ DESCRIBE(data3_t, s32, 0x3, SINT32),
+ DESCRIBE(data3_t, dbl, 0x4, DOUBLE),
+ };
+ int data3_nfield = MRP_ARRAY_SIZE(data3_descr);
+
+#define TAG_DATA1 0x1
+#define TAG_DATA2 0x2
+#define TAG_DATA3 0x3
+
+
+ data1_t data1 = {
+ .str1 = "data1, str1",
+ .u16 = 32768U,
+ .s32 = -12345678,
+ .str2 = "data1, str2",
+ .dbl1 = 9.81,
+ .bln1 = TRUE,
+ .dbl2 = -3.141,
+ .str3 = "data1, str3",
+ .bln2 = FALSE
+ };
+ data2_t data2 = {
+ .str = "data2, str",
+ .u8 = 128,
+ .bln = TRUE
+ };
+ data3_t data3 = {
+ .str = "data3, str",
+ .u16 = 32768U,
+ .s32 = -12345678,
+ .dbl = 1.2345
+ };
+
+ data1_t *d1;
+ data2_t *d2;
+ data3_t *d3;
+ void *buf;
+ size_t size;
+
+ size = mrp_msg_encode(&buf, &data1, data1_descr, data1_nfield);
+
+ if (size <= 0) {
+ mrp_log_error("failed to encode data1_t");
+ exit(1);
+ }
+
+ d1 = mrp_msg_decode(&buf, &size, sizeof(data1_t), data1_descr,data1_nfield);
+
+ if (d1 == NULL) {
+ mrp_log_error("failed to decode encoded data1_t");
+ exit(1);
+ }
+
+ dump_data1("original data1: ", &data1);
+ dump_data1("decoded data1: ", d1);
+ if (!cmp_data1(&data1, d1)) {
+ mrp_log_error("Original and decoded data1_t do not match!");
+ exit(1);
+ }
+ else
+ mrp_log_info("ok, original and decoded match...");
+
+
+ size = mrp_msg_encode(&buf, &data2, data2_descr, data2_nfield);
+
+ if (size <= 0) {
+ mrp_log_error("failed to encode data2_t");
+ exit(1);
+ }
+
+ d2 = mrp_msg_decode(&buf, &size, sizeof(data2_t), data2_descr,data2_nfield);
+
+ if (d2 == NULL) {
+ mrp_log_error("failed to decode encoded data2_t");
+ exit(1);
+ }
+
+ dump_data2("original data2: ", &data2);
+ dump_data2("decoded data2: ", d2);
+ if (!cmp_data2(&data2, d2)) {
+ mrp_log_error("Original and decoded data2_t do not match!");
+ exit(1);
+ }
+ else
+ mrp_log_info("ok, original and decoded match...");
+
+
+ size = mrp_msg_encode(&buf, &data3, data3_descr, data3_nfield);
+
+ if (size <= 0) {
+ mrp_log_error("failed to encode data3_t");
+ exit(1);
+ }
+
+ d3 = mrp_msg_decode(&buf, &size, sizeof(data3_t), data3_descr,data3_nfield);
+
+ if (d3 == NULL) {
+ mrp_log_error("failed to decode encoded data3_t");
+ exit(1);
+ }
+
+ dump_data3("original data3: ", &data3);
+ dump_data3("decoded data3: ", d3);
+ if (!cmp_data3(&data3, d3)) {
+ mrp_log_error("Original and decoded data3_t do not match!");
+ exit(1);
+ }
+ else
+ mrp_log_info("ok, original and decoded match...");
+}
+
+
+static void test_basic(void)
+{
+ mrp_msg_t *msg;
+ char *str1, *str2;
+ uint16_t u16;
+ int16_t s16;
+ uint32_t u32;
+ int32_t s32;
+ double dbl1, dbl2;
+ int i;
+
+ struct field_t {
+ uint16_t tag;
+ uint16_t type;
+ void *ptr;
+ } f[] = {
+ { 0x1, MRP_MSG_FIELD_STRING, &str1 },
+ { 0x2, MRP_MSG_FIELD_STRING, &str2 },
+ { 0x3, MRP_MSG_FIELD_UINT16, &u16 },
+ { 0x4, MRP_MSG_FIELD_SINT16, &s16 },
+ { 0x5, MRP_MSG_FIELD_UINT32, &u32 },
+ { 0x6, MRP_MSG_FIELD_SINT32, &s32 },
+ { 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1 },
+ { 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2 }
+ };
+
+ msg = mrp_msg_create(MRP_MSG_TAG_STRING(0x1, "string 0x1"),
+ MRP_MSG_TAG_STRING(0x2, "string 0x2"),
+ MRP_MSG_TAG_UINT16(0x3, 3),
+ MRP_MSG_TAG_SINT16(0x4, -4),
+ MRP_MSG_TAG_UINT32(0x5, 5),
+ MRP_MSG_TAG_SINT32(0x6, -6),
+ MRP_MSG_TAG_DOUBLE(0x7, 3.14),
+ MRP_MSG_TAG_DOUBLE(0x8, -9.81),
+ MRP_MSG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create message.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Message created OK.");
+
+
+ if (!mrp_msg_get(msg,
+ 0x1, MRP_MSG_FIELD_STRING, &str1,
+ 0x2, MRP_MSG_FIELD_STRING, &str2,
+ 0x3, MRP_MSG_FIELD_UINT16, &u16,
+ 0x4, MRP_MSG_FIELD_SINT16, &s16,
+ 0x5, MRP_MSG_FIELD_UINT32, &u32,
+ 0x6, MRP_MSG_FIELD_SINT32, &s32,
+ 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1,
+ 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2,
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields.");
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields:");
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+
+ if (!mrp_msg_get(msg,
+ 0x8, MRP_MSG_FIELD_DOUBLE, &dbl2,
+ 0x7, MRP_MSG_FIELD_DOUBLE, &dbl1,
+ 0x6, MRP_MSG_FIELD_SINT32, &s32,
+ 0x5, MRP_MSG_FIELD_UINT32, &u32,
+ 0x4, MRP_MSG_FIELD_SINT16, &s16,
+ 0x3, MRP_MSG_FIELD_UINT16, &u16,
+ 0x2, MRP_MSG_FIELD_STRING, &str2,
+ 0x1, MRP_MSG_FIELD_STRING, &str1,
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields.");
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields:");
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+
+
+#define TAG(idx) f[(idx) & 0x7].tag
+#define TYPE(idx) f[(idx) & 0x7].type
+#define PTR(idx) f[(idx) & 0x7].ptr
+#define FIELD(idx) TAG((idx)), TYPE((idx)), PTR((idx))
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(f); i++) {
+ if (!mrp_msg_get(msg,
+ FIELD(i+0), FIELD(i+1), FIELD(i+2), FIELD(i+3),
+ FIELD(i+4), FIELD(i+5), FIELD(i+6), FIELD(i+7),
+ MRP_MSG_END)) {
+ mrp_log_error("Failed to get message fields for offset %d.", i);
+ exit(1);
+ }
+ else {
+ mrp_log_info("Got message fields for offset %d:", i);
+ mrp_log_info(" str1='%s', str2='%s'", str1, str2);
+ mrp_log_info(" u16=%u, s16=%d", u16, s16);
+ mrp_log_info(" u32=%u, s32=%d", u32, s32);
+ mrp_log_info(" dbl1=%f, dbl2=%f", dbl1, dbl2);
+ }
+ }
+
+ if (mrp_msg_get(msg,
+ 0x9, MRP_MSG_FIELD_STRING, &str1, MRP_MSG_END)) {
+ mrp_log_error("Hmm... non-existent field found.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Ok, non-existent field not found...");
+}
+
+
+int main(int argc, char *argv[])
+{
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_DEBUG));
+ mrp_log_set_target(MRP_LOG_TO_STDOUT);
+
+ test_basic();
+
+ test_default_encode_decode(argc, argv);
+ test_custom_encode_decode();
+
+ return 0;
+}
+
+
+static size_t mrp_msg_encode(void **bufp, void *data,
+ mrp_data_member_t *fields, int nfield)
+{
+ mrp_data_member_t *f;
+ mrp_msgbuf_t mb;
+ mrp_msg_value_t *v;
+ uint32_t len;
+ int i;
+ size_t size;
+
+ size = nfield * (2 * sizeof(uint16_t) + sizeof(uint64_t));
+
+ if (mrp_msgbuf_write(&mb, size)) {
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ MRP_MSGBUF_PUSH(&mb, htobe16(f->tag) , 1, nomem);
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = strlen(v->str) + 1;
+ MRP_MSGBUF_PUSH(&mb, htobe32(len), 1, nomem);
+ MRP_MSGBUF_PUSH_DATA(&mb, v->str, len, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->bln ? TRUE : FALSE), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ MRP_MSGBUF_PUSH(&mb, v->u8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ MRP_MSGBUF_PUSH(&mb, v->s8, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->u16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ MRP_MSGBUF_PUSH(&mb, htobe16(v->s16), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->u32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ MRP_MSGBUF_PUSH(&mb, htobe32(v->s32), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->u64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ MRP_MSGBUF_PUSH(&mb, htobe64(v->s64), 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ MRP_MSGBUF_PUSH(&mb, v->dbl, 1, nomem);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ errno = EOPNOTSUPP;
+ /* intentional fall through */
+
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ errno = EOPNOTSUPP;
+ mrp_log_error("XXX TODO: MRP_MSG_FIELD_ARRAY "
+ "not implemented");
+ }
+ else
+ errno = EINVAL;
+
+ mrp_msgbuf_cancel(&mb);
+ nomem:
+ *bufp = NULL;
+ return 0;
+ }
+ }
+ }
+
+ *bufp = mb.buf;
+ return (size_t)(mb.p - mb.buf);
+}
+
+
+#if 0
+static mrp_data_member_t *member_type(mrp_data_member_t *fields, int nfield,
+ uint16_t tag)
+{
+ mrp_data_member_t *f;
+ int i;
+
+ for (i = 0, f = fields; i < nfield; i++, f++)
+ if (f->tag == tag)
+ return f;
+
+ return NULL;
+}
+#endif
+
+static void *mrp_msg_decode(void **bufp, size_t *sizep, size_t data_size,
+ mrp_data_member_t *fields, int nfield)
+{
+ void *data;
+ mrp_data_member_t *f;
+ mrp_msgbuf_t mb;
+ uint16_t tag;
+ mrp_msg_value_t *v;
+ void *value;
+ uint32_t len;
+ int i;
+
+ if (MRP_UNLIKELY((data = mrp_allocz(data_size)) == NULL))
+ return NULL;
+
+ mrp_msgbuf_read(&mb, *bufp, *sizep);
+
+ for (i = 0; i < nfield; i++) {
+ tag = be16toh(MRP_MSGBUF_PULL(&mb, typeof(tag) , 1, nodata));
+ f = member_type(fields, nfield, tag);
+
+ if (MRP_UNLIKELY(f == NULL))
+ goto unknown_field;
+
+ v = (mrp_msg_value_t *)(data + f->offs);
+
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ len = be32toh(MRP_MSGBUF_PULL(&mb, typeof(len), 1, nodata));
+ value = MRP_MSGBUF_PULL_DATA(&mb, len, 1, nodata);
+ v->str = mrp_strdup((char *)value);
+ if (v->str == NULL)
+ goto nomem;
+ break;
+
+ case MRP_MSG_FIELD_BOOL:
+ v->bln = be32toh(MRP_MSGBUF_PULL(&mb, uint32_t, 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT8:
+ v->u8 = MRP_MSGBUF_PULL(&mb, typeof(v->u8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_SINT8:
+ v->s8 = MRP_MSGBUF_PULL(&mb, typeof(v->s8), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_UINT16:
+ v->u16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->u16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT16:
+ v->s16 = be16toh(MRP_MSGBUF_PULL(&mb, typeof(v->s16), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT32:
+ v->u32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->u32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT32:
+ v->s32 = be32toh(MRP_MSGBUF_PULL(&mb, typeof(v->s32), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_UINT64:
+ v->u64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->u64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_SINT64:
+ v->s64 = be64toh(MRP_MSGBUF_PULL(&mb, typeof(v->s64), 1, nodata));
+ break;
+
+ case MRP_MSG_FIELD_DOUBLE:
+ v->dbl = MRP_MSGBUF_PULL(&mb, typeof(v->dbl), 1, nodata);
+ break;
+
+ case MRP_MSG_FIELD_BLOB:
+ errno = EOPNOTSUPP;
+ default:
+ if (f->type & MRP_MSG_FIELD_ARRAY) {
+ errno = EOPNOTSUPP;
+ mrp_log_error("XXX TODO: MRP_MSG_FIELD_ARRAY "
+ "not implemented");
+ }
+ else {
+ unknown_field:
+ errno = EINVAL;
+ }
+ goto fail;
+ }
+ }
+
+ *bufp = mb.buf;
+ *sizep -= mb.p - mb.buf;
+ return data;
+
+ nodata:
+ nomem:
+ fail:
+ if (data != NULL) {
+ for (i = 0, f = fields; i < nfield; i++, f++) {
+ switch (f->type) {
+ case MRP_MSG_FIELD_STRING:
+ case MRP_MSG_FIELD_BLOB:
+ mrp_free(data + f->offs);
+ }
+ }
+
+ mrp_free(data);
+ }
+
+ return NULL;
+}
--- /dev/null
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/native-types.h>
+
+
+typedef enum {
+ MUSIC,
+ MOVIE,
+ BOOK,
+ PAINTING,
+} art_type_t;
+
+
+typedef struct {
+ art_type_t type;
+ char *artist;
+ char *title;
+ uint16_t year;
+ char *location;
+ double price;
+} art_t;
+
+
+typedef enum {
+ LEFT = 0,
+ RIGHT,
+ BOTH
+} hand_t;
+
+
+typedef enum {
+ MALE = 0,
+ FEMALE = 1,
+} gender_t;
+
+
+typedef struct {
+ char *name;
+ gender_t gender;
+ int age;
+ char **languages;
+ unsigned int height;
+ float weight;
+ char nationality[32];
+ hand_t hand;
+ bool glasses;
+ art_t *favourites;
+ size_t nfavourite;
+} person_t;
+
+
+typedef struct {
+ person_t *father;
+ person_t *mother;
+ person_t *children;
+} family_t;
+
+
+art_t paps_favourites[] = {
+ {
+ BOOK ,
+ "Douglas Adams", "Dirk Gently's Holistic Detective Agency",
+ 1987, "bookshelf", 9.5
+ },
+ {
+ MUSIC,
+ "Megadeth", "Sweating Bullets",
+ 1992, "pocket", 12.5
+ },
+ {
+ MUSIC,
+ "Sentenced", "Noose",
+ 1996, "phone", 12
+ },
+ {
+ MOVIE,
+ "Bananas", "Woody Allen",
+ 1971, "PVR", 20.5
+ }
+};
+
+
+char *paps_languages[] = {
+ "english", "swedish", "finnish", NULL
+};
+
+person_t pap = {
+ .name = "Pap",
+ .gender = MALE,
+ .age = 30,
+ .languages = paps_languages,
+ .height = 180,
+ .weight = 84.5,
+ .nationality = "martian",
+ .hand = RIGHT,
+ .glasses = false,
+ .favourites = paps_favourites,
+ .nfavourite = MRP_ARRAY_SIZE(paps_favourites),
+};
+
+
+art_t moms_favourites[] = {
+ {
+ BOOK ,
+ "Douglas Adams", "THHGTTG",
+ 1982, "bookshelf", 11.8
+ },
+ {
+ MUSIC,
+ "Megadeth", "Sweating Bullets",
+ 1992, "pocket", 12.5
+ },
+ {
+ MOVIE,
+ "Hottie Chick", "GGW-II",
+ 1996, "PVR", 0.5
+ },
+ {
+ BOOK ,
+ "Douglas Adams", "The Long Dark Tea-Time of the Soul",
+ 1988, "Kindle Touch", 8.50
+ }
+};
+
+
+char *moms_languages[] = {
+ "finnish", "english", "swedish", "french", NULL
+};
+
+person_t mom = {
+ .name = "Mom",
+ .gender = FEMALE,
+ .age = 28,
+ .languages = moms_languages,
+ .height = 165,
+ .weight = 57.8,
+ .nationality = "venusian",
+ .hand = LEFT,
+ .glasses = true,
+ .favourites = moms_favourites,
+ .nfavourite = MRP_ARRAY_SIZE(moms_favourites),
+};
+
+
+char *kids_languages[] = {
+ "english", "finnish", "swedish", NULL
+};
+
+person_t tom_dick_and_harry[] = {
+ {
+ .name = "Tom",
+ .gender = MALE,
+ .age = 10,
+ .languages = kids_languages + 1,
+ .height = 135,
+ .weight = 40.5,
+ .nationality = "UFO",
+ .hand = BOTH,
+ .glasses = false,
+ .favourites = NULL,
+ .nfavourite = 0,
+ },
+ {
+ .name = "Dick",
+ .gender = MALE,
+ .age = 12,
+ .languages = kids_languages,
+ .height = 145,
+ .weight = 45.5,
+ .nationality = "UFO",
+ .hand = RIGHT,
+ .glasses = true,
+ .favourites = paps_favourites + 1,
+ .nfavourite = MRP_ARRAY_SIZE(paps_favourites) - 2,
+ },
+ {
+ .name = "Harry",
+ .gender = MALE,
+ .age = 14,
+ .languages = kids_languages + 2,
+ .height = 165,
+ .weight = 60.5,
+ .nationality = "UFO",
+ .hand = LEFT,
+ .glasses = false,
+ .favourites = moms_favourites + 1,
+ .nfavourite = MRP_ARRAY_SIZE(moms_favourites) - 2,
+ },
+ {
+ .name = NULL,
+ },
+};
+
+
+family_t family = { &pap, &mom, &tom_dick_and_harry[0] };
+
+
+int main(int argc, char *argv[])
+{
+ MRP_NATIVE_TYPE(art_type, art_t,
+ MRP_UINT32(art_t, type , DEFAULT),
+ MRP_STRING(art_t, artist , DEFAULT),
+ MRP_STRING(art_t, title , DEFAULT),
+ MRP_UINT16(art_t, year , DEFAULT),
+ MRP_STRING(art_t, location, DEFAULT),
+ MRP_DOUBLE(art_t, price , DEFAULT));
+ MRP_NATIVE_TYPE(person_type, person_t,
+ MRP_STRING(person_t, name , DEFAULT),
+ MRP_UINT32(person_t, gender , DEFAULT),
+ MRP_INT (person_t, age , DEFAULT),
+ MRP_ARRAY (person_t, languages , DEFAULT, GUARDED,
+ char *, "", .strp = NULL),
+ MRP_UINT (person_t, height , DEFAULT),
+ MRP_FLOAT (person_t, weight , DEFAULT),
+ MRP_STRING(person_t, nationality, INLINED),
+ MRP_UINT32(person_t, hand , DEFAULT),
+ MRP_BOOL (person_t, glasses , DEFAULT),
+ MRP_ARRAY (person_t, favourites , DEFAULT, SIZED,
+ art_t, nfavourite),
+ MRP_SIZET (person_t, nfavourite , DEFAULT));
+ MRP_NATIVE_TYPE(family_type, family_t,
+ MRP_STRUCT(family_t, father , DEFAULT, person_t),
+ MRP_STRUCT(family_t, mother , DEFAULT, person_t),
+ MRP_ARRAY (family_t, children, DEFAULT, GUARDED,
+ person_t, name, .strp = NULL));
+ mrp_typemap_t map[4];
+
+ uint32_t art_type_id, person_type_id, family_type_id;
+ void *ebuf;
+ size_t esize;
+ int fd;
+ void *dbuf;
+ family_t *decoded;
+ char dump[16 * 1024];
+
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_INFO));
+
+ art_type_id = mrp_register_native(&art_type);
+
+ if (art_type_id == MRP_INVALID_TYPE)
+ mrp_log_error("Failed to register art_t type.");
+ else
+ mrp_log_info("Type art_t sucessfully registered.");
+
+ person_type_id = mrp_register_native(&person_type);
+
+ if (person_type_id == MRP_INVALID_TYPE)
+ mrp_log_error("Failed to register person_t type.");
+ else
+ mrp_log_info("Type person_t sucessfully registered.");
+
+ family_type_id = mrp_register_native(&family_type);
+
+ if (family_type_id == MRP_INVALID_TYPE)
+ mrp_log_error("Failed to register family_t type.");
+ else
+ mrp_log_info("Type family_t sucessfully registered.");
+
+ ebuf = NULL;
+
+ map[0] = (mrp_typemap_t)MRP_TYPEMAP(1, art_type_id );
+ map[1] = (mrp_typemap_t)MRP_TYPEMAP(2, person_type_id);
+ map[2] = (mrp_typemap_t)MRP_TYPEMAP(3, family_type_id);
+ map[3] = (mrp_typemap_t)MRP_TYPEMAP_END;
+
+ if (mrp_encode_native(&family, family_type_id, 0, &ebuf, &esize, map) < 0) {
+ mrp_log_error("Failed to encode test data.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Test data successfully encoded (%zd bytes).", esize);
+
+ if ((fd = open("type-test.encoded",
+ O_CREAT | O_TRUNC | O_WRONLY, 0644)) >= 0) {
+ if (write(fd, ebuf, esize) != (ssize_t)esize)
+ mrp_log_error("Failed to write encoded data.");
+ close(fd);
+ }
+
+ if (mrp_decode_native(&ebuf, &esize, &dbuf, &family_type_id, map) < 0) {
+ mrp_log_error("Failed to decode test data.");
+ exit(1);
+ }
+ else
+ mrp_log_info("Test data sucessfully decoded.");
+
+ decoded = dbuf;
+
+ if (mrp_print_native(dump, sizeof(dump), decoded, family_type_id) >= 0)
+ mrp_log_info("dump of decoded data: %s", dump);
+ else
+ mrp_log_error("Failed to dump decoded data.");
+
+ mrp_free_native(dbuf, family_type_id);
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/file-utils.h>
+
+int main(int argc, char *argv[])
+{
+ int i;
+ size_t size;
+ char *p, buf[PATH_MAX];
+ struct stat ost, nst;
+
+ if (argc > 1) {
+ size = strtoul(argv[1], &p, 10);
+ if (*p || size > sizeof(buf))
+ size = sizeof(buf);
+ }
+ else
+ size = sizeof(buf);
+
+ for (i = 1; i < argc; i++) {
+ printf("'%s':\n", argv[i]);
+ if ((p = mrp_normalize_path(buf, size, argv[i])) != NULL) {
+ printf(" -> '%s'\n", p);
+
+ if (stat(argv[i], &ost) < 0)
+ printf(" Non-existing path, can't test in practice...\n");
+ else{
+ if (stat(buf, &nst) == 0 &&
+ ost.st_dev == nst.st_dev && ost.st_ino == nst.st_ino)
+ printf(" Filesystem-equality check: OK.\n");
+ else {
+ printf(" Filesystem-equality check: FAILED\n");
+ exit(1);
+ }
+ }
+ }
+ else {
+ printf(" failed (%d: %s)\n", errno, strerror(errno));
+ exit(1);
+ }
+ }
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common.h>
+#include <murphy/common/process.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+
+static void process_watch(const char *id, mrp_process_state_t s,
+ void *userdata)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *) userdata;
+
+ printf("process watch received event for %s: %s (%p)\n",
+ id, s == MRP_PROCESS_STATE_READY ? "ready" : "not ready", userdata);
+
+ mrp_mainloop_quit(ml, 0);
+}
+
+
+static void test_process_watch(mrp_mainloop_t *ml)
+{
+ mrp_process_state_t s = mrp_process_query_state("foobar");
+
+ printf("initial state %s\n",
+ s == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
+
+ if (mrp_process_set_state("foobar", MRP_PROCESS_STATE_READY) < 0) {
+ printf("error setting the state 1\n");
+ }
+
+ s = mrp_process_query_state("foobar");
+
+ printf("second state %s\n",
+ s == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
+
+ if (mrp_process_set_state("foobar", MRP_PROCESS_STATE_NOT_READY) < 0) {
+ printf("error setting the state 2\n");
+ }
+
+ s = mrp_process_query_state("foobar");
+
+ printf("third state %s\n",
+ s == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
+
+ if (mrp_process_set_watch("foobar", ml, process_watch, ml) < 0) {
+ printf("failed to register watch\n");
+ }
+
+ printf("setting state to ready\n");
+
+ if (mrp_process_set_state("foobar", MRP_PROCESS_STATE_READY) < 0) {
+ printf("error setting the state 3\n");
+ }
+
+ mrp_mainloop_run(ml);
+
+ printf("removing the watch\n");
+
+ if(mrp_process_remove_watch("foobar") < 0) {
+ printf("failed to remove watch\n");
+ }
+}
+
+static void pid_watch(pid_t pid, mrp_process_state_t s, void *userdata)
+{
+ mrp_mainloop_t *ml = (mrp_mainloop_t *) userdata;
+
+ printf("pid watch received event for %d: %s (%p)\n",
+ pid, s == MRP_PROCESS_STATE_READY ? "ready" : "not ready", userdata);
+
+ mrp_mainloop_quit(ml, 0);
+}
+
+static void test_pid_watch(mrp_mainloop_t *ml)
+{
+ pid_t pid = fork();
+
+ if (pid < 0) {
+ printf("error forking\n");
+ }
+ else if (pid > 0) {
+ mrp_pid_watch_t *w;
+
+ if (mrp_pid_query_state(pid) != MRP_PROCESS_STATE_READY) {
+ printf("failed to query the process READY state\n");
+ }
+
+ printf("setting pid watch\n");
+ w = mrp_pid_set_watch(pid, ml, pid_watch, ml);
+
+ printf("killing the process '%d'\n", pid);
+ kill(pid, 15);
+ waitpid(pid, NULL, 0);
+
+ printf("running main loop\n");
+ mrp_mainloop_run(ml);
+
+ if (mrp_pid_query_state(pid) != MRP_PROCESS_STATE_NOT_READY) {
+ printf("failed to query the process NOT READY state\n");
+ }
+ printf("removing the watch\n");
+ mrp_pid_remove_watch(w);
+ }
+}
+
+int main(int argc, char **argv) {
+ mrp_mainloop_t *ml = mrp_mainloop_create();
+
+ if (argc == 2 && strcmp(argv[1], "pid") == 0) {
+ test_pid_watch(ml);
+ }
+ else if (argc == 2 && strcmp(argv[1], "process") == 0) {
+ test_process_watch(ml);
+ }
+ else {
+ printf("Usage: process-watch-test <process|pid>\n");
+ }
+
+ mrp_mainloop_destroy(ml);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <murphy/common.h>
+#include <murphy/core.h>
+#include <murphy/common/dbus-sdbus.h>
+
+static int msg_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
+{
+ mrp_dbus_err_t err;
+ mrp_dbus_msg_t *reply;
+ const char *member = mrp_dbus_msg_member(msg);
+ const char *iface = mrp_dbus_msg_interface(msg);
+ const char *path = mrp_dbus_msg_path(msg);
+
+ MRP_UNUSED(data);
+
+ printf("Message callback called -- member: '%s', path: '%s',"
+ " interface: '%s'\n", member, path, iface);
+
+ mrp_dbus_error_init(&err);
+ mrp_dbus_error_set(&err, "org.freedesktop.DBus.Error.Failed", "Error message");
+
+ reply = mrp_dbus_msg_error(dbus, msg, &err);
+
+ if (reply) {
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ return TRUE;
+}
+
+int main()
+{
+ mrp_dbus_t *dbus;
+ mrp_mainloop_t *ml;
+
+ ml = mrp_mainloop_create();
+
+ if (!(dbus = mrp_dbus_connect(ml, "session", NULL))) {
+ printf("Failed to connect to D-Bus\n");
+ }
+
+ if (!mrp_dbus_acquire_name(dbus, "org.example", NULL)) {
+ printf("Failed to acquire name on D-Bus\n");
+ goto error;
+ }
+
+ if (!mrp_dbus_export_method(dbus, "/example", "org.example", "member",
+ msg_cb, NULL)) {
+ printf("Failed to register method\n");
+ goto error;
+ }
+
+ printf("waiting for 'dbus-send --session --print-reply --type=method_call"
+ "--dest=org.example /example org.example.member'\n");
+
+ mrp_mainloop_run(ml);
+
+ return 0;
+
+error:
+ return 1;
+}
--- /dev/null
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+
+#include "sd-bus.h"
+#include "bus-message.h"
+
+#define USEC_TO_MSEC(usec) ((unsigned int)((usec) / 1000))
+
+typedef struct {
+ sd_bus *bus;
+ mrp_mainloop_t *ml;
+ mrp_subloop_t *sl;
+} bus_t;
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ case SIGQUIT:
+ mrp_log_info("Received signal %d (%s), exiting...", signum,
+ strsignal(signum));
+ mrp_mainloop_quit(mrp_get_sighandler_mainloop(h), 0);
+ }
+}
+
+
+static int bus_prepare(void *user_data)
+{
+ MRP_UNUSED(user_data);
+
+ return FALSE;
+}
+
+
+static int bus_query(void *user_data, struct pollfd *fds, int nfd,
+ int *timeout)
+{
+ bus_t *b = (bus_t *)user_data;
+ uint64_t usec;
+
+ mrp_log_info("nfd: %d", nfd);
+
+ if (nfd > 0) {
+ fds[0].fd = sd_bus_get_fd(b->bus);
+ fds[0].events = sd_bus_get_events(b->bus) | POLLIN;
+ fds[0].revents = 0;
+
+ if (sd_bus_get_timeout(b->bus, &usec) < 0)
+ *timeout = -1;
+ else
+ *timeout = USEC_TO_MSEC(usec);
+
+ mrp_log_info("fd: %d, events: 0x%x, timeout: %u", fds[0].fd,
+ fds[0].events, *timeout);
+ }
+
+ return 1;
+}
+
+
+static int bus_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ MRP_UNUSED(user_data);
+
+ if (nfd > 0 && fds[0].revents != 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static void bus_dispatch(void *user_data)
+{
+ bus_t *b = (bus_t *)user_data;
+
+ if (sd_bus_process(b->bus, NULL) > 0)
+ sd_bus_flush(b->bus);
+}
+
+
+static int bus_signal_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data)
+{
+ mrp_log_info("%s(): got bus signal...", __FUNCTION__);
+
+ bus_message_dump(m);
+
+ return 0;
+}
+
+
+static int bus_method_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data)
+{
+ mrp_log_info("%s(): got bus method call message %p...", __FUNCTION__, m);
+
+ bus_message_dump(m);
+
+ if (!strcmp(sd_bus_message_get_member(m), "unhandled"))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+static int bus_return_cb(sd_bus *bus, int ret, sd_bus_message *m, void *user_data)
+{
+ mrp_log_info("%s(): got bus method reply...", __FUNCTION__);
+
+ bus_message_dump(m);
+
+ return 0;
+}
+
+
+static void emit_signal(mrp_timer_t *t, void *user_data)
+{
+ sd_bus *bus = (sd_bus *)user_data;
+
+ sd_bus_emit_signal(bus, "/foo/bar", "foo.bar", "foobar", NULL);
+}
+
+
+static void call_method(mrp_timer_t *t, void *user_data)
+{
+ sd_bus *bus = (sd_bus *)user_data;
+ sd_bus_message *msg = NULL;
+ int r;
+ uint64_t serial;
+
+ r = sd_bus_message_new_method_call(bus, "org.freedesktop.DBus",
+ "/", "org.freedesktop.DBus", "GetId",
+ &msg);
+
+ if (r != 0) {
+ mrp_log_error("Failed to create new method call message.");
+ return;
+ }
+
+ r = sd_bus_send_with_reply(bus, msg, bus_return_cb, NULL, 100000 * 1000, &serial);
+
+ if (r != 0)
+ mrp_log_error("Failed to call method... (r = %d)", r);
+}
+
+
+int main(int argc, char *argv[])
+{
+ static mrp_subloop_ops_t bus_ops = {
+ .prepare = bus_prepare,
+ .query = bus_query,
+ .check = bus_check,
+ .dispatch = bus_dispatch
+ };
+
+ mrp_mainloop_t *ml = NULL;
+ mrp_timer_t *ts = NULL;
+ mrp_timer_t *tm = NULL;
+ sd_bus *bus = NULL;
+ int r;
+ bus_t *b;
+
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_INFO));
+
+ ml = mrp_mainloop_create();
+ r = sd_bus_open_user(&bus);
+
+ if (ml == NULL || r != 0)
+ goto fail;
+
+ mrp_add_sighandler(ml, SIGINT , signal_handler, NULL);
+ mrp_add_sighandler(ml, SIGTERM, signal_handler, NULL);
+ mrp_add_sighandler(ml, SIGQUIT, signal_handler, NULL);
+
+ b = mrp_allocz(sizeof(*b));
+
+ if (b == NULL)
+ goto fail;
+
+ sd_bus_add_match(bus, "type='signal'" , bus_signal_cb, bus);
+#if 0
+ sd_bus_add_match(bus, "type='method_call'" , bus_method_cb, bus);
+ sd_bus_add_match(bus, "type='method_return'", bus_return_cb, bus);
+#else
+ sd_bus_add_fallback(bus, "/", bus_method_cb, bus);
+#endif
+
+ while (sd_bus_process(bus, NULL) > 0)
+ sd_bus_flush(bus);
+
+ b->bus = bus;
+ b->ml = ml;
+ b->sl = mrp_add_subloop(ml, &bus_ops, b);
+
+ if (b->sl == NULL) {
+ mrp_log_error("Failed to register D-Bus subloop.");
+ exit(1);
+ }
+
+#if 0
+ if ((ts = mrp_add_timer(ml, 1000, emit_signal, bus)) == NULL) {
+ mrp_log_error("Failed to create signal emission timer.");
+ exit(1);
+ }
+#endif
+
+ if ((ts = mrp_add_timer(ml, 1000, call_method, bus)) == NULL) {
+ mrp_log_error("Failed to create method call timer.");
+ exit(1);
+ }
+
+ mrp_mainloop_run(ml);
+
+ fail:
+ mrp_del_timer(ts);
+ mrp_del_timer(tm);
+
+ sd_bus_unref(bus);
+ mrp_mainloop_destroy(ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+
+
+/*
+ * tags for generic message fields
+ */
+
+#define TAG_SEQ ((uint16_t)0x1)
+#define TAG_MSG ((uint16_t)0x2)
+#define TAG_U8 ((uint16_t)0x3)
+#define TAG_S8 ((uint16_t)0x4)
+#define TAG_U16 ((uint16_t)0x5)
+#define TAG_S16 ((uint16_t)0x6)
+#define TAG_DBL ((uint16_t)0x7)
+#define TAG_BLN ((uint16_t)0x8)
+#define TAG_ASTR ((uint16_t)0x9)
+#define TAG_AU32 ((uint16_t)0xa)
+#define TAG_RPL ((uint16_t)0xb)
+#define TAG_END MRP_MSG_FIELD_END
+
+#define U32_GUARD (uint32_t)-1
+
+/*
+ * our test custom data type
+ */
+
+#define TAG_CUSTOM 0x1
+
+typedef struct {
+ uint32_t seq;
+ char *msg;
+ uint8_t u8;
+ int8_t s8;
+ uint16_t u16;
+ int16_t s16;
+ double dbl;
+ bool bln;
+ char **astr;
+ uint32_t nstr;
+ uint32_t fsck;
+ uint32_t *au32;
+ char *rpl;
+} custom_t;
+
+
+typedef custom_t native_t;
+
+static uint32_t native_id;
+
+MRP_DATA_DESCRIPTOR(custom_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, nstr,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+MRP_DATA_DESCRIPTOR(buggy_descr, TAG_CUSTOM, custom_t,
+ MRP_DATA_MEMBER(custom_t, seq, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, msg, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, u8, MRP_MSG_FIELD_UINT8 ),
+ MRP_DATA_MEMBER(custom_t, s8, MRP_MSG_FIELD_SINT8 ),
+ MRP_DATA_MEMBER(custom_t, u16, MRP_MSG_FIELD_UINT16),
+ MRP_DATA_MEMBER(custom_t, s16, MRP_MSG_FIELD_SINT16),
+ MRP_DATA_MEMBER(custom_t, dbl, MRP_MSG_FIELD_DOUBLE),
+ MRP_DATA_MEMBER(custom_t, bln, MRP_MSG_FIELD_BOOL ),
+ MRP_DATA_MEMBER(custom_t, rpl, MRP_MSG_FIELD_STRING),
+ MRP_DATA_MEMBER(custom_t, nstr, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_MEMBER(custom_t, fsck, MRP_MSG_FIELD_UINT32),
+ MRP_DATA_ARRAY_COUNT(custom_t, astr, fsck,
+ MRP_MSG_FIELD_STRING),
+ MRP_DATA_ARRAY_GUARD(custom_t, au32, u32, U32_GUARD,
+ MRP_MSG_FIELD_UINT32));
+
+
+
+mrp_data_descr_t *data_descr;
+
+
+typedef enum {
+ MODE_DEFAULT = 0,
+ MODE_MESSAGE = 1,
+ MODE_DATA = 2,
+ MODE_RAW = 3,
+ MODE_NATIVE = 4,
+} msg_mode_t;
+
+
+typedef struct {
+ mrp_mainloop_t *ml;
+ mrp_transport_t *lt, *t;
+ char *addrstr;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ const char *atype;
+ int server;
+ int sock;
+ mrp_io_watch_t *iow;
+ mrp_timer_t *timer;
+ int mode;
+ int buggy;
+ int connect;
+ int stream;
+ int log_mask;
+ const char *log_target;
+ uint32_t seqno;
+} context_t;
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data);
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data);
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+void recvraw(mrp_transport_t *t, void *data, size_t size, void *user_data);
+void recvrawfrom(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data);
+
+
+void dump_msg(mrp_msg_t *msg, FILE *fp)
+{
+ mrp_msg_dump(msg, fp);
+}
+
+
+void recvfrom_msg(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ mrp_msg_field_t *f;
+ uint32_t seq;
+ char buf[256];
+ int status;
+
+ mrp_log_info("received a message");
+ dump_msg(msg, stdout);
+
+ if (c->server) {
+ seq = 0;
+ if ((f = mrp_msg_find(msg, TAG_SEQ)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_UINT32)
+ seq = f->u32;
+ }
+
+ snprintf(buf, sizeof(buf), "reply to message #%u", seq);
+
+ if (!mrp_msg_append(msg, TAG_RPL, MRP_MSG_FIELD_STRING, buf,
+ TAG_END)) {
+ mrp_log_info("failed to append to received message");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(t, msg);
+ else
+ status = mrp_transport_sendto(t, msg, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+
+ /* message unreffed by transport layer */
+ }
+}
+
+
+void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void dump_custom(custom_t *msg, FILE *fp)
+{
+ uint32_t i;
+
+ mrp_data_dump(msg, data_descr, fp);
+ fprintf(fp, "{\n");
+ fprintf(fp, " seq = %u\n" , msg->seq);
+ fprintf(fp, " msg = '%s'\n", msg->msg);
+ fprintf(fp, " u8 = %u\n" , msg->u8);
+ fprintf(fp, " s8 = %d\n" , msg->s8);
+ fprintf(fp, " u16 = %u\n" , msg->u16);
+ fprintf(fp, " s16 = %d\n" , msg->s16);
+ fprintf(fp, " dbl = %f\n" , msg->dbl);
+ fprintf(fp, " bln = %s\n" , msg->bln ? "true" : "false");
+ fprintf(fp, " astr = (%u)\n", msg->nstr);
+ for (i = 0; i < msg->nstr; i++)
+ fprintf(fp, " %s\n", msg->astr[i]);
+ fprintf(fp, " au32 =\n");
+ for (i = 0; msg->au32[i] != U32_GUARD; i++)
+ fprintf(fp, " %u\n", msg->au32[i]);
+ fprintf(fp, " rpl = '%s'\n", msg->rpl);
+ fprintf(fp, "}\n");
+}
+
+
+void free_custom(custom_t *msg)
+{
+ mrp_data_free(msg, data_descr->tag);
+}
+
+
+void recvfrom_data(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ custom_t *msg = (custom_t *)data;
+ custom_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("received custom message of type 0x%x", tag);
+ dump_custom(data, stdout);
+
+ if (tag != data_descr->tag) {
+ mrp_log_error("Tag 0x%x != our custom type (0x%x).",
+ tag, data_descr->tag);
+ exit(1);
+ }
+
+ if (c->server) {
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_senddata(t, &rpl, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(t, &rpl, data_descr->tag,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+
+ free_custom(msg);
+}
+
+
+void recv_data(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
+{
+ recvfrom_data(t, data, tag, NULL, 0, user_data);
+}
+
+
+void dump_raw(void *data, size_t size, FILE *fp)
+{
+ int len = (int)size;
+
+ fprintf(fp, "[%*.*s]\n", len, len, (char *)data);
+}
+
+
+void recvfrom_raw(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ char rpl[256];
+ size_t rpl_size;
+ int status;
+
+ rpl_size = snprintf(rpl, sizeof(rpl), "reply to message [%*.*s]",
+ (int)size, (int)size, (char *)data);
+
+ mrp_log_info("received raw message");
+ dump_raw(data, size, stdout);
+
+ if (strncmp((char *)data, "reply to ", 9) != 0) {
+ if (c->connect)
+ status = mrp_transport_sendraw(t, rpl, rpl_size);
+ else
+ status = mrp_transport_sendrawto(t, rpl, rpl_size, addr, addrlen);
+
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+}
+
+
+void recv_raw(mrp_transport_t *t, void *data, size_t size, void *user_data)
+{
+ recvfrom_raw(t, data, size, NULL, 0, user_data);
+}
+
+
+void free_native(native_t *msg)
+{
+ mrp_free_native(msg, native_id);
+}
+
+
+void recvfrom_native(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ native_t *msg = (native_t *)data;
+ native_t rpl;
+ char buf[256];
+ uint32_t au32[] = { 9, 8, 7, 6, 5, -1 };
+ int status;
+
+ mrp_log_info("received native message of type 0x%x", type_id);
+ dump_custom(data, stdout);
+
+ if (type_id != native_id) {
+ mrp_log_error("Received type 0x%x, expected 0x%x.", type_id, native_id);
+ exit(1);
+ }
+
+ if (c->server) {
+ rpl = *msg;
+ snprintf(buf, sizeof(buf), "reply to message #%u", msg->seq);
+ rpl.rpl = buf;
+ rpl.au32 = au32;
+
+ if (c->connect)
+ status = mrp_transport_sendnative(t, &rpl, native_id);
+ else
+ status = mrp_transport_sendnativeto(t, &rpl, native_id,
+ addr, addrlen);
+ if (status)
+ mrp_log_info("reply successfully sent");
+ else
+ mrp_log_error("failed to send reply");
+ }
+
+ free_native(msg);
+}
+
+
+void recv_native(mrp_transport_t *t, void *data, uint32_t type_id,
+ void *user_data)
+{
+ recvfrom_native(t, data, type_id, NULL, 0, user_data);
+}
+
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ exit(0);
+ }
+}
+
+
+void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+ int flags;
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->t = mrp_transport_accept(lt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to accept new connection.");
+ exit(1);
+ }
+}
+
+
+void type_init(context_t *c)
+{
+ if (c->buggy && c->server) {
+ data_descr = &buggy_descr;
+ mrp_log_info("Deliberately using buggy data descriptor...");
+ }
+ else
+ data_descr = &custom_descr;
+
+ if (!mrp_msg_register_type(data_descr)) {
+ mrp_log_error("Failed to register custom data type.");
+ exit(1);
+ }
+}
+
+
+void register_native(void)
+{
+ MRP_NATIVE_TYPE(native_type, native_t,
+ MRP_UINT32(native_t, seq , DEFAULT),
+ MRP_STRING(native_t, msg , DEFAULT),
+ MRP_UINT8 (native_t, u8 , DEFAULT),
+ MRP_INT8 (native_t, s8 , DEFAULT),
+ MRP_UINT16(native_t, u16 , DEFAULT),
+ MRP_INT16 (native_t, s16 , DEFAULT),
+ MRP_DOUBLE(native_t, dbl , DEFAULT),
+ MRP_BOOL (native_t, bln , DEFAULT),
+ MRP_ARRAY (native_t, astr , DEFAULT, SIZED,
+ char *, nstr),
+ MRP_UINT32(native_t, nstr , DEFAULT),
+ MRP_ARRAY (native_t, au32 , DEFAULT, GUARDED,
+ uint32_t, "", .u32 = -1),
+ MRP_STRING(native_t, rpl , DEFAULT));
+
+
+ if ((native_id = mrp_register_native(&native_type)) != MRP_INVALID_TYPE)
+ mrp_log_info("Successfully registered native type 'native_t'.");
+ else {
+ mrp_log_error("Failed to register native type 'native_t'.");
+ exit(1);
+ }
+}
+
+
+void server_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = NULL,
+ .connection = NULL,
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ break;
+ case MODE_NATIVE:
+ evt.recvnative = recv_native;
+ evt.recvnativefrom = recvfrom_native;
+ break;
+ case MODE_MESSAGE:
+ default:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ }
+
+ if (c->stream) {
+ evt.connection = connection_evt;
+ evt.closed = closed_evt;
+ }
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+
+ switch (c->mode) {
+ case MODE_DATA: flags |= MRP_TRANSPORT_MODE_DATA; break;
+ case MODE_RAW: flags |= MRP_TRANSPORT_MODE_RAW; break;
+ case MODE_NATIVE: flags |= MRP_TRANSPORT_MODE_NATIVE; break;
+ default:
+ case MODE_MESSAGE: flags |= MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->lt = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->lt == NULL) {
+ mrp_log_error("Failed to create listening server transport.");
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->lt, &c->addr, c->alen)) {
+ mrp_log_error("Failed to bind transport to address %s.", c->addrstr);
+ exit(1);
+ }
+
+ if (c->stream) {
+ if (!mrp_transport_listen(c->lt, 0)) {
+ mrp_log_error("Failed to listen on server transport.");
+ exit(1);
+ }
+ }
+}
+
+
+void send_msg(context_t *c)
+{
+ mrp_msg_t *msg;
+ uint32_t seq;
+ char buf[256];
+ char *astr[] = { "this", "is", "an", "array", "of", "strings" };
+ uint32_t au32[] = { 1, 2, 3,
+ 1 << 16, 2 << 16, 3 << 16,
+ 1 << 24, 2 << 24, 3 << 24 };
+ uint32_t nstr = MRP_ARRAY_SIZE(astr);
+ uint32_t nu32 = MRP_ARRAY_SIZE(au32);
+ int status;
+
+ seq = c->seqno++;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+
+ msg = mrp_msg_create(TAG_SEQ , MRP_MSG_FIELD_UINT32, seq,
+ TAG_MSG , MRP_MSG_FIELD_STRING, buf,
+ TAG_U8 , MRP_MSG_FIELD_UINT8 , seq & 0xf,
+ TAG_S8 , MRP_MSG_FIELD_SINT8 , -(seq & 0xf),
+ TAG_U16 , MRP_MSG_FIELD_UINT16, seq,
+ TAG_S16 , MRP_MSG_FIELD_SINT16, - seq,
+ TAG_DBL , MRP_MSG_FIELD_DOUBLE, seq / 3.0,
+ TAG_BLN , MRP_MSG_FIELD_BOOL , seq & 0x1,
+ TAG_ASTR, MRP_MSG_FIELD_ARRAY_OF(STRING), nstr, astr,
+ TAG_AU32, MRP_MSG_FIELD_ARRAY_OF(UINT32), nu32, au32,
+ TAG_END);
+
+ if (msg == NULL) {
+ mrp_log_error("Failed to create new message.");
+ exit(1);
+ }
+
+ if (c->connect)
+ status = mrp_transport_send(c->t, msg);
+ else
+ status = mrp_transport_sendto(c->t, msg, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", seq);
+
+ mrp_msg_unref(msg);
+}
+
+
+void send_data(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_senddata(c->t, &msg, data_descr->tag);
+ else
+ status = mrp_transport_senddatato(c->t, &msg, data_descr->tag,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+void send_raw(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ char msg[256];
+ size_t size;
+ int status;
+
+ size = snprintf(msg, sizeof(msg), "this is message #%u", seq);
+
+ if (c->connect)
+ status = mrp_transport_sendraw(c->t, msg, size);
+ else
+ status = mrp_transport_sendrawto(c->t, msg, size, &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send raw message #%d.", seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%u succesfully sent.", seq);
+}
+
+
+void send_native(context_t *c)
+{
+ uint32_t seq = c->seqno++;
+ custom_t msg;
+ char buf[256];
+ char *astr[] = { "this", "is", "a", "test", "string", "array" };
+ uint32_t au32[] = { 1, 2, 3, 4, 5, 6, 7, -1 };
+ int status;
+
+ msg.seq = seq;
+ snprintf(buf, sizeof(buf), "this is message #%u", (unsigned int)seq);
+ msg.msg = buf;
+ msg.u8 = seq & 0xf;
+ msg.s8 = -(seq & 0xf);
+ msg.u16 = seq;
+ msg.s16 = - seq;
+ msg.dbl = seq / 3.0;
+ msg.bln = seq & 0x1;
+ msg.astr = astr;
+ msg.nstr = MRP_ARRAY_SIZE(astr);
+ msg.fsck = 1000;
+ msg.au32 = au32;
+ msg.rpl = "";
+
+ if (c->connect)
+ status = mrp_transport_sendnative(c->t, &msg, native_id);
+ else
+ status = mrp_transport_sendnativeto(c->t, &msg, native_id,
+ &c->addr, c->alen);
+
+ if (!status) {
+ mrp_log_error("Failed to send message #%d.", msg.seq);
+ exit(1);
+ }
+ else
+ mrp_log_info("Message #%d succesfully sent.", msg.seq);
+}
+
+
+void send_cb(mrp_timer_t *t, void *user_data)
+{
+ context_t *c = (context_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ switch (c->mode) {
+ case MODE_DATA: send_data(c); break;
+ case MODE_RAW: send_raw(c); break;
+ case MODE_NATIVE: send_native(c); break;
+ default:
+ case MODE_MESSAGE: send_msg(c);
+ }
+}
+
+
+void client_init(context_t *c)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = NULL },
+ { .recvmsgfrom = NULL },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int flags;
+
+ type_init(c);
+
+ switch (c->mode) {
+ case MODE_DATA:
+ evt.recvdata = recv_data;
+ evt.recvdatafrom = recvfrom_data;
+ flags = MRP_TRANSPORT_MODE_DATA;
+ break;
+ case MODE_RAW:
+ evt.recvraw = recv_raw;
+ evt.recvrawfrom = recvfrom_raw;
+ flags = MRP_TRANSPORT_MODE_RAW;
+ break;
+ case MODE_NATIVE:
+ evt.recvnative = recv_native;
+ evt.recvnativefrom = recvfrom_native;
+ flags = MRP_TRANSPORT_MODE_NATIVE;
+ break;
+ default:
+ case MODE_MESSAGE:
+ evt.recvmsg = recv_msg;
+ evt.recvmsgfrom = recvfrom_msg;
+ flags = MRP_TRANSPORT_MODE_MSG;
+ }
+
+ c->t = mrp_transport_create(c->ml, c->atype, &evt, c, flags);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ exit(1);
+ }
+
+ if (!strcmp(c->atype, "unxd")) {
+ char addrstr[] = "unxd:@stream-test-client";
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+
+ alen = mrp_transport_resolve(NULL, addrstr, &addr, sizeof(addr), NULL);
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", addrstr);
+ exit(1);
+ }
+
+ if (!mrp_transport_bind(c->t, &addr, alen)) {
+ mrp_log_error("Failed to bind to transport address '%s'.", addrstr);
+ exit(1);
+ }
+ }
+
+ if (c->connect) {
+ if (!mrp_transport_connect(c->t, &c->addr, c->alen)) {
+ mrp_log_error("Failed to connect to %s.", c->addrstr);
+ exit(1);
+ }
+ }
+
+
+ c->timer = mrp_add_timer(c->ml, 1000, send_cb, c);
+
+ if (c->timer == NULL) {
+ mrp_log_error("Failed to create send timer.");
+ exit(1);
+ }
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -s, --server run as test server (default)\n"
+ " -C, --connect connect transport\n"
+ " For connection-oriented transports, this is automatic.\n"
+ " -a, --address address to use\n"
+ " -c, --custom use custom messages\n"
+ " -m, --message use generic messages (default)\n"
+ " -r, --raw use raw messages\n"
+ " -n, --native use native messages\n"
+ " -b, --buggy use buggy data descriptors\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->addrstr = "tcp4:127.0.0.1:3000";
+ ctx->server = FALSE;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "scmrnbCa:l:t:v:d:h"
+ struct option options[] = {
+ { "server" , no_argument , NULL, 's' },
+ { "address" , required_argument, NULL, 'a' },
+ { "custom" , no_argument , NULL, 'c' },
+ { "message" , no_argument , NULL, 'm' },
+ { "raw" , no_argument , NULL, 'r' },
+ { "native" , no_argument , NULL, 'n' },
+ { "connect" , no_argument , NULL, 'C' },
+
+ { "buggy" , no_argument , NULL, 'b' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ ctx->server = TRUE;
+ break;
+
+ case 'c':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_DATA;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'm':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_MESSAGE;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_RAW;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'n':
+ if (ctx->mode == MODE_DEFAULT)
+ ctx->mode = MODE_NATIVE;
+ else {
+ mrp_log_error("Multiple modes requested.");
+ exit(1);
+ }
+ break;
+
+ case 'b':
+ ctx->buggy = TRUE;
+ break;
+
+ case 'C':
+ ctx->connect = TRUE;
+ break;
+
+ case 'a':
+ ctx->addrstr = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.server)
+ mrp_log_info("Running as server, using address '%s'...", c.addrstr);
+ else
+ mrp_log_info("Running as client, using address '%s'...", c.addrstr);
+
+ switch (c.mode) {
+ case MODE_DATA: mrp_log_info("Using custom data messages..."); break;
+ case MODE_RAW: mrp_log_info("Using raw messages..."); break;
+ case MODE_NATIVE:
+ register_native();
+ mrp_log_info("Using native messages...");
+ break;
+ default:
+ case MODE_MESSAGE: mrp_log_info("Using generic messages...");
+ }
+
+ if (!strncmp(c.addrstr, "tcp", 3) || !strncmp(c.addrstr, "unxs", 4) ||
+ !strncmp(c.addrstr, "wsck", 4)) {
+ c.stream = TRUE;
+ c.connect = TRUE;
+ }
+
+ c.alen = mrp_transport_resolve(NULL, c.addrstr,
+ &c.addr, sizeof(c.addr), &c.atype);
+ if (c.alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", c.addrstr);
+ exit(1);
+ }
+
+ c.ml = mrp_mainloop_create();
+
+ if (c.server)
+ server_init(&c);
+ else
+ client_init(&c);
+
+ mrp_mainloop_run(c.ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/tlv.h>
+
+#define TLV_MIN_PREALLOC 4096
+#define TLV_MIN_CHUNK 64
+
+int mrp_tlv_setup_write(mrp_tlv_t *tlv, size_t prealloc)
+{
+ if (prealloc < TLV_MIN_PREALLOC)
+ prealloc = TLV_MIN_PREALLOC;
+
+ if ((tlv->buf = mrp_allocz(prealloc)) == NULL)
+ return -1;
+
+ tlv->size = prealloc;
+ tlv->p = tlv->buf;
+ tlv->write = 1;
+
+ return 0;
+}
+
+
+static inline size_t tlv_space(mrp_tlv_t *tlv)
+{
+ if (tlv->size > 0 && tlv->write)
+ return tlv->size - (tlv->p - tlv->buf);
+ else
+ return 0;
+}
+
+
+static inline size_t tlv_data(mrp_tlv_t *tlv)
+{
+ if (!tlv->write)
+ return tlv->size - (tlv->p - tlv->buf);
+ else
+ return tlv->p - tlv->buf;
+}
+
+
+int mrp_tlv_ensure(mrp_tlv_t *tlv, size_t size)
+{
+ size_t left, diff;
+
+ if (!tlv->write)
+ return -1;
+
+ if ((left = tlv_space(tlv)) < size) {
+ diff = size - left;
+
+ if (diff < TLV_MIN_CHUNK)
+ diff = TLV_MIN_CHUNK;
+
+ tlv->p -= (ptrdiff_t)tlv->buf;
+
+ if (mrp_realloc(tlv->buf, tlv->size + diff) == NULL) {
+ tlv->p += (ptrdiff_t)tlv->buf;
+
+ return -1;
+ }
+
+ memset(tlv->buf + tlv->size, 0, diff);
+
+ tlv->size += diff;
+ tlv->p += (ptrdiff_t)tlv->buf;
+ }
+
+ return 0;
+}
+
+
+void *mrp_tlv_reserve(mrp_tlv_t *tlv, size_t size, int align)
+{
+ void *reserved;
+ ptrdiff_t offs, pad;
+ size_t len;
+
+ offs = tlv->p - tlv->buf;
+
+ if (align > 1)
+ pad = align - (offs & (align - 1));
+ else
+ pad = 0;
+
+ len = size + pad;
+
+ if (mrp_tlv_ensure(tlv, len) < 0)
+ return NULL;
+
+ if (pad)
+ memset(tlv->p, 0, pad);
+
+ reserved = tlv->p + pad;
+ tlv->p += len;
+
+ return reserved;
+}
+
+
+int mrp_tlv_setup_read(mrp_tlv_t *tlv, void *buf, size_t size)
+{
+ tlv->buf = tlv->p = buf;
+ tlv->size = size;
+ tlv->write = 0;
+
+ return 0;
+}
+
+
+static void *tlv_consume(mrp_tlv_t *tlv, size_t size)
+{
+ char *p;
+
+ if (tlv_data(tlv) < size)
+ return NULL;
+
+ p = tlv->p;
+ tlv->p += size;
+
+ return p;
+}
+
+
+void mrp_tlv_trim(mrp_tlv_t *tlv)
+{
+ size_t left;
+
+ if (!tlv->write)
+ return;
+
+ if ((left = tlv_space(tlv)) == 0)
+ return;
+
+ tlv->p -= (ptrdiff_t)tlv->buf;
+
+ if (mrp_realloc(tlv->buf, tlv->size - left) != NULL) {
+ tlv->size -= left;
+ tlv->p += (ptrdiff_t)tlv->buf;
+ }
+}
+
+
+size_t mrp_tlv_offset(mrp_tlv_t *tlv)
+{
+ return (size_t)(tlv->p - tlv->buf);
+}
+
+
+void mrp_tlv_cleanup(mrp_tlv_t *tlv)
+{
+ if (tlv->write)
+ mrp_free(tlv->buf);
+
+ tlv->buf = tlv->p = NULL;
+ tlv->size = 0;
+}
+
+
+void mrp_tlv_steal(mrp_tlv_t *tlv, void **bufp, size_t *sizep)
+{
+ if (tlv->write) {
+ *bufp = tlv->buf;
+ *sizep = tlv->p - tlv->buf;
+
+ tlv->buf = tlv->p = NULL;
+ tlv->size = 0;
+ }
+ else {
+ *bufp = NULL;
+ *sizep = 0;
+ }
+}
+
+
+static inline int push_tag(mrp_tlv_t *tlv, uint32_t tag)
+{
+ uint32_t *tagp;
+
+ if (tag) {
+ if ((tagp = mrp_tlv_reserve(tlv, sizeof(*tagp), 1)) == NULL)
+ return -1;
+ else
+ *tagp = htobe32(tag);
+ }
+
+ return 0;
+}
+
+
+int mrp_tlv_push_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t v)
+{
+ int8_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t v)
+{
+ uint8_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t v)
+{
+ int16_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe16(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t v)
+{
+ uint16_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe16(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t v)
+{
+ int32_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe32(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t v)
+{
+ uint32_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe32(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t v)
+{
+ int64_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe64(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t v)
+{
+ uint64_t *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = htobe64(v);
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_float(mrp_tlv_t *tlv, uint32_t tag, float v)
+{
+ float *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_double(mrp_tlv_t *tlv, uint32_t tag, double v)
+{
+ double *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_bool(mrp_tlv_t *tlv, uint32_t tag, bool v)
+{
+ bool *p;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = mrp_tlv_reserve(tlv, sizeof(*p), 1)) != NULL) {
+ *p = v;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int mrp_tlv_push_string(mrp_tlv_t *tlv, uint32_t tag, const char *str)
+{
+ uint32_t *sizep;
+ char *strp;
+ size_t len = str ? strlen(str) + 1 : 0;
+
+ if (push_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((sizep = mrp_tlv_reserve(tlv, sizeof(*sizep), 1)) == NULL)
+ return -1;
+
+ *sizep = htobe32((uint32_t)len);
+
+ if (len > 0) {
+ if ((strp = mrp_tlv_reserve(tlv, len, 1)) == NULL)
+ return -1;
+
+ strcpy(strp, str);
+ }
+
+ return 0;
+}
+
+
+int pull_tag(mrp_tlv_t *tlv, uint32_t tag)
+{
+ uint32_t *tagp;
+
+ if (tag) {
+ if ((tagp = tlv_consume(tlv, sizeof(*tagp))) == NULL)
+ return -1;
+
+ if (be32toh(*tagp) != tag)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t *v)
+{
+ int8_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t *v)
+{
+ uint8_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t *v)
+{
+ int16_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be16toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t *v)
+{
+ uint16_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be16toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t *v)
+{
+ int32_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be32toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t *v)
+{
+ uint32_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be32toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t *v)
+{
+ int64_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be64toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t *v)
+{
+ uint64_t *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = be64toh(*p);
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_float(mrp_tlv_t *tlv, uint32_t tag, float *v)
+{
+ float *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_double(mrp_tlv_t *tlv, uint32_t tag, double *v)
+{
+ double *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_bool(mrp_tlv_t *tlv, uint32_t tag, bool *v)
+{
+ bool *p;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((p = tlv_consume(tlv, sizeof(*p))) == NULL)
+ return -1;
+
+ *v = *p;
+
+ return 0;
+}
+
+
+int mrp_tlv_pull_string(mrp_tlv_t *tlv, uint32_t tag, char **v, size_t max,
+ void *(alloc)(size_t, void *), void *alloc_data)
+{
+ uint32_t *sizep, size;
+ char *str;
+
+ if (pull_tag(tlv, tag) < 0)
+ return -1;
+
+ if ((sizep = tlv_consume(tlv, sizeof(*sizep))) == NULL)
+ return -1;
+
+ size = be32toh(*sizep);
+
+ if (max != (size_t)-1 && max < size) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ if (size > 0) {
+ if ((str = tlv_consume(tlv, size)) == NULL)
+ return -1;
+
+ if (*v == NULL)
+ if ((*v = alloc(size, alloc_data)) == NULL)
+ return -1;
+
+ strncpy(*v, str, size - 1);
+ (*v)[size - 1] = '\0';
+ }
+ else
+ *v = NULL;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MRP_COMMON_TLV_H__
+#define __MRP_COMMON_TLV_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy/common/macros.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_TLV_UNTAGGED 0
+
+/**
+ * a tagged-value-list encoding/decoding buffer
+ */
+
+typedef struct {
+ void *buf; /* actual data buffer */
+ size_t size; /* allocated buffer size */
+ void *p; /* encoding/decoding pointer */
+ int write : 1; /* whether set up for writing */
+} mrp_tlv_t;
+
+/** Set up the given TLV buffer for encoding. */
+int mrp_tlv_setup_write(mrp_tlv_t *tlv, size_t prealloc);
+
+/** Set up the given TLV buffer for decoding. */
+int mrp_tlv_setup_read(mrp_tlv_t *tlv, void *buf, size_t size);
+
+/** Clean up the given TLV buffer. */
+void mrp_tlv_cleanup(mrp_tlv_t *tlv);
+
+/** Ensure the given amount of space is available in the TLV buffer. */
+int mrp_tlv_ensure(mrp_tlv_t *tlv, size_t size);
+
+/** Reserve the given amount of buffer space from the TLV buffer. */
+void *mrp_tlv_reserve(mrp_tlv_t *tlv, size_t size, int align);
+
+/** Take ownership of the data buffer from the TLV buffer. */
+void mrp_tlv_steal(mrp_tlv_t *tlv, void **bufp, size_t *sizep);
+
+/** Trim the data buffer of the TLV buffer to current amount of data. */
+void mrp_tlv_trim(mrp_tlv_t *tlv);
+
+/** Get the current read/write offset from the TLV buffer. */
+size_t mrp_tlv_offset(mrp_tlv_t *tlv);
+
+/** Add an int8_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t v);
+
+/** Add an uint8_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t v);
+
+/** Add an int16_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t v);
+
+/** Add an uint16_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t v);
+
+/** Add an int32_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t v);
+
+/** Add an uint32_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t v);
+
+/** Add an int64_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t v);
+
+/** Add an uint64_t with an optional tag to the TLV buffer. */
+int mrp_tlv_push_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t v);
+
+/** Add an float with an optional tag to the TLV buffer. */
+int mrp_tlv_push_float(mrp_tlv_t *tlv, uint32_t tag, float v);
+
+/** Add an double with an optional tag to the TLV buffer. */
+int mrp_tlv_push_double(mrp_tlv_t *tlv, uint32_t tag, double v);
+
+/** Add a boolean with an optional tag to the TLV buffer. */
+int mrp_tlv_push_bool(mrp_tlv_t *tlv, uint32_t tag, bool v);
+
+/** Add a string with an optional tag to the TLV buffer. */
+int mrp_tlv_push_string(mrp_tlv_t *tlv, uint32_t tag, const char *str);
+
+
+int mrp_tlv_pull_int8(mrp_tlv_t *tlv, uint32_t tag, int8_t *v);
+int mrp_tlv_pull_uint8(mrp_tlv_t *tlv, uint32_t tag, uint8_t *v);
+int mrp_tlv_pull_int16(mrp_tlv_t *tlv, uint32_t tag, int16_t *v);
+int mrp_tlv_pull_uint16(mrp_tlv_t *tlv, uint32_t tag, uint16_t *v);
+int mrp_tlv_pull_int32(mrp_tlv_t *tlv, uint32_t tag, int32_t *v);
+int mrp_tlv_pull_uint32(mrp_tlv_t *tlv, uint32_t tag, uint32_t *v);
+int mrp_tlv_pull_int64(mrp_tlv_t *tlv, uint32_t tag, int64_t *v);
+int mrp_tlv_pull_uint64(mrp_tlv_t *tlv, uint32_t tag, uint64_t *v);
+int mrp_tlv_pull_float(mrp_tlv_t *tlv, uint32_t tag, float *v);
+int mrp_tlv_pull_double(mrp_tlv_t *tlv, uint32_t tag, double *v);
+int mrp_tlv_pull_bool(mrp_tlv_t *tlv, uint32_t tag, bool *v);
+int mrp_tlv_pull_string(mrp_tlv_t *tlv, uint32_t tag, char **v, size_t max,
+ void *(alloc)(size_t, void *), void *alloc_data);
+
+MRP_CDECL_END
+
+#endif /* __MRP_COMMON_TLV_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/native-types.h>
+#include <murphy/common/transport.h>
+
+static int check_destroy(mrp_transport_t *t);
+static int recv_data(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+static inline int purge_destroyed(mrp_transport_t *t);
+
+
+static MRP_LIST_HOOK(transports);
+static mrp_sighandler_t *pipe_handler;
+
+
+static int check_request_callbacks(mrp_transport_req_t *req)
+{
+ /* XXX TODO: hmm... this probably needs more thought/work */
+
+ if (!req->open || !req->close)
+ return FALSE;
+
+ if (req->accept) {
+ if (!req->sendmsg || !req->sendraw || !req->senddata)
+ return FALSE;
+ }
+ else {
+ if (!req->sendmsgto || !req->sendrawto || !req->senddatato)
+ return FALSE;
+ }
+
+ if (( req->connect && !req->disconnect) ||
+ (!req->connect && req->disconnect))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+int mrp_transport_register(mrp_transport_descr_t *d)
+{
+ if (!check_request_callbacks(&d->req))
+ return FALSE;
+
+ if (d->size >= sizeof(mrp_transport_t)) {
+ mrp_list_init(&d->hook);
+ mrp_list_append(&transports, &d->hook);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void mrp_transport_unregister(mrp_transport_descr_t *d)
+{
+ mrp_list_delete(&d->hook);
+}
+
+
+static mrp_transport_descr_t *find_transport(const char *type)
+{
+ mrp_transport_descr_t *d;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&transports, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ if (!strcmp(d->type, type))
+ return d;
+ }
+
+ return NULL;
+}
+
+
+static int check_event_callbacks(mrp_transport_evt_t *evt)
+{
+ /*
+ * For connection-oriented transports we require a recv* callback
+ * and a closed callback.
+ *
+ * For connectionless transports we only require a recvfrom* callback.
+ * A recv* callback is optional, however the transport cannot be put
+ * to connected mode (usually for doing sender-based filtering) if
+ * recv* is omitted.
+ */
+
+ if (evt->connection != NULL) {
+ if (evt->recvmsg == NULL || evt->closed == NULL)
+ return FALSE;
+ }
+ else {
+ if (evt->recvmsgfrom == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void sigpipe_handler(mrp_sighandler_t *h, int sig, void *user_data)
+{
+ MRP_UNUSED(h);
+ MRP_UNUSED(user_data);
+
+ mrp_debug("caught signal %d (%s)...", sig, strsignal(sig));
+}
+
+
+mrp_transport_t *mrp_transport_create(mrp_mainloop_t *ml, const char *type,
+ mrp_transport_evt_t *evt, void *user_data,
+ int flags)
+{
+ mrp_transport_descr_t *d;
+ mrp_transport_t *t;
+
+ if (!pipe_handler)
+ pipe_handler = mrp_add_sighandler(ml, SIGPIPE, sigpipe_handler, NULL);
+
+ if (!check_event_callbacks(evt)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((d = find_transport(type)) != NULL) {
+ if ((t = mrp_allocz(d->size)) != NULL) {
+ t->descr = d;
+ t->ml = ml;
+ t->evt = *evt;
+ t->user_data = user_data;
+
+ t->check_destroy = check_destroy;
+ t->recv_data = recv_data;
+ t->flags = flags & ~MRP_TRANSPORT_MODE_MASK;
+ t->mode = flags & MRP_TRANSPORT_MODE_MASK;
+
+ if (!t->descr->req.open(t)) {
+ mrp_free(t);
+ t = NULL;
+ }
+ }
+ }
+ else
+ t = NULL;
+
+ return t;
+}
+
+
+mrp_transport_t *mrp_transport_create_from(mrp_mainloop_t *ml, const char *type,
+ void *conn, mrp_transport_evt_t *evt,
+ void *user_data, int flags,
+ int state)
+{
+ mrp_transport_descr_t *d;
+ mrp_transport_t *t;
+
+ if (!pipe_handler)
+ pipe_handler = mrp_add_sighandler(ml, SIGPIPE, sigpipe_handler, NULL);
+
+ if (!check_event_callbacks(evt)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((d = find_transport(type)) != NULL) {
+ if ((t = mrp_allocz(d->size)) != NULL) {
+ t->descr = d;
+ t->ml = ml;
+ t->evt = *evt;
+ t->user_data = user_data;
+
+ t->check_destroy = check_destroy;
+ t->recv_data = recv_data;
+ t->flags = flags & ~MRP_TRANSPORT_MODE_MASK;
+ t->mode = flags & MRP_TRANSPORT_MODE_MASK;
+
+ t->connected = !!(state & MRP_TRANSPORT_CONNECTED);
+ t->listened = !!(state & MRP_TRANSPORT_LISTENED);
+
+ if (t->connected && t->listened) {
+ mrp_free(t);
+ return NULL;
+ }
+
+ if (!t->descr->req.createfrom(t, conn)) {
+ mrp_free(t);
+ t = NULL;
+ }
+ }
+ }
+ else
+ t = NULL;
+
+ return t;
+}
+
+
+int mrp_transport_setopt(mrp_transport_t *t, const char *opt, const void *val)
+{
+ if (t != NULL) {
+ if (t->descr->req.setopt != NULL)
+ return t->descr->req.setopt(t, opt, val);
+ else {
+ if (t->mode == MRP_TRANSPORT_MODE_NATIVE) {
+ if (!strcmp(opt, MRP_TRANSPORT_OPT_TYPEMAP)) {
+ t->map = (void *)val;
+ return TRUE;
+ }
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static inline int type_matches(const char *type, const char *addr)
+{
+ while (*type == *addr)
+ type++, addr++;
+
+ return (*type == '\0' && *addr == ':');
+}
+
+
+socklen_t mrp_transport_resolve(mrp_transport_t *t, const char *str,
+ mrp_sockaddr_t *addr, socklen_t size,
+ const char **typep)
+{
+ mrp_transport_descr_t *d;
+ mrp_list_hook_t *p, *n;
+ socklen_t l;
+
+ if (t != NULL)
+ return t->descr->resolve(str, addr, size, typep);
+ else {
+ mrp_list_foreach(&transports, p, n) {
+ d = mrp_list_entry(p, typeof(*d), hook);
+ l = d->resolve(str, addr, size, typep);
+
+ if (l > 0)
+ return l;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_transport_bind(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ if (t != NULL) {
+ if (t->descr->req.bind != NULL)
+ return t->descr->req.bind(t, addr, addrlen);
+ else
+ return TRUE; /* assume no binding is needed */
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_transport_listen(mrp_transport_t *t, int backlog)
+{
+ int result;
+
+ if (t != NULL) {
+ if (t->descr->req.listen != NULL) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.listen(t, backlog);
+ });
+
+ purge_destroyed(t);
+
+ return result;
+ }
+ }
+
+ return FALSE;
+}
+
+
+mrp_transport_t *mrp_transport_accept(mrp_transport_t *lt,
+ void *user_data, int flags)
+{
+ mrp_transport_t *t;
+
+ if ((t = mrp_allocz(lt->descr->size)) != NULL) {
+ bool failed = FALSE;
+ t->descr = lt->descr;
+ t->ml = lt->ml;
+ t->evt = lt->evt;
+ t->user_data = user_data;
+
+ t->check_destroy = check_destroy;
+ t->recv_data = recv_data;
+ t->flags = (lt->flags & MRP_TRANSPORT_INHERIT) | flags;
+ t->flags = t->flags & ~MRP_TRANSPORT_MODE_MASK;
+ t->mode = lt->mode;
+ t->map = lt->map;
+
+ MRP_TRANSPORT_BUSY(t, {
+ if (!t->descr->req.accept(t, lt)) {
+ failed = TRUE;
+ }
+ else {
+ t->connected = TRUE;
+ }
+ });
+
+ if (failed) {
+ mrp_free(t);
+ t = NULL;
+ }
+ }
+
+ return t;
+}
+
+
+static inline int purge_destroyed(mrp_transport_t *t)
+{
+ if (t->destroyed && !t->busy) {
+ mrp_debug("destroying transport %p...", t);
+ mrp_free(t);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void mrp_transport_destroy(mrp_transport_t *t)
+{
+ if (t != NULL) {
+ t->destroyed = TRUE;
+
+ MRP_TRANSPORT_BUSY(t, {
+ t->descr->req.disconnect(t);
+ t->descr->req.close(t);
+ });
+
+ purge_destroyed(t);
+ }
+}
+
+
+static int check_destroy(mrp_transport_t *t)
+{
+ return purge_destroyed(t);
+}
+
+
+int mrp_transport_connect(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ int result;
+
+ if (!t->connected) {
+
+ /* make sure we can deliver reception noifications */
+ if (t->evt.recvmsg == NULL) {
+ errno = EINVAL;
+ return FALSE;
+ }
+
+ MRP_TRANSPORT_BUSY(t, {
+ if (t->descr->req.connect(t, addr, addrlen)) {
+ t->connected = TRUE;
+ result = TRUE;
+ }
+ else
+ result = FALSE;
+ });
+
+ purge_destroyed(t);
+ }
+ else {
+ errno = EISCONN;
+ result = FALSE;
+ }
+
+ return result;
+}
+
+
+int mrp_transport_disconnect(mrp_transport_t *t)
+{
+ int result;
+
+ if (t != NULL && t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ if (t->descr->req.disconnect(t)) {
+ t->connected = FALSE;
+ result = TRUE;
+ }
+ else
+ result = TRUE;
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_send(mrp_transport_t *t, mrp_msg_t *msg)
+{
+ int result;
+
+ if (t->connected && t->descr->req.sendmsg) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendmsg(t, msg);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendto(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->descr->req.sendmsgto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendmsgto(t, msg, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendraw(mrp_transport_t *t, void *data, size_t size)
+{
+ int result;
+
+ if (t->connected &&
+ t->mode == MRP_TRANSPORT_MODE_RAW && t->descr->req.sendraw) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendraw(t, data, size);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendrawto(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_RAW && t->descr->req.sendrawto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendrawto(t, data, size, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_senddata(mrp_transport_t *t, void *data, uint16_t tag)
+{
+ int result;
+
+ if (t->connected &&
+ t->mode == MRP_TRANSPORT_MODE_DATA && t->descr->req.senddata) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.senddata(t, data, tag);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_senddatato(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_DATA && t->descr->req.senddatato) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.senddatato(t, data, tag, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendcustom(mrp_transport_t *t, void *data)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_CUSTOM && t->descr->req.sendcustom) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendcustom(t, data);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendcustomto(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_CUSTOM && t->descr->req.sendcustomto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendcustomto(t, data, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendnative(mrp_transport_t *t, void *data, uint32_t type_id)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_NATIVE && t->descr->req.sendnative) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendnative(t, data, type_id);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendnativeto(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_NATIVE && t->descr->req.sendnativeto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendnativeto(t, data, type_id,
+ addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendjson(mrp_transport_t *t, mrp_json_t *msg)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_JSON && t->descr->req.sendjson) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendjson(t, msg);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+int mrp_transport_sendjsonto(mrp_transport_t *t, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ int result;
+
+ if (t->mode == MRP_TRANSPORT_MODE_JSON && t->descr->req.sendjsonto) {
+ MRP_TRANSPORT_BUSY(t, {
+ result = t->descr->req.sendjsonto(t, msg, addr, addrlen);
+ });
+
+ purge_destroyed(t);
+ }
+ else
+ result = FALSE;
+
+ return result;
+}
+
+
+static int recv_data(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen)
+{
+ mrp_data_descr_t *type;
+ uint16_t tag;
+ mrp_msg_t *msg;
+ uint32_t type_id;
+ void *decoded;
+
+ switch (t->mode) {
+ case MRP_TRANSPORT_MODE_DATA:
+ tag = be16toh(*(uint16_t *)data);
+ data += sizeof(tag);
+ size -= sizeof(tag);
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ decoded = mrp_data_decode(&data, &size, type);
+
+ if (decoded != NULL && size == 0) {
+ if (t->connected && t->evt.recvdata) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvdata(t, decoded, tag, t->user_data);
+ });
+ }
+ else if (t->evt.recvdatafrom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvdatafrom(t, decoded, tag, addr, addrlen,
+ t->user_data);
+ });
+ }
+ else
+ mrp_free(decoded); /* no callback, discard */
+
+ return 0;
+ }
+ else {
+ if (decoded != NULL) {
+ mrp_free(decoded);
+ return -EMSGSIZE;
+ }
+ else
+ return -errno;
+ }
+ }
+ else
+ return -ENOPROTOOPT;
+ break;
+
+ case MRP_TRANSPORT_MODE_RAW:
+ if (t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvraw(t, data, size, t->user_data);
+ });
+ }
+ else {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvrawfrom(t, data, size, addr, addrlen,
+ t->user_data);
+ });
+ }
+ return 0;
+
+ case MRP_TRANSPORT_MODE_MSG:
+ tag = be16toh(*(uint16_t *)data);
+ data += sizeof(tag);
+ size -= sizeof(tag);
+
+ if (tag != MRP_MSG_TAG_DEFAULT ||
+ (msg = mrp_msg_default_decode(data, size)) == NULL) {
+ return -EPROTO;
+ }
+ else {
+ if (t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvmsg(t, msg, t->user_data);
+ });
+ }
+ else {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvmsgfrom(t, msg, addr, addrlen,
+ t->user_data);
+ });
+ }
+
+ mrp_msg_unref(msg);
+
+ return 0;
+ }
+ break;
+
+ case MRP_TRANSPORT_MODE_CUSTOM:
+ if (t->connected) {
+ if (t->evt.recvcustom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvcustom(t, data, t->user_data);
+ });
+
+ return 0;
+ }
+ }
+ else {
+ if (t->evt.recvcustomfrom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvcustomfrom(t, data, addr, addrlen,
+ t->user_data);
+ });
+
+ return 0;
+ }
+ }
+ return -EPROTOTYPE;
+
+ case MRP_TRANSPORT_MODE_NATIVE:
+ type_id = 0;
+ if (mrp_decode_native(&data, &size, &decoded, &type_id, t->map) < 0)
+ return -EPROTO;
+
+ if (decoded == NULL || size != 0) {
+ mrp_free_native(decoded, type_id);
+ return -EPROTO;
+ }
+
+ if (t->connected) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvnative(t, decoded, type_id, t->user_data);
+ });
+ }
+ else {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvnativefrom(t, decoded, type_id, addr, addrlen,
+ t->user_data);
+ });
+ }
+ return 0;
+
+ case MRP_TRANSPORT_MODE_JSON:
+ if (t->connected) {
+ if (t->evt.recvjson) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvjson(t, data, t->user_data);
+ });
+ }
+ }
+ else {
+ if (t->evt.recvjsonfrom) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.recvjsonfrom(t, data, addr, addrlen,
+ t->user_data);
+ });
+ }
+ }
+ return 0;
+
+ default:
+ return -EPROTOTYPE;
+ }
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_TRANSPORT_H__
+#define __MURPHY_TRANSPORT_H__
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/native-types.h>
+
+/*
+ * json-c and JSON-Glib have a symbol clash on json_object_get_type.
+ * Unfortunately we'd really need to include our own json.h here to
+ * get mrp_json_t defined. That however pulls in json-c's headers as
+ * our implementation uses json-c and the type itself is just a
+ * typedef'd alias to json_object.
+ *
+ * Now if some unfortunate sould ends up directly or indirectly
+ * including both our transport.h, and consequently json.h, and
+ * JSON-Glib, we'll trigger the symbol clash.
+ *
+ * As a workaround if we detect that JSON-Glib has already been
+ * included we'll compile with alternative signatures (void *,
+ * instead of mrp_json_t *) and omit including json.h. Also we
+ * let people give us a warning by defining __JSON_GLIB_DANGER__
+ * that they will or might include JSON-Glib, in which case
+ * we also compile with the alternative signatures. Oh boy...
+ */
+
+#if !defined(__JSON_TYPES_H__) && !defined(__JSON_GLIB_DANGER__)
+# include <murphy/common/json.h>
+#else
+# define mrp_json_t void
+#endif
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_transport_s mrp_transport_t;
+
+
+
+/*
+ * transport socket address
+ */
+
+#define MRP_SOCKADDR_SIZE 256
+
+typedef union {
+ struct sockaddr any;
+ struct sockaddr_in ipv4;
+ struct sockaddr_in6 ipv6;
+ struct sockaddr_un unx;
+ char data[MRP_SOCKADDR_SIZE];
+} mrp_sockaddr_t;
+
+
+static inline mrp_sockaddr_t *mrp_sockaddr_cpy(mrp_sockaddr_t *d,
+ mrp_sockaddr_t *s, socklen_t n)
+{
+ memcpy(d, s, n);
+ return d;
+}
+
+
+/*
+ * various transport flags
+ */
+
+typedef enum {
+ MRP_TRANSPORT_MODE_MSG = 0x00, /* generic message encoding */
+ MRP_TRANSPORT_MODE_RAW = 0x01, /* uses bitpipe mode */
+ MRP_TRANSPORT_MODE_DATA = 0x02, /* uses registered data types */
+ MRP_TRANSPORT_MODE_CUSTOM = 0x03, /* custom message encoding */
+ MRP_TRANSPORT_MODE_NATIVE = 0x04, /* uses registered native-types */
+ MRP_TRANSPORT_MODE_JSON = 0x05, /* uses JSON messages */
+} mrp_transport_mode_t;
+
+typedef enum {
+ MRP_TRANSPORT_MODE_MASK = 0x0f, /* mask of mode bits */
+ MRP_TRANSPORT_INHERIT = 0x0f, /* mask of all inherited flags */
+
+ MRP_TRANSPORT_REUSEADDR = 0x010,
+ MRP_TRANSPORT_NONBLOCK = 0x020,
+ MRP_TRANSPORT_CLOEXEC = 0x040,
+ MRP_TRANSPORT_CONNECTED = 0x080,
+ MRP_TRANSPORT_LISTENED = 0x001,
+} mrp_transport_flag_t;
+
+#define MRP_TRANSPORT_MODE(t) ((t)->flags & MRP_TRANSPORT_MODE_MASK)
+
+
+#define MRP_TRANSPORT_OPT_TYPEMAP "type-map"
+
+/*
+ * transport requests
+ *
+ * Transport requests correspond to top-down event propagation in the
+ * communication stack. These requests are made by the core tansport
+ * abstraction layer to the underlying actual transport implementation
+ * to carry out the implementation-specific details of some transport
+ * operation.
+ */
+
+typedef struct {
+ /** Open a new transport. */
+ int (*open)(mrp_transport_t *t);
+ /** Create a new transport from an existing backend object. */
+ int (*createfrom)(mrp_transport_t *t, void *obj);
+ /** Bind a transport to a given transport-specific address. */
+ int (*bind)(mrp_transport_t *t, mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Listen on a transport for incoming connections. */
+ int (*listen)(mrp_transport_t *t, int backlog);
+ /** Accept a new transport connection over an existing transport. */
+ int (*accept)(mrp_transport_t *t, mrp_transport_t *lt);
+ /** Connect a transport to an endpoint. */
+ int (*connect)(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+ /** Disconnect a transport, if it is connection-oriented. */
+ int (*disconnect)(mrp_transport_t *t);
+ /** Close a transport, free all resources from open/accept/connect. */
+ void (*close)(mrp_transport_t *t);
+ /** Set a (possibly type specific) transport option. */
+ int (*setopt)(mrp_transport_t *t, const char *opt, const void *value);
+ /** Send a message over a (connected) transport. */
+ int (*sendmsg)(mrp_transport_t *t, mrp_msg_t *msg);
+ /** Send raw data over a (connected) transport. */
+ int (*sendraw)(mrp_transport_t *t, void *buf, size_t size);
+ /** Send registered data over a (connected) transport. */
+ int (*senddata)(mrp_transport_t *t, void *data, uint16_t tag);
+ /** Send data with a custom encoder over a transport. */
+ int (*sendcustom)(mrp_transport_t *t, void *data);
+ /** Send a native type over a (connected) transport. */
+ int (*sendnative)(mrp_transport_t *t, void *data, uint32_t type_id);
+ /** Send a JSON message over a (connected) transport. */
+ int (*sendjson)(mrp_transport_t *t, mrp_json_t *msg);
+
+ /** Send a message over a(n unconnected) transport. */
+ int (*sendmsgto)(mrp_transport_t *t, mrp_msg_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+ /** Send raw data over a(n unconnected) transport. */
+ int (*sendrawto)(mrp_transport_t *t, void *buf, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send registered data over a(n unconnected) transport. */
+ int (*senddatato)(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send data with a custom encoder over a transport. */
+ int (*sendcustomto)(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send a native type over a transport. */
+ int (*sendnativeto)(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+ /** Send a JSON messgae over a(n unconnected) transport. */
+ int (*sendjsonto)(mrp_transport_t *t, mrp_json_t *msg, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+} mrp_transport_req_t;
+
+
+/*
+ * transport events
+ *
+ * Transport events correspond to bottom-up event propagation in the
+ * communication stack. These callbacks are made by the actual transport
+ * implementation to the generic transport abstraction to inform it
+ * about relevant transport events, such as the reception of data, or
+ * transport disconnection by the peer.
+ */
+
+typedef struct {
+ /** Message received on a connected transport. */
+ union {
+ /** Generic message callback for connected transports. */
+ void (*recvmsg)(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+ /** Raw data callback for connected transports. */
+ void (*recvraw)(mrp_transport_t *t, void *data, size_t size,
+ void *user_data);
+ /** Registered data callback for connected transports. */
+ void (*recvdata)(mrp_transport_t *t, void *data, uint16_t tag,
+ void *user_data);
+ /** Custom encoded data callback for connected transports. */
+ void (*recvcustom)(mrp_transport_t *t, void *data,
+ void *user_data);
+ /** Native type callback for connected transports. */
+ void (*recvnative)(mrp_transport_t *t, void *data, uint32_t type_id,
+ void *user_data);
+ /** JSON type callback for connected transports. */
+ void (*recvjson)(mrp_transport_t *t, mrp_json_t *msg, void *user_data);
+ };
+
+ /** Message received on an unconnected transport. */
+ union {
+ /** Generic message callback for unconnected transports. */
+ void (*recvmsgfrom)(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Raw data callback for unconnected transports. */
+ void (*recvrawfrom)(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Registered data callback for unconnected transports. */
+ void (*recvdatafrom)(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Custom encoded data callback for unconnected transports. */
+ void (*recvcustomfrom)(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** Native type callback for unconnected transports. */
+ void (*recvnativefrom)(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ /** JSON type callback for unconnected transports. */
+ void (*recvjsonfrom)(mrp_transport_t *t, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+ };
+ /** Connection closed by peer. */
+ void (*closed)(mrp_transport_t *t, int error, void *user_data);
+ /** Connection attempt on a socket being listened on. */
+ void (*connection)(mrp_transport_t *t, void *user_data);
+} mrp_transport_evt_t;
+
+
+/*
+ * transport descriptor
+ */
+
+typedef struct {
+ const char *type; /* transport type name */
+ size_t size; /* full transport struct size */
+ mrp_transport_req_t req; /* transport requests */
+ socklen_t (*resolve)(const char *str, mrp_sockaddr_t *addr,
+ socklen_t addrlen, const char **typep);
+ mrp_list_hook_t hook; /* to list of registered transports */
+} mrp_transport_descr_t;
+
+
+/*
+ * transport
+ */
+
+#define MRP_TRANSPORT_PUBLIC_FIELDS \
+ mrp_mainloop_t *ml; \
+ mrp_transport_descr_t *descr; \
+ mrp_transport_evt_t evt; \
+ int (*check_destroy)(mrp_transport_t *t); \
+ int (*recv_data)(mrp_transport_t *t, void *data, \
+ size_t size, \
+ mrp_sockaddr_t *addr, \
+ socklen_t addrlen); \
+ void *user_data; \
+ mrp_typemap_t *map; \
+ int flags; \
+ int mode; \
+ int busy; \
+ int connected : 1; \
+ int listened : 1; \
+ int destroyed : 1 \
+
+
+struct mrp_transport_s {
+ MRP_TRANSPORT_PUBLIC_FIELDS;
+};
+
+
+/*
+ * Notes:
+ *
+ * Transports can get destructed in two slightly different ways.
+ *
+ * 1)
+ * Someone calls mrp_transport_destroy while the transport is
+ * idle, ie. with no callbacks or operations being active. This
+ * is simple and straightforward:
+ * - mrp_transport_destroy calls req.disconnect
+ * - mrp_transport_destroy calls req.close
+ * - mrp_transport_destroy check and sees the transport is idle
+ * so it frees the transport
+ *
+ * 2)
+ * Someone calls mrp_tansport_destroy while the transport is
+ * busy, ie. it has an unfinished callback or operation running.
+ * This typically happens when an operation or callback function,
+ * or a user function called from either of those calls
+ * mrp_transport_destroy as a result of a received message, or a
+ * (communication) error. In this case destroying the transport
+ * is less straightforward and needs to get delayed to avoid
+ * shooting out the transport underneath the active operation or
+ * callback.
+ *
+ * To handle the latter case, the generic (ie. top-level) transport
+ * layer has a member function check_destroy. This function checks
+ * for pending destroy requests and destroys the transport if it
+ * is not busy. All transport backends MUST CALL this function and
+ * CHECK ITS RETURN VALUE, whenever a user callback or a transport
+ * callback (ie. bottom-up event propagation) function invoked by
+ * the backend returns.
+ *
+ * If the transport has been left intact, check_destroy returns
+ * FALSE and processing can continue normally, taking into account
+ * that any transport state stored locally in the stack frame of the
+ * backend function might have changed during the callback. However,
+ * if check_destroy returns TRUE, it has nuked the transport and the
+ * backend MUST NOT touch or try to dereference the transport any more
+ * as its resources have already been released.
+ */
+
+
+/*
+ * convenience macros
+ */
+
+/**
+ * Macro to mark a transport busy while running a block of code.
+ *
+ * The backend needs to make sure the transport is not freed while a
+ * transport request or event callback function is active. Similarly,
+ * the backend needs to check if the transport has been marked for
+ * destruction whenever an event callback returns and trigger the
+ * destruction if it is necessary and possible (ie. the above criterium
+ * of not being active is fullfilled).
+ *
+ * These are the easiest to accomplish using the provided MRP_TRANSPORT_BUSY
+ * macro and the check_destroy callback member provided by mrp_transport_t.
+ *
+ * 1) Use the provided MRP_TRANSPORT_BUSY macro to enclose al blocks of
+ * code that invoke event callbacks. Do not do a return directly
+ * from within the enclosed call blocks, rather just set a flag
+ * within the block, check it after the block and do the return
+ * from there if necessary.
+ *
+ * 2) Call mrp_transport_t->check_destroy after any call to an event
+ * callback. check_destroy will check for any pending destroy
+ * request and perform the actual destruction if it is necessary
+ * and possible. If the transport has been left intact, check_destroy
+ * returns FALSE. However, if the transport has been destroyed and
+ * freed it returns TRUE, in which case the caller must not attempt
+ * to use or dereference the transport data structures any more.
+ */
+
+
+#ifndef __MRP_TRANSPORT_DISABLE_CODE_CHECK__
+# define W mrp_log_error
+# define __TRANSPORT_CHK_BLOCK(...) do { \
+ static int __checked = FALSE, __warned = FALSE; \
+ \
+ if (MRP_UNLIKELY(!__checked)) { \
+ __checked = TRUE; \
+ if (MRP_UNLIKELY(!__warned && \
+ strstr(#__VA_ARGS__, "return") != NULL)) { \
+ W("*********************** WARNING ********************"); \
+ W("* You seem to directly do a return from a block of *"); \
+ W("* code protected by MRP_TRANSPORT_BUSY. Are you *"); \
+ W("* absolutely sure you know what you are doing and *"); \
+ W("* that you are also doing it correctly ? *"); \
+ W("****************************************************"); \
+ W("The suspicious code block is located at: "); \
+ W(" %s@%s:%d", __FUNCTION__, __FILE__, __LINE__); \
+ W("and it looks like this:"); \
+ W("---------------------------------------------"); \
+ W("%s", #__VA_ARGS__); \
+ W("---------------------------------------------"); \
+ W("If you understand what MRP_TRANSPORT_BUSY does and"); \
+ W("how, and you are sure about the corretness of your"); \
+ W("code you can disable this error message by"); \
+ W("#defining __MRP_TRANSPORT_DISABLE_CODE_CHECK__"); \
+ W("when compiling %s.", __FILE__); \
+ __warned = TRUE; \
+ } \
+ } \
+ } while (0)
+#else
+# define __TRANSPORT_CHK_BLOCK(...) do { } while (0)
+#endif
+
+#define MRP_TRANSPORT_BUSY(t, ...) do { \
+ __TRANSPORT_CHK_BLOCK(__VA_ARGS__); \
+ (t)->busy++; \
+ __VA_ARGS__ \
+ (t)->busy--; \
+ } while (0)
+
+
+
+/** Automatically register a transport on startup. */
+#define MRP_REGISTER_TRANSPORT(_prfx, _typename, _structtype, _resolve, \
+ _open, _createfrom, _close, _setopt, \
+ _bind, _listen, _accept, \
+ _connect, _disconnect, \
+ _sendmsg, _sendmsgto, \
+ _sendraw, _sendrawto, \
+ _senddata, _senddatato, \
+ _sendcustom, _sendcustomto, \
+ _sendnative, _sendnativeto, \
+ _sendjson, _sendjsonto) \
+ static void _prfx##_register_transport(void) \
+ __attribute__((constructor)); \
+ \
+ static void _prfx##_register_transport(void) { \
+ static mrp_transport_descr_t descriptor = { \
+ .type = _typename, \
+ .size = sizeof(_structtype), \
+ .resolve = _resolve, \
+ .req = { \
+ .open = _open, \
+ .createfrom = _createfrom, \
+ .bind = _bind, \
+ .listen = _listen, \
+ .accept = _accept, \
+ .close = _close, \
+ .setopt = _setopt, \
+ .connect = _connect, \
+ .disconnect = _disconnect, \
+ .sendmsg = _sendmsg, \
+ .sendmsgto = _sendmsgto, \
+ .sendraw = _sendraw, \
+ .sendrawto = _sendrawto, \
+ .senddata = _senddata, \
+ .senddatato = _senddatato, \
+ .sendcustom = _sendcustom, \
+ .sendcustomto = _sendcustomto, \
+ .sendnative = _sendnative, \
+ .sendnativeto = _sendnativeto, \
+ .sendjson = _sendjson, \
+ .sendjsonto = _sendjsonto, \
+ }, \
+ }; \
+ \
+ if (!mrp_transport_register(&descriptor)) \
+ mrp_log_error("Failed to register transport '%s'.", \
+ _typename); \
+ else \
+ mrp_log_info("Registered transport '%s'.", _typename); \
+ } \
+ struct mrp_allow_trailing_semicolon
+
+
+
+/** Register a new transport type. */
+int mrp_transport_register(mrp_transport_descr_t *d);
+
+/** Unregister a transport. */
+void mrp_transport_unregister(mrp_transport_descr_t *d);
+
+/** Create a new transport. */
+mrp_transport_t *mrp_transport_create(mrp_mainloop_t *ml, const char *type,
+ mrp_transport_evt_t *evt,
+ void *user_data, int flags);
+
+/** Create a new transport from a backend object. */
+mrp_transport_t *mrp_transport_create_from(mrp_mainloop_t *ml, const char *type,
+ void *conn, mrp_transport_evt_t *evt,
+ void *user_data, int flags,
+ int state);
+
+/** Set a (possibly type-specific) transport option. */
+int mrp_transport_setopt(mrp_transport_t *t, const char *opt, const void *val);
+
+/** Resolve an address string to a transport-specific address. */
+socklen_t mrp_transport_resolve(mrp_transport_t *t, const char *str,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ const char **type);
+
+/** Bind a given transport to a transport-specific address. */
+int mrp_transport_bind(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+
+/** Listen for incoming connection on the given transport. */
+int mrp_transport_listen(mrp_transport_t *t, int backlog);
+
+/** Accept and create a new transport connection. */
+mrp_transport_t *mrp_transport_accept(mrp_transport_t *t,
+ void *user_data, int flags);
+
+/** Destroy a transport. */
+void mrp_transport_destroy(mrp_transport_t *t);
+
+/** Connect a transport to the given address. */
+int mrp_transport_connect(mrp_transport_t *t, mrp_sockaddr_t *addr,
+ socklen_t addrlen);
+
+/** Disconnect a transport. */
+int mrp_transport_disconnect(mrp_transport_t *t);
+
+/** Send a message through the given (connected) transport. */
+int mrp_transport_send(mrp_transport_t *t, mrp_msg_t *msg);
+
+/** Send a message through the given transport to the remote address. */
+int mrp_transport_sendto(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send raw data through the given (connected) transport. */
+int mrp_transport_sendraw(mrp_transport_t *t, void *data, size_t size);
+
+/** Send raw data through the given transport to the remote address. */
+int mrp_transport_sendrawto(mrp_transport_t *t, void *data, size_t size,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send registered data through the given (connected) transport. */
+int mrp_transport_senddata(mrp_transport_t *t, void *data, uint16_t tag);
+
+/** Send registered data through the given transport to the remote address. */
+int mrp_transport_senddatato(mrp_transport_t *t, void *data, uint16_t tag,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send custom data through the given (connected) transport. */
+int mrp_transport_sendcustom(mrp_transport_t *t, void *data);
+
+/** Send registered data through the given transport to the remote address. */
+int mrp_transport_sendcustomto(mrp_transport_t *t, void *data,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send a native type through the given (connected) transport. */
+int mrp_transport_sendnative(mrp_transport_t *t, void *data, uint32_t type_id);
+
+/** Send a native type through the given transport to the remote address. */
+int mrp_transport_sendnativeto(mrp_transport_t *t, void *data, uint32_t type_id,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+
+/** Send a JSON message through the given (connected) transport. */
+int mrp_transport_sendjson(mrp_transport_t *t, mrp_json_t *msg);
+
+/** Send a JSON message through the given transport to the remote address. */
+int mrp_transport_sendjsonto(mrp_transport_t *t, mrp_json_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen);
+MRP_CDECL_END
+
+#endif /* __MURPHY_TRANSPORT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/log.h>
+#include <murphy/common/utils.h>
+
+#define MSG_OK "OK"
+
+static int notify_parent(int fd, const char *fmt, ...)
+{
+ va_list ap;
+ int len;
+
+ va_start(ap, fmt);
+ len = vdprintf(fd, fmt, ap);
+ va_end(ap);
+
+ return (len > 0);
+}
+
+
+int mrp_daemonize(const char *dir, const char *new_out, const char *new_err)
+{
+ pid_t pid;
+ int in, out, err;
+ char msg[1024];
+ int chnl[2], len;
+
+ /*
+ * create a pipe for communicating back the child status
+ */
+
+ if (pipe(chnl) == -1) {
+ mrp_log_error("Failed to create pipe to get child status (%d: %s).",
+ errno, strerror(errno));
+ return FALSE;
+ }
+
+
+ /*
+ * fork, change to our new working directory and create a new session
+ */
+
+ switch ((pid = fork())) {
+ case -1: /* failed */
+ mrp_log_error("Could not daemonize, fork failed (%d: %s).",
+ errno, strerror(errno));
+ return FALSE;
+
+ case 0: /* child */
+ close(chnl[0]);
+ break;
+
+ default: /* parent */
+ close(chnl[1]);
+
+ /*
+ * wait for and check the status report from the child
+ */
+
+ len = read(chnl[0], msg, sizeof(msg) - 1);
+
+ if (len > 0) {
+ msg[len] = '\0';
+
+ if (!strcmp(msg, MSG_OK)) {
+ mrp_log_info("Successfully daemonized.");
+ exit(0);
+ }
+ else
+ mrp_log_error("Daemonizing failed after fork: %s.", msg);
+ }
+ else
+ mrp_log_error("Daemonizing failed in forked child.");
+
+ return FALSE;
+ }
+
+
+ if (chdir(dir) != 0) {
+ mrp_log_error("Could not daemonize, failed to chdir to %s (%d: %s).",
+ dir, errno, strerror(errno));
+ return FALSE;
+ }
+
+ if (setsid() < 0) {
+ notify_parent(chnl[1], "Failed to create new session (%d: %s).",
+ errno, strerror(errno));
+ exit(1);
+ }
+
+
+ /*
+ * fork again and redirect our stdin, stdout, and stderr
+ */
+
+ switch ((pid = fork())) {
+ case -1: /* failed */
+ notify_parent(chnl[1], "Could not daemonize, fork failed (%d: %s).",
+ errno, strerror(errno));
+ exit(1);
+
+ case 0: /* child */
+ break;
+
+ default: /* parent */
+ close(chnl[1]);
+ exit(0);
+ }
+
+
+ if ((in = open("/dev/null", O_RDONLY)) < 0) {
+ notify_parent(chnl[1], "Failed to open /dev/null (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if ((out = open(new_out, O_WRONLY)) < 0) {
+ notify_parent(chnl[1], "Failed to open %s (%d: %s).", new_out, errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if ((err = open(new_err, O_WRONLY)) < 0) {
+ notify_parent(chnl[1], "Failed to open %s (%d: %s).", new_err, errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (dup2(in, fileno(stdin)) < 0) {
+ notify_parent(chnl[1], "Failed to redirect stdin (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (dup2(out, fileno(stdout)) < 0) {
+ notify_parent(chnl[1], "Failed to redirect stdout (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (dup2(err, fileno(stderr)) < 0) {
+ notify_parent(chnl[1], "Failed to redirect stderr (%d: %s).", errno,
+ strerror(errno));
+ exit(1);
+ }
+
+ close(in);
+ close(out);
+ close(err);
+
+ notify_parent(chnl[1], "%s", MSG_OK);
+
+ return TRUE;
+}
+
+
+int mrp_string_comp(const void *key1, const void *key2)
+{
+ return strcmp(key1, key2);
+}
+
+
+uint32_t mrp_string_hash(const void *key)
+{
+ uint32_t h;
+ const char *p;
+
+ for (h = 0, p = key; *p; p++) {
+ h <<= 1;
+ h ^= *p;
+ }
+
+ return h;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_UTILS_H__
+#define __MURPHY_UTILS_H__
+
+#include <stdint.h>
+
+int mrp_daemonize(const char *dir, const char *new_out, const char *new_err);
+
+int mrp_string_comp(const void *key1, const void *key2);
+uint32_t mrp_string_hash(const void *key);
+
+#endif /* __MURPHY_UTILS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/websocket.h>
+
+
+void mrp_websock_set_loglevel(mrp_websock_loglevel_t mask)
+{
+ wsl_set_loglevel(mask);
+}
+
+
+mrp_websock_context_t *mrp_websock_create_context(mrp_mainloop_t *ml,
+ mrp_websock_config_t *cfg)
+{
+ return wsl_create_context(ml, cfg);
+}
+
+
+mrp_websock_context_t *mrp_websock_ref_context(mrp_websock_context_t *ctx)
+{
+ return wsl_ref_context(ctx);
+}
+
+
+int mrp_websock_unref_context(mrp_websock_context_t *ctx)
+{
+ return wsl_unref_context(ctx);
+}
+
+
+mrp_websock_t *mrp_websock_connect(mrp_websock_context_t *ctx,
+ struct sockaddr *sa, const char *protocol,
+ mrp_wsl_ssl_t ssl, void *user_data)
+{
+ return wsl_connect(ctx, sa, protocol, ssl, user_data);
+}
+
+
+mrp_websock_t *mrp_websock_accept_pending(mrp_websock_context_t *ctx,
+ void *user_data)
+{
+ return wsl_accept_pending(ctx, user_data);
+}
+
+
+void mrp_websock_reject_pending(mrp_websock_context_t *ctx)
+{
+ wsl_reject_pending(ctx);
+}
+
+
+void *mrp_websock_close(mrp_websock_t *sck)
+{
+ return wsl_close(sck);
+}
+
+
+int mrp_websock_send(mrp_websock_t *sck, void *payload, size_t size)
+{
+ return wsl_send(sck, payload, size);
+}
+
+
+int mrp_websock_server_http_file(mrp_websock_t *sck, const char *path,
+ const char *mime)
+{
+ return wsl_serve_http_file(sck, path, mime);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_WEBSOCKET_H__
+#define __MURPHY_WEBSOCKET_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/websocklib.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * websocket types (mapped)
+ */
+
+typedef wsl_ctx_cfg_t mrp_websock_config_t;
+typedef wsl_ctx_t mrp_websock_context_t;
+typedef wsl_sck_t mrp_websock_t;
+typedef wsl_callbacks_t mrp_websock_evt_t;
+typedef wsl_proto_t mrp_websock_proto_t;
+typedef wsl_ssl_t mrp_wsl_ssl_t;
+
+/*
+ * websocket log levels (mapped)
+ */
+
+typedef enum {
+#define MAP(mrp, wsl) MRP_WEBSOCK_LOG_##mrp = WSL_LOG_##wsl
+ MAP(NONE , NONE),
+ MAP(ERROR , ERROR),
+ MAP(WARNING, WARNING),
+ MAP(INFO , INFO),
+ MAP(DEBUG , DEBUG),
+ MAP(ALL , ALL),
+ MAP(PARSER , PARSER),
+ MAP(EXT , EXT),
+ MAP(CLIENT , CLIENT),
+ MAP(EXTRA , EXTRA),
+ MAP(VERBOSE, VERBOSE)
+#undef MAP
+} mrp_websock_loglevel_t;
+
+
+
+/*
+ * websocket function prototypes
+ */
+
+/** Set websocket logging level. */
+void mrp_websock_set_loglevel(mrp_websock_loglevel_t mask);
+
+/** Create a websocket context. */
+mrp_websock_context_t *mrp_websock_create_context(mrp_mainloop_t *ml,
+ mrp_websock_config_t *cfg);
+
+/** Add a reference to a websocket context. */
+mrp_websock_context_t *mrp_websock_ref_context(mrp_websock_context_t *ctx);
+
+/** Remove a context reference. */
+int mrp_websock_unref_context(mrp_websock_context_t *ctx);
+
+/** Create and connect a websocket to a given address. */
+mrp_websock_t *mrp_websock_connect(mrp_websock_context_t *ctx,
+ struct sockaddr *sa, const char *protocol,
+ mrp_wsl_ssl_t ssl, void *user_data);
+
+/** Accept a pending connection of a context. */
+mrp_websock_t *mrp_websock_accept_pending(mrp_websock_context_t *ctx,
+ void *user_data);
+
+/** Reject a pending connection of a context. */
+void mrp_websock_reject_pending(mrp_websock_context_t *ctx);
+
+/** Close a websocket. Return the user_data of it's associated context. */
+void *mrp_websock_close(mrp_websock_t *sck);
+
+/** Send data over a connected websocket. */
+int mrp_websock_send(mrp_websock_t *sck, void *payload, size_t size);
+
+/** Serve the given file, with MIME type, over the given websocket. */
+int mrp_websock_server_http_file(mrp_websock_t *sck, const char *path,
+ const char *mime);
+
+MRP_CDECL_END
+
+
+#endif /* __MURPHY_WEBSOCKET_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <arpa/inet.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/fragbuf.h>
+#include <murphy/common/hashtbl.h>
+
+#include "websocklib.h"
+
+#define LWS_EVENT_OK 0 /* event handler result: ok */
+#define LWS_EVENT_DENY 1 /* event handler result: deny */
+#define LWS_EVENT_ERROR 1 /* event handler result: error */
+#define LWS_EVENT_CLOSE -1 /* event handler result: close */
+
+/* libwebsocket status used to close sockets upon error */
+#ifndef WEBSOCKETS_OLD
+# define LWS_INTERNAL_ERROR LWS_CLOSE_STATUS_UNEXPECTED_CONDITION
+#else
+# define LWS_INTERNAL_ERROR LWS_CLOSE_STATUS_PROTOCOL_ERR /* arrghmm... */
+#endif
+
+/* SSL modes */
+#define LWS_NO_SSL 0 /* no SSL at all */
+#define LWS_SSL 1 /* SSL, deny self-signed certs */
+#define LWS_SSL_SELFSIGNED 2 /* SSL, allow self-signed certs */
+
+/*
+ * define shorter aliasen for libwebsocket types
+ */
+
+typedef struct libwebsocket lws_t;
+typedef struct libwebsocket_context lws_ctx_t;
+typedef struct libwebsocket_extension lws_ext_t;
+typedef struct libwebsocket_protocols lws_proto_t;
+typedef enum libwebsocket_callback_reasons lws_event_t;
+#ifdef WEBSOCKETS_CONTEXT_INFO
+ typedef struct lws_context_creation_info lws_cci_t;
+#else /* !WEBSOCKETS_CONTEXT_INFO */
+typedef struct {
+ int port;
+ const char *iface;
+ struct libwebsocket_protocols *protocols;
+ struct libwebsocket_extension *extensions;
+ const char *ssl_cert_filepath;
+ const char *ssl_private_key_filepath;
+ const char *ssl_ca_filepath;
+ const char *ssl_cipher_list;
+ int gid;
+ int uid;
+ unsigned int options;
+ void *user;
+ int ka_time;
+ int ka_probes;
+ int ka_interval;
+} lws_cci_t;
+#endif /* !WEBSOCKETS_CONTEXT_INFO */
+
+static lws_ext_t *lws_get_internal_extensions(void);
+
+/*
+ * a libwebsocket fd we (e)poll
+ *
+ * Unfortunately the mechanism offered by libwebsockets for external
+ * mainloop integration uses event mask diffs when asking the mainloop
+ * to modify what an fd is polled for. This forces us to do double
+ * bookkeeping: we need to to keep track of the current event mask for
+ * all descriptors just to figure out the new mask when libwebsockets
+ * hands us a diff.
+ */
+
+typedef struct {
+ int fd; /* libwebsocket file descriptor */
+ uint32_t events; /* monitored (epoll) events */
+} pollfd_t;
+
+
+typedef enum {
+ POLLFD_SET = 0,
+ POLLFD_CLEAR = TRUE,
+ POLLFD_CHANGE,
+} pollfd_op_t;
+
+/*
+ * a websocket context
+ */
+
+struct wsl_ctx_s {
+ lws_ctx_t *ctx; /* libwebsocket context */
+ wsl_proto_t *http; /* has HTTP as upper layer protocol */
+ wsl_proto_t *protos; /* protocols */
+ int nproto; /* number of protocols */
+ lws_proto_t *lws_protos; /* libwebsocket protocols */
+ mrp_refcnt_t refcnt; /* reference count */
+ int epollfd; /* epoll descriptor */
+ mrp_io_watch_t *w; /* I/O watch for epollfd */
+ mrp_mainloop_t *ml; /* pumping mainloop */
+ pollfd_t *fds; /* polled descriptors */
+ int nfd; /* number descriptors */
+ void *user_data; /* opaque user data */
+ lws_t *pending; /* pending connection */
+ void *pending_user; /* user_data of pending */
+ wsl_proto_t *pending_proto; /* protocol of pending */
+ mrp_list_hook_t pure_http; /* pure HTTP sockets */
+};
+
+/*
+ * a websocket instance
+ */
+
+struct wsl_sck_s {
+ wsl_ctx_t *ctx; /* associated context */
+ lws_t *sck; /* libwebsocket instance */
+ wsl_proto_t *proto; /* protocol data */
+ wsl_sendmode_t send_mode; /* libwebsocket write mode */
+ mrp_fragbuf_t *buf; /* fragment collection buffer */
+ void *user_data; /* opaque user data */
+ wsl_sck_t **sckptr; /* back pointer from sck to us */
+ int closing : 1; /* close in progress */
+ int pure_http : 1; /* pure HTTP socket */
+ int busy; /* upper-layer callback(s) active */
+ mrp_list_hook_t hook; /* to pure HTTP list, if such */
+};
+
+
+/*
+ * mark a socket busy while executing a piece of code
+ */
+
+#define SOCKET_BUSY_REGION(sck, ...) do { \
+ (sck)->busy++; \
+ __VA_ARGS__; \
+ (sck)->busy--; \
+ } while (0)
+
+
+
+static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len);
+static int wsl_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len);
+static void destroy_context(wsl_ctx_t *ctx);
+
+static void MRP_EXIT destroy_context_table(void);
+
+
+
+static inline uint32_t map_poll_to_event(int in)
+{
+ uint32_t mask = 0;
+
+ if (in & POLLIN) mask |= MRP_IO_EVENT_IN;
+ if (in & POLLOUT) mask |= MRP_IO_EVENT_OUT;
+ if (in & POLLHUP) mask |= MRP_IO_EVENT_HUP;
+ if (in & POLLERR) mask |= MRP_IO_EVENT_ERR;
+
+ return mask;
+
+}
+
+
+static inline short map_event_to_poll(uint32_t in)
+{
+ short mask = 0;
+
+ if (in & MRP_IO_EVENT_IN) mask |= POLLIN;
+ if (in & MRP_IO_EVENT_OUT) mask |= POLLOUT;
+ if (in & MRP_IO_EVENT_HUP) mask |= POLLHUP;
+ if (in & MRP_IO_EVENT_ERR) mask |= POLLERR;
+
+ return mask;
+}
+
+
+static int add_fd(wsl_ctx_t *wsc, int fd, int events)
+{
+ struct epoll_event e;
+
+ if (wsc != NULL) {
+ e.data.u64 = 0;
+ e.data.fd = fd;
+ e.events = map_poll_to_event(events);
+
+ if (epoll_ctl(wsc->epollfd, EPOLL_CTL_ADD, fd, &e) == 0) {
+ if (mrp_reallocz(wsc->fds, wsc->nfd, wsc->nfd + 1) != NULL) {
+ wsc->fds[wsc->nfd].fd = fd;
+ wsc->fds[wsc->nfd].events = e.events;
+ wsc->nfd++;
+
+ return TRUE;
+ }
+ else
+ epoll_ctl(wsc->epollfd, EPOLL_CTL_DEL, fd, &e);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int del_fd(wsl_ctx_t *wsc, int fd)
+{
+ struct epoll_event e;
+ int i;
+
+ if (wsc != NULL) {
+ e.data.u64 = 0;
+ e.data.fd = fd;
+ e.events = 0;
+ epoll_ctl(wsc->epollfd, EPOLL_CTL_DEL, fd, &e);
+
+ for (i = 0; i < wsc->nfd; i++) {
+ if (wsc->fds[i].fd == fd) {
+ if (i < wsc->nfd - 1)
+ memmove(wsc->fds + i, wsc->fds + i + 1,
+ (wsc->nfd - i - 1) * sizeof(*wsc->fds));
+
+ mrp_reallocz(wsc->fds, wsc->nfd, wsc->nfd - 1);
+ wsc->nfd--;
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static pollfd_t *find_fd(wsl_ctx_t *wsc, int fd)
+{
+ int i;
+
+ if (wsc != NULL) {
+ for (i = 0; i < wsc->nfd; i++)
+ if (wsc->fds[i].fd == fd)
+ return wsc->fds + i;
+ }
+
+ return NULL;
+}
+
+
+static int mod_fd(wsl_ctx_t *wsc, int fd, int events, int op)
+{
+ struct epoll_event e;
+ pollfd_t *wfd;
+
+ if (wsc != NULL) {
+ wfd = find_fd(wsc, fd);
+
+ if (wfd != NULL) {
+ e.data.u64 = 0;
+ e.data.fd = fd;
+
+ switch (op) {
+ case POLLFD_CLEAR:
+ e.events = wfd->events & ~map_poll_to_event(events);
+ break;
+ case POLLFD_SET:
+ e.events = wfd->events | map_poll_to_event(events);
+ break;
+ case POLLFD_CHANGE:
+ e.events = wfd->events = map_poll_to_event(events);
+ break;
+ default:
+ return FALSE;
+ }
+
+ if (epoll_ctl(wsc->epollfd, EPOLL_CTL_MOD, fd, &e) == 0)
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_fds(wsl_ctx_t *wsc)
+{
+ if (wsc != NULL) {
+ mrp_free(wsc->fds);
+ wsc->fds = NULL;
+ wsc->nfd = 0;
+ }
+}
+
+
+static void epoll_event(mrp_io_watch_t *w, int fd, mrp_io_event_t mask,
+ void *user_data)
+{
+ wsl_ctx_t *wsc = (wsl_ctx_t *)user_data;
+ pollfd_t *wfd;
+ struct epoll_event *events, *e;
+ int nevent, n, i;
+ struct pollfd pollfd;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(fd);
+
+ if (wsc->nfd <= 0 || !(mask & MRP_IO_EVENT_IN))
+ return;
+
+ nevent = wsc->nfd;
+ events = alloca(nevent * sizeof(*events));
+
+ while ((n = epoll_wait(wsc->epollfd, events, nevent, 0)) > 0) {
+ mrp_debug("got %d epoll events for websocket context %p", n, wsc);
+
+ for (i = 0, e = events; i < n; i++, e++) {
+ wfd = find_fd(wsc, e->data.fd);
+
+ if (wfd != NULL) {
+ pollfd.fd = wfd->fd;
+ pollfd.events = map_event_to_poll(wfd->events);
+ pollfd.revents = map_event_to_poll(e->events);
+
+ mrp_debug("delivering events 0x%x to websocket fd %d",
+ pollfd.revents, pollfd.fd);
+
+ libwebsocket_service_fd(wsc->ctx, &pollfd);
+ }
+ }
+ }
+}
+
+
+/*
+ * context handling
+ */
+
+#ifdef WEBSOCKETS_OLD
+
+/*
+ * Notes:
+ * In some environments we might be forced to run with really old
+ * versions of libwebsockets. This causes some amount of pain as
+ * some of the recent features in libwebsockets are essential for
+ * building a reasonable abstraction on top of it.
+ *
+ * Most notably, versions prior to 0291eb3..d764e84 (Oct 19 2012)
+ * do not support per-context user data. Since we need to associate
+ * our context with that of libwebsockets we have to build an extra
+ * mechanism for mapping between the two when user data support is
+ * not available. We use an extra hash table to store our context
+ * and use directly the (low 32-bits of the) libwebsocket context
+ * pointer as the key to store and fetch it.
+ *
+ * To minimize unreadibility and other code uglification factors,
+ * most of the code that deals with this version-dependent extra
+ * mechanism is put into the contamination chamber formed by the
+ * triplet of {set,get,clear}_context_userdata routines below.
+ *
+ * Note that since we decided (maybe unecessarily) to do mainloop
+ * integration also on a per-context basis, with old versions we
+ * also have a phase error: the first pollfd manipulation events
+ * for a context being created come __before__ the libwebsockets
+ * context creation routine returns, IOW before we get a chance to
+ * administer the reverse mapping between the contexts. This is
+ * unfortunate because if we cannot find our context for a pollfd
+ * request/event we just cannot handle it. This in turn would result
+ * in a practically useless context as its socket would not be
+ * (e)polled at all.
+ *
+ * The ugly pending_userdata kludge below is to overcome this problem.
+ * While creating a context, we register its intended userdata as the
+ * pending userdata before calling libwebsockets context creation
+ * routine and clear it afterwards. get_context_userdata knows to
+ * return the pending_userdata if it cannot do the reverse mapping
+ * using the libwebsocket context pointer. While this is quite ugly,
+ * I really don't see any other way. A limitation of this is that we
+ * cannot have two unfinished contexts being created in parallel, but
+ * that really should not happen under any circumstances anyway.
+ */
+
+
+static mrp_htbl_t *ctxtbl;
+static void *pending_userdata;
+
+static int ctx_cmp(const void *ctx1, const void *ctx2)
+{
+ return ctx2 - ctx1;
+}
+
+static uint32_t ctx_hash(const void *ctx)
+{
+ uint64_t h;
+
+ h = (ptrdiff_t)ctx;
+
+ return (uint32_t)(h & 0xffffffff);
+}
+
+static int create_context_table(void)
+{
+ mrp_htbl_config_t hcfg;
+
+ if (ctxtbl == NULL) {
+ mrp_clear(&hcfg);
+
+ hcfg.comp = ctx_cmp;
+ hcfg.hash = ctx_hash;
+ hcfg.free = NULL;
+
+ ctxtbl = mrp_htbl_create(&hcfg);
+
+ return (ctxtbl != NULL);
+ }
+ else
+ return TRUE;
+}
+
+static void destroy_context_table(void)
+{
+ if (ctxtbl != NULL)
+ mrp_htbl_destroy(ctxtbl, FALSE);
+}
+
+
+static int set_pending_userdata(void *ptr)
+{
+ if (pending_userdata == NULL) {
+ pending_userdata = ptr;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void clear_pending_userdata(void *ptr)
+{
+ if (pending_userdata == ptr)
+ pending_userdata = NULL;
+}
+
+
+static void *get_pending_userdata(void)
+{
+ return pending_userdata;
+}
+
+
+static int set_context_userdata(lws_ctx_t *ws_ctx, wsl_ctx_t *ctx)
+{
+ if (ctxtbl == NULL)
+ create_context_table();
+
+ if (ctxtbl != NULL)
+ return mrp_htbl_insert(ctxtbl, ws_ctx, ctx);
+ else
+ return FALSE;
+}
+
+
+static wsl_ctx_t *get_context_userdata(lws_ctx_t *ws_ctx)
+{
+ wsl_ctx_t *ctx;
+
+ if (ctxtbl != NULL)
+ ctx = (wsl_ctx_t *)mrp_htbl_lookup(ctxtbl, (void *)ws_ctx);
+ else
+ ctx = NULL;
+
+ if (ctx != NULL)
+ return ctx;
+ else
+ return get_pending_userdata();
+}
+
+
+static void clear_context_userdata(lws_ctx_t *ws_ctx)
+{
+ if (ctxtbl != NULL)
+ mrp_htbl_remove(ctxtbl, ws_ctx, FALSE);
+}
+
+
+static lws_ctx_t *lws_create_ctx(lws_cci_t *cci)
+{
+ lws_ctx_t *ws_ctx;
+
+ set_pending_userdata(cci->user);
+
+ ws_ctx = libwebsocket_create_context(cci->port, cci->iface,
+ cci->protocols, cci->extensions,
+ cci->ssl_cert_filepath,
+ cci->ssl_private_key_filepath,
+ /* no ssl_ca */
+ cci->gid, cci->uid, cci->options
+ /*no user_data*/);
+ if (ws_ctx != NULL)
+ set_context_userdata(ws_ctx, cci->user);
+
+ clear_pending_userdata(cci->user);
+
+ return ws_ctx;
+}
+
+#else /* !WEBSOCKETS_OLD */
+
+static void destroy_context_table(void)
+{
+ return;
+}
+
+
+static wsl_ctx_t *get_context_userdata(lws_ctx_t *ws_ctx)
+{
+ return libwebsocket_context_user(ws_ctx);
+}
+
+
+static void clear_context_userdata(lws_ctx_t *ws_ctx)
+{
+ MRP_UNUSED(ws_ctx);
+}
+
+
+static lws_ctx_t *lws_create_ctx(lws_cci_t *cci)
+{
+#ifdef WEBSOCKETS_CONTEXT_INFO
+ return libwebsocket_create_context(cci);
+#else
+ return libwebsocket_create_context(cci->port, cci->iface,
+ cci->protocols, cci->extensions,
+ cci->ssl_cert_filepath,
+ cci->ssl_private_key_filepath,
+ cci->ssl_ca_filepath,
+ cci->gid, cci->uid, cci->options,
+ cci->user);
+#endif
+}
+
+#endif /* !WEBSOCKETS_OLD */
+
+static lws_ext_t *lws_get_internal_extensions(void)
+{
+#ifdef WEBSOCKETS_QUERY_EXTENSIONS
+ return libwebsocket_get_internal_extensions();
+#else
+ return libwebsocket_internal_extensions;
+#endif
+}
+
+static int find_device(struct sockaddr *sa, char *buf, size_t size)
+{
+ struct sockaddr *ia;
+ struct ifreq ifreq[64];
+ struct ifconf ifconf;
+ int status, sck, n, i;
+
+ /*
+ * XXX FIXME: we only handle primary addresses at the moment...
+ */
+
+ if (size < IFNAMSIZ) {
+ errno = ENOBUFS;
+ return -1;
+ }
+
+ if (sa->sa_family != AF_INET) { /* libwebsockets can't handle IPv6 */
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr == 0x0) {
+ *buf = '\0';
+ return 0;
+ }
+
+ sck = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (sck < 0)
+ return -1;
+
+ ifconf.ifc_len = sizeof(ifreq);
+ ifconf.ifc_buf = (char *)&ifreq[0];
+
+ status = ioctl(sck, SIOCGIFCONF, &ifconf);
+ close(sck);
+
+ if (status < 0)
+ return -1;
+
+ n = ifconf.ifc_len / sizeof(ifreq[0]);
+
+ for (i = 0; i < n; i++) {
+ ia = &ifreq[i].ifr_addr;
+
+ if (ia->sa_family == sa->sa_family) {
+ if (((struct sockaddr_in *)sa)->sin_addr.s_addr ==
+ ((struct sockaddr_in *)ia)->sin_addr.s_addr) {
+ strncpy(buf, ifreq[i].ifr_name, IFNAMSIZ - 1);
+ buf[IFNAMSIZ - 1] = '\0';
+ return 0;
+ }
+ }
+ }
+
+ errno = EADDRNOTAVAIL;
+ return -1;
+}
+
+
+wsl_ctx_t *wsl_create_context(mrp_mainloop_t *ml, wsl_ctx_cfg_t *cfg)
+{
+ lws_ext_t *builtin = lws_get_internal_extensions();
+ lws_cci_t cci;
+ wsl_ctx_t *ctx;
+ wsl_proto_t *up, *http;
+ lws_proto_t *lws_protos, *lp;
+ int lws_nproto;
+ mrp_io_event_t events;
+ char ifname[IFNAMSIZ + 1];
+ int i;
+
+ ctx = NULL;
+ mrp_clear(&cci);
+
+ if (cfg->addr != NULL) {
+ if (find_device(cfg->addr, ifname, sizeof(ifname)) < 0)
+ return NULL;
+ else {
+ mrp_debug("address mapped to device '%s'",
+ *ifname ? ifname : "<any>");
+
+ cci.iface = ifname;
+
+ switch (cfg->addr->sa_family) {
+ case AF_INET:
+ cci.port = ntohs(((struct sockaddr_in *)cfg->addr)->sin_port);
+ break;
+ case AF_INET6:
+ cci.port = ntohs(((struct sockaddr_in6 *)cfg->addr)->sin6_port);
+ break;
+ default:
+ goto fail;
+ }
+ }
+ }
+
+ ctx = mrp_allocz(sizeof(*ctx));
+
+ if (ctx == NULL)
+ goto fail;
+
+ mrp_refcnt_init(&ctx->refcnt);
+ mrp_list_init(&ctx->pure_http);
+
+ ctx->protos = cfg->protos;
+ ctx->nproto = cfg->nproto;
+
+ if (!strcmp(cfg->protos[0].name, "http") ||
+ !strcmp(cfg->protos[0].name, "http-only"))
+ http = &cfg->protos[0];
+ else
+ http = NULL;
+
+ lws_nproto = (http ? cfg->nproto : cfg->nproto + 1) + 1;
+ lws_protos = mrp_allocz_array(lws_proto_t, lws_nproto);
+
+ if (lws_protos == NULL)
+ goto fail;
+
+ lws_protos[0].name = "http";
+ lws_protos[0].callback = http_event;
+ if (!http)
+ lws_protos[0].per_session_data_size = sizeof(void *);
+ else
+ lws_protos[0].per_session_data_size = sizeof(void *);
+
+ lp = lws_protos + 1;
+ up = cfg->protos + (http ? 1 : 0);
+
+ for (i = (http ? 1 : 0); i < cfg->nproto; i++) {
+ lp->name = up->name;
+ lp->callback = wsl_event;
+ lp->per_session_data_size = sizeof(void *);
+
+ lp++;
+ up++;
+ }
+
+ ctx->lws_protos = lws_protos;
+ ctx->http = http;
+
+ ctx->epollfd = epoll_create1(EPOLL_CLOEXEC);
+
+ if (ctx->epollfd < 0)
+ goto fail;
+
+ events = MRP_IO_EVENT_IN;
+ ctx->ml = ml;
+ ctx->w = mrp_add_io_watch(ml, ctx->epollfd, events, epoll_event, ctx);
+
+ if (ctx->w == NULL)
+ goto fail;
+
+ cci.protocols = lws_protos;
+ cci.extensions = builtin;
+ cci.user = ctx;
+ cci.gid = cfg->gid;
+ cci.uid = cfg->uid;
+
+ cci.ssl_cert_filepath = cfg->ssl_cert;
+ cci.ssl_private_key_filepath = cfg->ssl_pkey;
+ cci.ssl_ca_filepath = cfg->ssl_ca;
+ cci.ssl_cipher_list = cfg->ssl_ciphers;
+
+ cci.options = 0;
+ cci.ka_time = cfg->timeout;
+ cci.ka_probes = cfg->nprobe;
+ cci.ka_interval = cfg->interval;
+
+ ctx->ctx = lws_create_ctx(&cci);
+
+ if (ctx->ctx != NULL) {
+ ctx->user_data = cfg->user_data;
+
+ return ctx;
+ }
+
+ fail:
+ if (ctx != NULL) {
+ if (ctx->epollfd >= 0) {
+ mrp_del_io_watch(ctx->w);
+ close(ctx->epollfd);
+ }
+
+ mrp_free(ctx);
+ }
+
+ return NULL;
+}
+
+
+wsl_ctx_t *wsl_ref_context(wsl_ctx_t *ctx)
+{
+ return mrp_ref_obj(ctx, refcnt);
+}
+
+
+int wsl_unref_context(wsl_ctx_t *ctx)
+{
+ if (mrp_unref_obj(ctx, refcnt)) {
+ mrp_debug("refcount of context %p dropped to zero", ctx);
+ destroy_context(ctx);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void destroy_context(wsl_ctx_t *ctx)
+{
+ if (ctx != NULL) {
+ mrp_debug("destroying context %p", ctx);
+
+ mrp_del_io_watch(ctx->w);
+ ctx->w = NULL;
+
+ close(ctx->epollfd);
+ ctx->epollfd = -1;
+
+ purge_fds(ctx);
+
+ if (ctx->ctx != NULL) {
+ clear_context_userdata(ctx->ctx);
+ libwebsocket_context_destroy(ctx->ctx);
+ }
+
+ mrp_free(ctx->lws_protos);
+ mrp_free(ctx);
+ }
+}
+
+
+static wsl_proto_t *find_context_protocol(wsl_ctx_t *ctx, const char *protocol)
+{
+ wsl_proto_t *up;
+ int i;
+
+ if (protocol != NULL) {
+ for (i = 0, up = ctx->protos; i < ctx->nproto; i++, up++)
+ if (!strcmp(up->name, protocol))
+ return up;
+ }
+
+ return NULL;
+}
+
+
+static wsl_sck_t *find_pure_http(wsl_ctx_t *ctx, lws_t *ws)
+{
+ mrp_list_hook_t *p, *n;
+ wsl_sck_t *sck;
+
+ /*
+ * Notes:
+ * We expect an extremely low number of concurrent pure
+ * HTTP connections so we do asimple linear search here.
+ * We can change this if this turns out to be a false
+ * assumption.
+ */
+
+ mrp_list_foreach(&ctx->pure_http, p, n) {
+ sck = mrp_list_entry(p, typeof(*sck), hook);
+
+ if (sck->sck == ws)
+ return sck;
+ }
+
+ return NULL;
+}
+
+
+wsl_sck_t *wsl_connect(wsl_ctx_t *ctx, struct sockaddr *sa,
+ const char *protocol, wsl_ssl_t ssl, void *user_data)
+{
+ wsl_sck_t *sck, **ptr;
+ wsl_proto_t *up;
+ int port;
+ void *aptr;
+ char abuf[256];
+ const char *astr;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ aptr = &((struct sockaddr_in *)sa)->sin_addr;
+ port = ntohs(((struct sockaddr_in *)sa)->sin_port);
+ break;
+ case AF_INET6:
+ aptr = &((struct sockaddr_in6 *)sa)->sin6_addr;
+ port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ astr = inet_ntop(sa->sa_family, aptr, abuf, sizeof(abuf));
+
+ if (astr == NULL)
+ return NULL;
+
+ up = find_context_protocol(ctx, protocol);
+
+ if (up == NULL) {
+ errno = ENOPROTOOPT;
+ return NULL;
+ }
+
+ sck = mrp_allocz(sizeof(*sck));
+ ptr = mrp_allocz(sizeof(*ptr));
+
+ if (sck != NULL && ptr != NULL) {
+ /*
+ * Now we need to create and connect a new libwebsocket instance
+ * within the given context. We also need to set up a one-to-one
+ * mapping between the underlying libwebsocket and our wsl_sck_t
+ * so that we can handle both top-down (sending) and bottom-up
+ * (receiving) event propagation in the stack.
+ *
+ * We use the user data associated with the libwebsocket instance
+ * to store a back pointer to us. Whenever the socket instance
+ * is deleted locally (as opposed to our peer closing the session)
+ * we need to prevent the propagation of any potentially pending
+ * events to our deleted wsl_sck_t (which might have been freed).
+ * This we do by clearing the back pointer from the instance to us.
+ *
+ * However, since libwebsockets does not provide an API for this,
+ * as a trick we use an indirect back pointer and store a pointer
+ * to the actual back pointer also in wsl_sck_t here. This way we
+ * can always clear the back pointer when we need to.
+ *
+ * Also note, that memory management for the associated user data
+ * is asymmetric in many sense. For client connections, we allocate
+ * the data buffer and pass it on to libwebsockets. For incoming
+ * connections the user data buffer is allocated by libwebsockets
+ * and we only get a chance to fill it in the event handler for
+ * connection establishment. However, for both incoming and outgoing
+ * connections libwebsockets will free the buffer on behalf of us.
+ *
+ * The exact same notes apply to wsl_accept_pending below...
+ */
+
+ mrp_list_init(&sck->hook);
+ sck->ctx = wsl_ref_context(ctx);
+ sck->proto = up;
+ sck->buf = mrp_fragbuf_create(/*up->framed*/TRUE, 0);
+
+ if (sck->buf != NULL) {
+ sck->user_data = user_data;
+
+ if (strncmp(protocol, "http", 4)) { /* Think harder, Homer ! */
+ *ptr = sck;
+ sck->sckptr = ptr;
+ }
+ else
+ mrp_list_append(&ctx->pure_http, &sck->hook);
+
+ sck->sck = libwebsocket_client_connect_extended(ctx->ctx,
+ astr, port,
+ ssl,
+ "/", astr, astr,
+ protocol, -1,
+ ptr);
+
+ if (sck->sck != NULL)
+ return sck;
+
+ mrp_fragbuf_destroy(sck->buf);
+ mrp_list_delete(&sck->hook);
+ }
+
+ wsl_unref_context(ctx);
+ mrp_free(ptr);
+ mrp_free(sck);
+ }
+
+ return NULL;
+}
+
+
+wsl_sck_t *wsl_accept_pending(wsl_ctx_t *ctx, void *user_data)
+{
+ wsl_sck_t *sck, **ptr;
+
+ if (ctx->pending == NULL || ctx->pending_proto == NULL)
+ return NULL;
+
+ mrp_debug("accepting pending websocket connection %p/%p", ctx->pending,
+ ctx->pending_user);
+
+ sck = mrp_allocz(sizeof(*sck));
+
+ if (sck != NULL) {
+ mrp_list_init(&sck->hook);
+
+ /*
+ * Notes:
+ * The same notes apply here for context creation as for
+ * wsl_connect above...
+ */
+ sck->ctx = wsl_ref_context(ctx);
+ sck->buf = mrp_fragbuf_create(/*ctx->pending_proto->framed*/TRUE, 0);
+
+ if (sck->buf != NULL) {
+ sck->proto = ctx->pending_proto;
+ sck->user_data = user_data;
+ sck->sck = ctx->pending;
+ ptr = (wsl_sck_t **)ctx->pending_user;
+ sck->sckptr = ptr;
+
+ mrp_debug("pending connection was a %s websocket",
+ ptr != NULL ? "real" : "HTTP");
+
+ if (ptr != NULL) /* genuine websocket */
+ *ptr = sck;
+ else /* pure http socket */
+ mrp_list_append(&ctx->pure_http, &sck->hook);
+
+ /* let the event handler know we accepted the client */
+ ctx->pending = NULL;
+ /* for pure http communicate sck back in pending_user */
+ ctx->pending_user = (ptr == NULL ? sck : NULL);
+ ctx->pending_proto = NULL;
+
+ return sck;
+ }
+
+ wsl_unref_context(ctx);
+ mrp_free(sck);
+ }
+
+ return NULL;
+}
+
+
+void wsl_reject_pending(wsl_ctx_t *ctx)
+{
+ mrp_debug("reject pending websocket (%s) connection %p/%p",
+ ctx->pending_proto->name, ctx->pending, ctx->pending_user);
+
+ /*
+ * Nothing to do here really... just don't clear ctx->pending so the
+ * event handler will know to reject once it regains control.
+ */
+}
+
+
+#ifdef WEBSOCKETS_CLOSE_SESSION
+
+/*
+ * WTF ? The prototype for this has been moved from libwebsockets.h to
+ * the uninstalled private-libwebsockets.h. If this is really going to
+ * be made private eventually, how is one supposed to close a websocket
+ * without closing its context and a side effect all other websockets
+ * associated with the same context ?
+ */
+extern void libwebsocket_close_and_free_session(struct libwebsocket_context *,
+ struct libwebsocket *,
+ enum lws_close_status);
+
+
+void *wsl_close(wsl_sck_t *sck)
+{
+ wsl_ctx_t *ctx;
+ void *user_data;
+ int status;
+
+ user_data = NULL;
+
+ if (sck != NULL) {
+ if (sck->sck != NULL && sck->busy <= 0) {
+ mrp_debug("closing websocket %p/%p", sck, sck->sck);
+
+ status = LWS_CLOSE_STATUS_NORMAL;
+ ctx = sck->ctx;
+
+ sck->closing = TRUE;
+ libwebsocket_close_and_free_session(ctx->ctx, sck->sck, status);
+ sck->sck = NULL;
+
+ if (sck->sckptr != NULL) /* genuine websocket */
+ *sck->sckptr = NULL;
+ else /* pure http socket */
+ mrp_list_delete(&sck->hook);
+
+ if (ctx != NULL) {
+ user_data = ctx->user_data;
+ wsl_unref_context(ctx);
+ sck->ctx = NULL;
+ }
+
+ mrp_fragbuf_destroy(sck->buf);
+ sck->buf = NULL;
+
+ mrp_debug("freeing websocket %p", sck);
+ mrp_free(sck);
+ }
+ else {
+ mrp_debug("marking websocket %p/%p for closing", sck, sck->sck);
+ sck->closing = TRUE;
+ }
+ }
+
+ return user_data;
+}
+
+
+#else /* !WEBSOCKET_CLOSE_SESSION */
+
+void *wsl_close(wsl_sck_t *sck)
+{
+ lws_ctx_t *ws_ctx;
+ lws_t *ws;
+ void *user_data;
+
+ /*
+ * With recent libwebsockets libwebsocket_close_and_free_session has
+ * been fully turned into a private library symbol. According to the
+ * docs the official way to trigger closing a websocket from the
+ * 'upper layers' (ie. outside of libwebsocket event callbacks) is to
+ * 1) administer the fact that the websocket should be closed
+ * 2) enable pollouts for the websocket (callback_on_writable)
+ * 3) hope that libwebsockets will not decide to omit delivering a
+ * LWS_CALLBACK_{CLIENT,SERVER}_WRITEABLE event, and
+ * 4) in the event callback check if the websocket is marked for
+ * deletion, and if it is reutrn -1 to indicate libwebsockets that
+ * it should close the socket
+ * Hmm... I guess simple elegance was not one of the design principles.
+ *
+ * Anyway, here's our second attempt to implement this indirect socket
+ * closing scheme without too much memory corruption and leaks... Argh.
+ *
+ * Notes: XXX TODO
+ * Currently we only check and handle pending deletion when
+ * dealing with *_WRITEABLE events. Probably we should also do
+ * it for a few other events as well, for instance for *_RECEIVE
+ * and *_CALLBACK_HTTP).
+ */
+
+ user_data = NULL;
+
+ if (sck != NULL) {
+ if (sck->sck != NULL && sck->busy <= 0) {
+ mrp_debug("closing %s websocket %p/%p",
+ sck->sckptr ? "real" : "HTTP", sck->sck, sck);
+
+ ws = sck->sck;
+ sck->sck = NULL;
+ sck->closing = TRUE;
+
+ /* clear the back pointer to us */
+ if (sck->sckptr != NULL)
+ *sck->sckptr = NULL;
+ else
+ mrp_list_delete(&sck->hook);
+
+ if (sck->ctx != NULL) {
+ ws_ctx = sck->ctx->ctx;
+ user_data = sck->ctx->user_data;
+ wsl_unref_context(sck->ctx);
+ sck->ctx = NULL;
+ }
+ else
+ ws_ctx = NULL;
+
+ mrp_fragbuf_destroy(sck->buf);
+ sck->buf = NULL;
+
+ mrp_debug("freeing websocket %p", sck);
+ mrp_free(sck);
+
+ if (ws_ctx != NULL)
+ libwebsocket_callback_on_writable(ws_ctx, ws);
+ }
+ else
+ sck->closing = TRUE;
+ }
+
+ return user_data;
+}
+
+
+#endif /* !WEBSOCKET_CLOSE_SESSION */
+
+
+static int check_closed(wsl_sck_t *sck)
+{
+ if (sck != NULL) {
+ if (sck->closing && sck->busy <= 0) {
+ wsl_close(sck);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+int wsl_set_sendmode(wsl_sck_t *sck, wsl_sendmode_t mode)
+{
+ const char *name;
+
+ switch (mode) {
+ case WSL_SEND_TEXT: name = "text"; break;
+ case WSL_SEND_BINARY: name = "binary"; break;
+ default: return FALSE;
+ }
+
+ mrp_debug("websocket %p/%p mode changed to %s", sck, sck->sck, name);
+ sck->send_mode = mode;
+
+ return TRUE;
+}
+
+
+int wsl_send(wsl_sck_t *sck, void *payload, size_t size)
+{
+ unsigned char *buf;
+ size_t pre, post, total;
+ uint32_t *len;
+
+ if (sck != NULL && sck->sck != NULL) {
+ if (sck->proto->framed) {
+ pre = LWS_SEND_BUFFER_PRE_PADDING;
+ post = LWS_SEND_BUFFER_POST_PADDING;
+ buf = alloca(pre + sizeof(*len) + size + post);
+ len = (uint32_t *)(buf + pre);
+ *len = htobe32(size);
+
+ memcpy(buf + pre + sizeof(*len), payload, size);
+ total = sizeof(*len) + size;
+ }
+ else {
+ pre = LWS_SEND_BUFFER_PRE_PADDING;
+ post = LWS_SEND_BUFFER_POST_PADDING;
+ buf = alloca(pre + size + post);
+
+ memcpy(buf + pre, payload, size);
+ total = size;
+ }
+
+#if (WSL_SEND_TEXT != 0)
+ if (!sck->send_mode)
+ sck->send_mode = WSL_SEND_TEXT;
+#endif
+
+ if (libwebsocket_write(sck->sck, buf + pre, total, sck->send_mode) >= 0)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int wsl_serve_http_file(wsl_sck_t *sck, const char *path, const char *type)
+{
+ mrp_debug("serving file '%s' (%s) over websocket %p", path, type, sck->sck);
+
+#ifndef WEBSOCKETS_OLD
+# ifdef WEBSOCKETS_SERVE_FILE_EXTRAARG
+ if (libwebsockets_serve_http_file(sck->ctx->ctx, sck->sck, path,
+ type, NULL) == 0)
+ return TRUE;
+ else
+ return FALSE;
+# else
+ if (libwebsockets_serve_http_file(sck->ctx->ctx, sck->sck, path, type) == 0)
+ return TRUE;
+ else
+ return FALSE;
+# endif
+#else
+ if (libwebsockets_serve_http_file(sck->sck, path, type) == 0)
+ return TRUE;
+ else
+ return FALSE;
+#endif
+}
+
+
+#ifdef LWS_OPENSSL_SUPPORT
+
+static void load_extra_certs(wsl_ctx_t *ctx, void *user, lws_event_t event)
+{
+ int is_server;
+
+ if (ctx != NULL && ctx->load_certs != NULL) {
+ if (event == LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS)
+ is_server = TRUE;
+ else
+ is_server = FALSE;
+
+ ctx->load_certs(ctx, (SSL_CTX *)user, is_server);
+ }
+}
+
+
+static int verify_client_cert(void *user, void *in, size_t len)
+{
+ X509_STORE_CTX *x509_ctx;
+ SSL *ssl;
+ int pre_ok;
+
+ if (verify_client_cert_cb != NULL) {
+ x509_ctx = (X509_STORE_CTX *)user;
+ ssl = (SSL *)in;
+ pre_ok = (int)len;
+
+ if (verify_client_cert_cb(x509_ctx, ssl, pre_ok))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+#else /* !LWS_OPENSSL_SUPPORT */
+
+static void load_extra_certs(wsl_ctx_t *ctx, void *user, lws_event_t event)
+{
+ MRP_UNUSED(ctx);
+ MRP_UNUSED(user);
+ MRP_UNUSED(event);
+
+ return;
+}
+
+
+static int verify_client_cert(void *user, void *in, size_t len)
+{
+ MRP_UNUSED(user);
+ MRP_UNUSED(in);
+ MRP_UNUSED(len);
+
+ return TRUE;
+}
+
+#endif
+
+
+
+static int http_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len)
+{
+ wsl_ctx_t *ctx = get_context_userdata(ws_ctx);
+ wsl_sck_t *sck;
+ wsl_proto_t *up;
+ const char *ext, *uri;
+ int fd, mask, status, accepted;
+
+ switch (event) {
+ case LWS_CALLBACK_ESTABLISHED:
+ mrp_debug("client-handshake completed on websocket %p/%p", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLOSED:
+ mrp_debug("websocket %p/%p closed", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ mrp_debug("server-handshake completed on websocket %p/%p", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ mrp_debug("client connection failed");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_RECEIVE:
+ mrp_debug("received HTTP data from client");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ mrp_debug("recived HTTP data from server");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_RECEIVE_PONG:
+ mrp_debug("client received pong");
+ return LWS_EVENT_OK;
+
+ /*
+ * mainloop integration
+ */
+#ifdef WEBSOCKETS_CHANGE_MODE_POLL_FD
+
+ case LWS_CALLBACK_ADD_POLL_FD: {
+ struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ fd = pa->fd;
+ mask = pa->events;
+
+ mrp_debug("start polling fd %d for events 0x%x", fd, mask);
+ if (add_fd(ctx, fd, mask))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+ }
+
+ case LWS_CALLBACK_DEL_POLL_FD: {
+ struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ fd = pa->fd;
+
+ mrp_debug("stop polling fd %d", fd);
+ if (del_fd(ctx, fd))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+ }
+
+ case LWS_CALLBACK_CHANGE_MODE_POLL_FD: {
+ struct libwebsocket_pollargs *pa = (struct libwebsocket_pollargs *)in;
+ fd = pa->fd;
+ mask = pa->events;
+
+ mrp_debug("setting poll events to 0x%x for fd %d", mask, fd);
+ if (mod_fd(ctx, fd, mask, FALSE))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+ }
+
+#else /* WEBSOCKETS_CHANGE_MODE_POLL_FD */
+
+ case LWS_CALLBACK_ADD_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mask = (int)len;
+ mrp_debug("start polling fd %d for events 0x%x", fd, mask);
+ if (add_fd(ctx, fd, mask))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+ case LWS_CALLBACK_DEL_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mrp_debug("stop polling fd %d", fd);
+ if (del_fd(ctx, fd))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+ case LWS_CALLBACK_SET_MODE_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mask = (int)len;
+ mrp_debug("enable poll events 0x%x for fd %d", mask, fd);
+ if (mod_fd(ctx, fd, mask, FALSE))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+ case LWS_CALLBACK_CLEAR_MODE_POLL_FD:
+#ifdef WEBSOCKETS_CONTEXT_INFO /* just brilliant... */
+ fd = (ptrdiff_t)in;
+#else
+ fd = (ptrdiff_t)user;
+#endif
+ mask = (int)len;
+ mrp_debug("disable poll events 0x%x for fd %d", mask, fd);
+ if (mod_fd(ctx, fd, mask, TRUE))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_ERROR;
+
+#endif /* WEBSOCKETS_CHANGE_MODE_POLL_FD */
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = find_pure_http(ctx, ws);
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket server side writeable again");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = find_pure_http(ctx, ws);
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket client side writeable again");
+ return LWS_EVENT_OK;
+
+ /*
+ * clients wanting to stay pure HTTP clients
+ *
+ * Notes:
+ * Clients that stay pure HTTP clients (ie. do not negotiate a
+ * websocket connection) never get an LWS_CALLBACK_ESTABLISHED
+ * event emitted for. This is a bit unfortunate, since that is
+ * the event we map to the incoming connection event of our
+ * transport layer.
+ *
+ * However, we'd really like to keep pure HTTP and websocket
+ * connections as much equal as possible. First and foremost
+ * this means that we'd like to associate our own websocklib
+ * wsl_sck_t socket context to lws_t and vice versa. Also
+ * similarly to websocket connections we want to give the upper
+ * layer a chance to accept or reject the connection.
+ *
+ * Since there is no ESTABLISHED event for pure HTTP clients,
+ * we have to emulate one such here. We need to check if test
+ * ws belongs to a known connection by checking if it has an
+ * associated wsl_sck_t. If not we need to call the upper layer
+ * to let it accept or reject the connection. If it has already
+ * we need to call the reception handler of the upper layer.
+ *
+ * However, unfortunately libwebsockets never allocates user
+ * data for the HTTP websockets even we specify a non-zero size
+ * for protocol 0. Hence, we cannot use our normal mechanism of
+ * associating the upper layer wsl_sck_t context using the ws
+ * user data. Instead we need to separately keep track of HTTP
+ * websockets and look up the associated wsl_sck_t using this
+ * secondary bookkeeping.
+ */
+
+
+#ifdef WEBSOCKETS_FILTER_HTTP_CONNECTION
+ case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
+ return 0;
+#endif
+
+ case LWS_CALLBACK_HTTP:
+ uri = (const char *)in;
+
+ if (ctx->http == NULL) {
+ mrp_debug("denying HTTP request of '%s' for httpless context", uri);
+ return LWS_EVENT_DENY;
+ }
+
+ sck = find_pure_http(ctx, ws);
+
+ if (sck != NULL) { /* known socket, deliver event */
+ deliver_event:
+ up = sck->proto;
+
+ if (up != NULL) {
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.recv(sck, in, strlen(uri), sck->user_data,
+ up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ sck = find_pure_http(ctx, ws);
+
+ if (check_closed(sck))
+ return 0;
+ }
+
+ status = LWS_EVENT_OK;
+ }
+ else { /* unknown socket, needs to accept */
+ if (ctx->pending != NULL) {
+ mrp_log_error("Multiple pending connections, rejecting.");
+ return LWS_EVENT_DENY;
+ }
+
+ up = ctx->http;
+
+ ctx->pending = ws;
+ ctx->pending_user = NULL;
+ ctx->pending_proto = up;
+
+ wsl_ref_context(ctx);
+ up->cbs.connection(ctx, "XXX TODO dig out peer address", up->name,
+ ctx->user_data, up->proto_data);
+ sck = ctx->pending_user;
+ ctx->pending_user = NULL;
+
+ /* XXX TODO
+ * check if sockets gets properly closed and freed if
+ * cb->connection calls close on the 'listening' websocket in
+ * the transport layer...
+ */
+
+ accepted = (ctx->pending == NULL);
+ wsl_unref_context(ctx);
+
+ if (accepted)
+ goto deliver_event;
+ else
+ status = LWS_EVENT_DENY;
+ }
+
+ return status;
+
+#ifndef WEBSOCKETS_OLD
+ case LWS_CALLBACK_HTTP_FILE_COMPLETION:
+ uri = (const char *)in;
+ if (uri != NULL)
+ mrp_debug("serving '%s' over HTTP completed", uri);
+ else
+ mrp_debug("serving HTTP content completed");
+
+ sck = find_pure_http(ctx, ws);
+
+ if (sck != NULL) { /* known socket, deliver event */
+ up = sck->proto;
+
+ if (up != NULL) {
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.http_done(sck, in, sck->user_data,
+ up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ sck = find_pure_http(ctx, ws);
+
+ if (check_closed(sck))
+ return 0;
+ }
+
+ status = LWS_EVENT_OK;
+ }
+
+ return LWS_EVENT_OK;
+#endif
+
+ /*
+ * events always routed to protocols[0]
+ *
+ * XXX TODO: we need to open up for the upper layers using
+ * optionally settable wsl_ctx_t-level callbacks at least
+ *
+ * FILTER_NETWORK_CONNECTION
+ * FILTER_PROTOCOL_CONNECTION
+ * OPENSSL_*
+ *
+ * Probably for the sake of completeness we should open up
+ * all of these...
+ */
+
+ case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
+ fd = (ptrdiff_t)user;
+ /* we don't filter based on the socket/address */
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION:
+ /* we don't filter based on headers */
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS:
+ load_extra_certs(ctx, user, event);
+ return LWS_EVENT_OK;
+
+#ifdef LWS_OPENSSL_SUPPORT
+ if (ctx != NULL && ctx->load_certs != NULL)
+ ctx->load_certs(ctx, user, FALSE);
+#endif
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS:
+ load_extra_certs(ctx, user, TRUE);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION:
+ if (verify_client_cert(user, in, len))
+ return LWS_EVENT_OK;
+ else
+ return LWS_EVENT_DENY;
+
+ case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
+ /* no extra headers we'd like to add */
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
+ ext = (const char *)in;
+ /* deny all extensions on the server side */
+ mrp_debug("denying server extension '%s'", ext);
+ return LWS_EVENT_DENY;
+
+ case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
+ ext = (const char *)in;
+ /* deny all extensions on the client side */
+ mrp_debug("denying client extension '%s'", ext);
+ return LWS_EVENT_DENY;
+
+ default:
+ break;
+ }
+
+ return LWS_EVENT_DENY;
+}
+
+
+static int wsl_event(lws_ctx_t *ws_ctx, lws_t *ws, lws_event_t event,
+ void *user, void *in, size_t len)
+{
+ wsl_ctx_t *ctx = get_context_userdata(ws_ctx);
+ wsl_sck_t *sck;
+ wsl_proto_t *up;
+ void *data;
+ size_t size;
+ uint32_t total;
+ const char *ext;
+ lws_proto_t *proto;
+ int status;
+
+ MRP_UNUSED(ext);
+ MRP_UNUSED(ws_ctx);
+
+ switch (event) {
+ case LWS_CALLBACK_ESTABLISHED:
+ mrp_debug("client-handshake completed on websocket %p/%p", ws, user);
+
+ /*
+ * Connection acceptance is a bit tricky. Once libwebsockets
+ * has completed its handshaking phase with the client it lets
+ * us know about a new established connection. This is what we
+ * want to map to an incoming connection attempt. Since we don't
+ * want to know about the internals of the upper layer, neither
+ * want the upper layer to know about our internals, the only
+ * way to pass information about the connection around in the
+ * context at this point.
+ *
+ * To keep things simple we only prepare and handle once
+ * outstanding connection attemp at a time. This is equivalent
+ * to listening on a stream-socket with a backlog of 1. Since we
+ * run single-threaded it shouldn't ever be possible to have more
+ * than one pending connection if the upper layer does things
+ * right but we do check for this and reject multiple pending
+ * connections here...
+ *
+ * We store the pending websocket instance and its associated
+ * user data in the context then call the connection notifier
+ * callback. If the upper layer wants to accept the connection
+ * it calls wsl_accept_pending. That in turn digs these out from
+ * the context to set up and hook together things properly. If all
+ * goes fine wsl_accept_pending clears pending and pending_user
+ * from the context. If something fails or the upper layer decides
+ * not to accept the connection, pending and pending_user stay
+ * intact in which case we'll reject the client here once the
+ * callback returns.
+ */
+
+ if (ctx->pending != NULL) {
+ mrp_log_error("Multiple pending connections, rejecting.");
+ return LWS_EVENT_DENY;
+ }
+
+
+ proto = (lws_proto_t *)libwebsockets_get_protocol(ws);
+ up = find_context_protocol(ctx, proto->name);
+
+ if (up == NULL) {
+ mrp_debug("unknown protocol '%s' requested, rejecting",
+ proto ? proto->name : "<none>");
+ return LWS_EVENT_DENY;
+ }
+ else
+ mrp_debug("found descriptor %p for protocol '%s'", up, up->name);
+
+ ctx->pending = ws;
+ ctx->pending_user = user;
+ ctx->pending_proto = up;
+
+ wsl_ref_context(ctx);
+ up->cbs.connection(ctx, "XXX TODO dig out peer address", up->name,
+ ctx->user_data, up->proto_data);
+
+ /* XXX TODO
+ * check if sockets gets properly closed and freed if
+ * cb->connection calls close on the 'listening' websocket in
+ * the transport layer...
+ */
+
+ if (ctx->pending == NULL) /* connection accepted */
+ status = LWS_EVENT_OK;
+ else /* connection rejected */
+ status = LWS_EVENT_DENY;
+ wsl_unref_context(ctx);
+
+ return status;
+
+ case LWS_CALLBACK_CLOSED:
+ proto = (lws_proto_t *)libwebsockets_get_protocol(ws);
+ up = find_context_protocol(ctx, proto->name);
+ mrp_debug("websocket %p/%p (%s) closed", ws, user,
+ up ? up->name : "<unknown>");
+
+ sck = *(wsl_sck_t **)user;
+ up = sck ? sck->proto : NULL;
+
+ if (up != NULL) {
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.closed(sck, 0, sck->user_data, up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ check_closed(sck);
+ }
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
+ mrp_debug("server-handshake completed on websocket %p/%p", ws, user);
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
+ mrp_debug("client connection failed");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_RECEIVE:
+ case LWS_CALLBACK_CLIENT_RECEIVE:
+ mrp_debug("%zu bytes received on websocket %p/%p", len, ws, user);
+ mrp_debug("%zd remaining from this message",
+ libwebsockets_remaining_packet_payload(ws));
+
+ sck = *(wsl_sck_t **)user;
+ up = sck ? sck->proto : NULL;
+
+ if (up != NULL) {
+ if (!up->framed && !mrp_fragbuf_missing(sck->buf)) {
+ /* new packet of an unframed protocol, push message size */
+ total = len + libwebsockets_remaining_packet_payload(ws);
+ mrp_debug("unframed protocol, total message size %u", total);
+
+ total = htobe32(total);
+ mrp_fragbuf_push(sck->buf, &total, sizeof(total));
+ }
+
+ if (mrp_fragbuf_push(sck->buf, in, len)) {
+ data = NULL;
+ size = 0;
+
+ while (mrp_fragbuf_pull(sck->buf, &data, &size)) {
+ mrp_debug("websocket %p/%p has a message of %zd bytes",
+ ws, user, size);
+
+ SOCKET_BUSY_REGION(sck, {
+ up->cbs.recv(sck, data, size, sck->user_data,
+ up->proto_data);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+ });
+
+ if (check_closed(sck))
+ break;
+ }
+ }
+ else {
+ mrp_log_error("failed to push data to fragment buffer");
+
+ SOCKET_BUSY_REGION(sck, {
+ wsl_close(sck);
+#if 0 /*
+ * XXX Hmm... calling wsl_close instead of this now. Should be tested
+ * if that really works.
+ */
+ sck->closing = TRUE; /* make sure sck gets closed */
+ up->cbs.closed(sck, ENOBUFS, sck->user_data,
+ up->proto_data);
+ libwebsocket_close_and_free_session(ctx->ctx, sck->sck,
+ LWS_INTERNAL_ERROR);
+ up->cbs.check(sck, sck->user_data, up->proto_data);
+#endif
+ });
+
+ check_closed(sck);
+ return -1;
+ }
+ }
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_SERVER_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = *(wsl_sck_t **)user;
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket server side writeable again");
+ return LWS_EVENT_OK;
+
+ case LWS_CALLBACK_CLIENT_WRITEABLE:
+#ifndef WEBSOCKETS_CLOSE_SESSION
+ sck = *(wsl_sck_t **)user;
+
+ if (sck == NULL) {
+ mrp_debug("asking to close unassociated websocket %p", ws);
+
+ return LWS_EVENT_CLOSE;
+ }
+#endif
+ mrp_debug("socket client side writeable again");
+ return LWS_EVENT_OK;
+
+ default:
+ break;
+ }
+
+ return LWS_EVENT_OK;
+}
+
+
+
+/*
+ * logging
+ */
+
+#ifndef WEBSOCKETS_OLD
+
+#ifdef WEBSOCKETS_LOG_WITH_LEVEL
+static void libwebsockets(int level, const char *line)
+#else
+static void libwebsockets(const char *line)
+#endif
+{
+ const char *ts, *ll;
+ const char *b, *e, *lvl;
+ int l, ls;
+ uint32_t mask;
+
+#ifdef WEBSOCKETS_LOG_WITH_LEVEL
+ MRP_UNUSED(level);
+#else
+ /*
+ * If our (shaky) configure-time check gives a false negative,
+ * we'll expect only line but will be passed both level and line.
+ * Try catching it instead of crashing on it here...
+ */
+ if (line < (const char *)(LLL_CLIENT << 3))
+ return;
+#endif
+
+ if ((mask = mrp_log_get_mask()) == 0)
+ return;
+
+ /*
+ * Notes:
+ * libwebsockets logging infrastructure has independently maskable
+ * log classes and supports overriding its default logger. The log
+ * classes are the regular error, warning, info, and debug classes
+ * plus the libwebsockets-specific parser, header, extension, and
+ * client classes. The logging infra filters the messages based on
+ * their class, then formats the message and passes it on to the
+ * (default builtin, or externally set) logger function. This gets
+ * a fully formatted log message that consists of a timestamp, a
+ * log class prefix and the message itself which typically contains
+ * at least one terminating newline.
+ *
+ * Because of the semantic content of the messages coming from
+ * libwebsockets we'd like to preserve the class of errors and
+ * warnings but convert the rest to debug messages. Additionally,
+ * we'd like to keep the message format as consistent with the
+ * murphy infra as possible with a reasonable effort. This means
+ * stripping the timestamp and log class, as these are provided
+ * by the murphy infra (if configured so). However, for the
+ * libwebsockets-specific parser-, header-, extension-, and client-
+ * classes we want to keep the extra information carried by the
+ * log class as part of the message.
+ *
+ * Because the libwebsockets log messages are terminated by '\n',
+ * we also prepare here to properly bridge multiline messages to
+ * the murphy infra (although I'm not sure the library ever issues
+ * such messages).
+ *
+ * So to sum it up the necessary steps to bridge messages here are:
+ * 1) strip timestamp,
+ * 2) dig out and strip log class
+ * 3) map log class to murphy infra, ie.
+ * keep errors and warnings, squash the rest to debug
+ * 4) break multiline messages to lines
+ * 5) pass each line on to the murphy infra,
+ * for parser-, header-, extension-, and client-messages
+ * prefix each line with the class
+ *
+ */
+
+ lvl = "???";
+ ls = 3;
+
+ ts = strchr(line, '[');
+ ll = ts != NULL ? strchr(ts, ']') : NULL;
+
+ /* strip timestamp, dig out log level, find beginning of the message */
+ if (ll != NULL && ll[1] == ' ') {
+ ll += 2;
+ b = strchr(ll, ':');
+
+ if (b != NULL && b[1] == ' ') {
+ b += 2;
+
+ while (*b == ' ')
+ b++;
+
+ /* map log level: debug, info, err, warn, or other */
+ switch (*ll) {
+ case 'D':
+ if (!(mask & MRP_LOG_MASK_DEBUG))
+ return;
+ lvl = "d";
+ break;
+ case 'I':
+ if (!(mask & MRP_LOG_MASK_INFO))
+ return;
+ lvl = "i";
+ break;
+ case 'W':
+ if (!(mask & MRP_LOG_MASK_WARNING))
+ return;
+ lvl = "w";
+ break;
+ case 'E':
+ if (ll[1] == 'R') {
+ if (!(mask & MRP_LOG_MASK_ERROR))
+ return;
+ lvl = "e";
+ }
+ else {
+ other:
+ if (!(mask & MRP_LOG_MASK_DEBUG))
+ return;
+ lvl = ll;
+ e = strchr(lvl, ':');
+
+ if (e != NULL)
+ ls = e - lvl;
+ else {
+ lvl = "???:";
+ ls = 4;
+ }
+ }
+ break;
+
+ default:
+ goto other;
+ }
+ }
+ else
+ goto unknown;
+ }
+ else {
+ unknown:
+ /* if we get confused with the format, default to logging it all */
+ lvl = NULL;
+ b = line;
+ }
+
+#ifdef WEBSOCKETS_LOG_WITH_LEVEL
+ switch (level) {
+ case LLL_ERR: lvl = "e"; ls = 0; break;
+ case LLL_WARN: lvl = "w"; ls = 0; break;
+ case LLL_INFO: lvl = "i"; ls = 0; break;
+ case LLL_DEBUG: lvl = "d"; ls = 0; break;
+ case LLL_NOTICE: lvl = "d"; ls = 0; break;
+ case LLL_PARSER: lvl = "parser" ; ls = 6; break;
+ case LLL_HEADER: lvl = "header" ; ls = 6; break;
+ case LLL_EXT: lvl = "ext" ; ls = 3; break;
+ case LLL_CLIENT: lvl = "client" ; ls = 6; break;
+ case LLL_LATENCY: lvl = "latency"; ls = 7; break;
+ default: lvl = "???" ; ls = 3; break;
+ }
+
+ b = line;
+ while (*b == ' ' || *b == '\t')
+ b++;
+#endif
+
+ /* break the message to lines and pass it on to the murphy infra */
+ e = strchr(b, '\n');
+ while (e || b) {
+ if (e)
+ l = e - b;
+ else
+ l = strlen(b);
+
+ if (!l)
+ break;
+
+ switch (lvl[0] | (lvl[1] << 8)) {
+ case 'd': mrp_debug("%*.*s", l, l, b); break;
+ case 'i': mrp_debug("%*.*s", l, l, b); break;
+ case 'w': mrp_log_warning("libwebsockets: %*.*s", l, l, b); break;
+ case 'e': mrp_log_error("libwebsockets: %*.*s", l, l, b); break;
+ default: mrp_debug("[%*.*s] %*.*s", ls, ls, lvl, l, l, b);
+ }
+
+ if (e != NULL) {
+ b = e + 1;
+ e = strchr(b, '\n');
+ }
+ else
+ b = NULL;
+ }
+}
+
+
+void wsl_set_loglevel(wsl_loglevel_t mask)
+{
+ lws_set_log_level(mask, libwebsockets);
+}
+
+
+#else /* WEBSOCKETS_OLD */
+
+void wsl_set_loglevel(wsl_loglevel_t mask)
+{
+ MRP_UNUSED(mask);
+
+ mrp_log_warning("libwebsockets too old to redirect logs...");
+}
+
+#endif /* WEBSOCKETS_OLD */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_WEBSOCKLIB_H__
+#define __MURPHY_WEBSOCKLIB_H__
+
+#include <sys/socket.h>
+
+#include <libwebsockets.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+
+MRP_CDECL_BEGIN
+
+/*
+ * websocket context
+ *
+ * A websocket context is basically a libwebsocket_context plus the
+ * additional glue data and code necessary to integrate the context
+ * into our mainloop. For our transport abstraction, we create one
+ * context per transport instance. However, accepted transports do
+ * share their context with the listening transport (ie. the server-
+ * side libwebsocket) they were accepted on.
+ *
+ * XXX TODO We probably need to change this so that we create one
+ * context per address/port (or in libwebsockets case device/port).
+ *
+ */
+
+typedef struct wsl_ctx_s wsl_ctx_t;
+
+
+/*
+ * websocket
+ *
+ * A websocket is a libwebsocket instance together with its
+ * associated websocket context.
+ */
+typedef struct wsl_sck_s wsl_sck_t;
+
+
+/*
+ * websocket event callbacks to the upper transport layer
+ *
+ * These callbacks are used to deliver events from the underlying
+ * websocket transport layer to the upper murphy transport layer.
+ */
+typedef struct {
+ /** Connection attempt on a websocket. */
+ void (*connection)(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data);
+ /** Websocket connection closed by peer. */
+ void (*closed)(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data);
+ /** Data received on websocket. */
+ void (*recv)(wsl_sck_t *sck, void *data, size_t size, void *user_data,
+ void *proto_data);
+ /** Check if transport should be destroyed. */
+ int (*check)(wsl_sck_t *sck, void *user_data, void *proto_data);
+
+ /** HTTP (content) request completed. */
+ void (*http_done)(wsl_sck_t *sck, const char *uri, void *user_data,
+ void *proto_data);
+
+#ifdef LWS_OPENSSL_SUPPORT
+ /** Load extra client or server certificates, if necessary. */
+ void (*load_certs)(wsl_ctx_t *ctx, SSL_CTX *ssl, int is_server);
+#else
+ void (*load_certs)(wsl_ctx_t *, void *, int);
+#endif
+} wsl_callbacks_t;
+
+
+/*
+ * websocket protocol
+ *
+ * A websocket protocol is a protocol name together with protocol-specific
+ * upper-layer callbacks.
+ */
+typedef struct {
+ const char *name; /* protocol name */
+ wsl_callbacks_t cbs; /* event/request callbacks */
+ int framed; /* whether a framed protocol */
+ void *proto_data; /* protocol-specific user data */
+} wsl_proto_t;
+
+
+/*
+ * websocket write modes
+ */
+
+typedef enum {
+ WSL_SEND_TEXT = LWS_WRITE_TEXT, /* text mode */
+ WSL_SEND_BINARY = LWS_WRITE_BINARY, /* binary/blob mode */
+#if 0
+ WSL_SEND_HTTP = LWS_WRITE_HTTP /* HTTP mode */
+#endif
+
+#define WSL_SEND_TEXT WSL_SEND_TEXT
+
+} wsl_sendmode_t;
+
+
+/*
+ * logging levels
+ */
+
+#ifndef WEBSOCKETS_OLD
+
+typedef enum {
+ WSL_LOG_NONE = 0x0,
+ WSL_LOG_ERROR = LLL_ERR,
+ WSL_LOG_WARNING = LLL_WARN,
+ WSL_LOG_INFO = LLL_INFO,
+ WSL_LOG_DEBUG = LLL_DEBUG,
+ WSL_LOG_ALL = LLL_ERR | LLL_WARN | LLL_INFO | LLL_DEBUG,
+ WSL_LOG_PARSER = LLL_PARSER,
+ WSL_LOG_HEADER = LLL_HEADER,
+ WSL_LOG_EXT = LLL_EXT,
+ WSL_LOG_CLIENT = LLL_CLIENT,
+ WSL_LOG_EXTRA = LLL_PARSER | LLL_HEADER | LLL_EXT | LLL_CLIENT,
+ WSL_LOG_VERBOSE = WSL_LOG_ALL | WSL_LOG_EXTRA
+} wsl_loglevel_t;
+
+#else /* !WEBSOCKETS_OLD */
+
+typedef enum {
+ WSL_LOG_NONE = 0x0,
+ WSL_LOG_ERROR = 0x0,
+ WSL_LOG_WARNING = 0x0,
+ WSL_LOG_INFO = 0x0,
+ WSL_LOG_DEBUG = 0x0,
+ WSL_LOG_ALL = 0x0,
+ WSL_LOG_PARSER = 0x0,
+ WSL_LOG_HEADER = 0x0,
+ WSL_LOG_EXT = 0x0,
+ WSL_LOG_CLIENT = 0x0,
+ WSL_LOG_EXTRA = 0x0,
+ WSL_LOG_VERBOSE = 0x0,
+} wsl_loglevel_t;
+
+#endif /* !WEBSOCKETS_OLD */
+
+typedef enum {
+ WSL_NO_SSL = 0, /* plain connection, no SSL */
+ WSL_SSL = 1, /* SSL, deny self-signed certs */
+ WSL_SSL_SELFSIGNED = 2, /* SSL, allow self-signed certs */
+} wsl_ssl_t;
+
+
+/*
+ * websockets context configuration
+ */
+
+#define WSL_NO_GID -1
+#define WSL_NO_UID -1
+
+typedef struct {
+ struct sockaddr *addr; /* address/port to listen on */
+ wsl_proto_t *protos; /* protocols to serve */
+ int nproto; /* number of protocols */
+ const char *ssl_cert; /* SSL certificate path */
+ const char *ssl_pkey; /* SSL private key path */
+ const char *ssl_ca; /* SSL CA path */
+ const char *ssl_ciphers; /* SSL cipher list */
+ int gid; /* group ID to change to, or -1 */
+ int uid; /* user ID to change to, or -1 */
+ void *user_data; /* opaque user data */
+ int timeout; /* keepalive timeout */
+ int nprobe; /* number of keepalive probes */
+ int interval; /* keepalive probe interval */
+} wsl_ctx_cfg_t;
+
+
+/** Set libwebsock logging level _and_ redirect to murphy logging infra. */
+void wsl_set_loglevel(wsl_loglevel_t mask);
+
+/** Create a websocket context. */
+wsl_ctx_t *wsl_create_context(mrp_mainloop_t *ml, wsl_ctx_cfg_t *cfg);
+
+/** Add a reference to a context. */
+wsl_ctx_t *wsl_ref_context(wsl_ctx_t *ctx);
+
+/** Remove a context reference, destroying it once the last is gone. */
+int wsl_unref_context(wsl_ctx_t *ctx);
+
+/** Create a new websocket connection using a given protocol. */
+wsl_sck_t *wsl_connect(wsl_ctx_t *ctx, struct sockaddr *sa,
+ const char *protocol, wsl_ssl_t ssl, void *user_data);
+
+/** Accept a pending connection. */
+wsl_sck_t *wsl_accept_pending(wsl_ctx_t *ctx, void *user_data);
+
+/** Reject a pending connection. */
+void wsl_reject_pending(wsl_ctx_t *ctx);
+
+/** Close a websocket connection. Return user_data of the associated context. */
+void *wsl_close(wsl_sck_t *sck);
+
+/** Set websocket write mode (binary or text). */
+int wsl_set_sendmode(wsl_sck_t *sck, wsl_sendmode_t mode);
+
+/** Send data over a wbesocket. */
+int wsl_send(wsl_sck_t *sck, void *payload, size_t size);
+
+/** Serve the given file over the given socket. */
+int wsl_serve_http_file(wsl_sck_t *sck, const char *path, const char *mime);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_WEBSOCKLIB_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <arpa/inet.h>
+
+#include <libwebsockets.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/json.h>
+
+#include "websocklib.h"
+#include "wsck-transport.h"
+
+#define WSCKP "wsck" /* websocket transport prefix */
+#define WSCKL 4 /* websocket transport prefix length */
+
+
+/*
+ * a websocket transport instance
+ */
+
+typedef struct {
+ MRP_TRANSPORT_PUBLIC_FIELDS; /* common transport fields */
+ wsl_ctx_t *ctx; /* websocket context */
+ wsl_sck_t *sck; /* websocket instance */
+ int send_mode; /* websocket send mode */
+ const char *http_root; /* HTTP content root */
+ mrp_wsck_urimap_t *uri_table; /* URI-to-path table */
+ mrp_wsck_mimemap_t *mime_table; /* suffix to MIME-type table */
+ const char *ssl_cert; /* path to SSL certificate */
+ const char *ssl_pkey; /* path to SSL private key */
+ const char *ssl_ca; /* path to SSL CA */
+ wsl_ssl_t ssl; /* SSL mode (wsl_ssl_t) */
+ char *protocol; /* websocket protocol name */
+ wsl_proto_t proto[2]; /* protocol setup */
+ mrp_list_hook_t http_clients; /* pure HTTP clients */
+} wsck_t;
+
+
+/*
+ * a pure HTTP client instance
+ */
+
+typedef struct {
+ wsl_sck_t *sck; /* websocket towards client */
+ mrp_list_hook_t hook; /* hook to listening socket */
+ const char *http_root; /* HTTP content root */
+ mrp_wsck_urimap_t *uri_table; /* URI to path mapping */
+ mrp_wsck_mimemap_t *mime_table; /* suffix to MIME type mapping */
+} http_client_t;
+
+
+/*
+ * default file suffix to MIME type mapping table
+ */
+
+static mrp_wsck_mimemap_t mime_table[] = {
+ { "js" , "application/javascript" },
+ { "html", "text/html" },
+ { "htm ", "text/html" },
+ { "txt" , "text/plain" },
+ { NULL, NULL }
+};
+
+
+static int resolve_address(const char *str, mrp_wsckaddr_t *wa, socklen_t alen);
+
+static void connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data);
+static void closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data);
+static void recv_cb(wsl_sck_t *sck, void *data, size_t size, void *user_data,
+ void *proto_data);
+static int check_cb(wsl_sck_t *sck, void *user_data, void *proto_data);
+
+static void http_connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data);
+static void http_closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data);
+static void http_req_cb(wsl_sck_t *sck, void *data, size_t size,
+ void *user_data, void *proto_data);
+static int http_check_cb(wsl_sck_t *sck, void *user_data, void *proto_data);
+static void http_done_cb(wsl_sck_t *sck, const char *uri, void *user_data,
+ void *proto_data);
+
+static socklen_t wsck_resolve(const char *str, mrp_sockaddr_t *addr,
+ socklen_t size, const char **typep)
+{
+ mrp_wsckaddr_t *wa = (mrp_wsckaddr_t *)addr;
+ socklen_t len;
+
+ len = resolve_address(str, wa, size);
+
+ if (len <= 0)
+ return 0;
+ else {
+ if (typep != NULL)
+ *typep = WSCKP;
+
+ return len;
+ }
+}
+
+
+static int wsck_open(mrp_transport_t *mt)
+{
+ wsck_t *t = (wsck_t *)mt;
+
+ mrp_list_init(&t->http_clients);
+ wsl_set_loglevel(WSL_LOG_ALL/* | WSL_LOG_EXTRA*/);
+
+ return TRUE;
+}
+
+
+static int wsck_createfrom(mrp_transport_t *mt, void *conn)
+{
+ wsck_t *t = (wsck_t *)mt;
+
+ MRP_UNUSED(conn);
+
+ mrp_list_init(&t->http_clients);
+
+ return FALSE;
+}
+
+
+static void wsck_close(mrp_transport_t *mt)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_ctx_t *ctx = t->ctx;
+ wsl_sck_t *sck = t->sck;
+ void *user_data;
+
+ t->sck = NULL;
+ t->ctx = NULL;
+ mrp_free(t->protocol);
+ t->protocol = NULL;
+
+ user_data = wsl_close(sck);
+
+ if (user_data == t) /* was our associated context */
+ wsl_unref_context(ctx);
+}
+
+
+static int wsck_setopt(mrp_transport_t *mt, const char *opt, const void *val)
+{
+ wsck_t *t = (wsck_t *)mt;
+ int success;
+
+ if (!strcmp(opt, MRP_WSCK_OPT_SENDMODE) && val != NULL) {
+ if (!strcmp(val, "binary"))
+ t->send_mode = WSL_SEND_BINARY;
+ else if (!strcmp(val, "text"))
+ t->send_mode = WSL_SEND_TEXT;
+ else
+ return FALSE;
+
+ if (t->sck != NULL)
+ return wsl_set_sendmode(t->sck, t->send_mode);
+ else
+ return TRUE;
+ }
+
+ success = TRUE;
+
+ if (!strcmp(opt, MRP_WSCK_OPT_HTTPDIR))
+ t->http_root = val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_MIMEMAP))
+ t->mime_table = (void *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_URIMAP))
+ t->uri_table = (void *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL_CERT))
+ t->ssl_cert = (const char *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL_PKEY))
+ t->ssl_pkey = (const char *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL_CA))
+ t->ssl_ca = (const char *)val;
+ else if (!strcmp(opt, MRP_WSCK_OPT_SSL))
+ t->ssl = *(wsl_ssl_t *)val;
+ else
+ success = FALSE;
+
+ return success;
+}
+
+
+static int wsck_bind(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_proto_t proto[] = {
+ {
+ .name = "http",
+ .cbs = { .connection = http_connection_cb,
+ .closed = http_closed_cb,
+ .recv = http_req_cb,
+ .check = http_check_cb,
+ .http_done = http_done_cb,
+ .load_certs = NULL, },
+ .framed = FALSE,
+ .proto_data = NULL
+ },
+ {
+ .name = "murphy",
+ .cbs = { .connection = connection_cb,
+ .closed = closed_cb,
+ .recv = recv_cb,
+ .check = check_cb,
+ .http_done = NULL,
+ .load_certs = NULL, },
+ .framed = FALSE,
+ .proto_data = NULL
+ }
+ };
+ wsl_ctx_cfg_t cfg;
+ mrp_wsckaddr_t *wa;
+ struct sockaddr *sa;
+
+ if (addr->any.sa_family != MRP_AF_WSCK || addrlen != sizeof(*wa))
+ return FALSE;
+
+ if (t->ctx != NULL)
+ return FALSE;
+
+ wa = (mrp_wsckaddr_t *)addr;
+
+ switch (wa->wsck_addr.family) {
+ case AF_INET: sa = (struct sockaddr *)&wa->wsck_addr.v4; break;
+ case AF_INET6: sa = (struct sockaddr *)&wa->wsck_addr.v6; break;
+ default:
+ errno = EAFNOSUPPORT;
+ return FALSE;
+ }
+
+ if ((t->protocol = mrp_strdup(wa->wsck_proto)) == NULL)
+ return FALSE;
+
+ t->proto[0] = proto[0];
+ t->proto[1] = proto[1];
+
+ t->proto[1].name = t->protocol;
+
+ mrp_clear(&cfg);
+ cfg.addr = sa;
+ cfg.protos = &t->proto[0];
+ cfg.nproto = MRP_ARRAY_SIZE(t->proto);
+ cfg.ssl_cert = t->ssl_cert;
+ cfg.ssl_pkey = t->ssl_pkey;
+ cfg.ssl_ca = t->ssl_ca;
+ cfg.gid = WSL_NO_GID;
+ cfg.uid = WSL_NO_UID;
+ cfg.user_data = t;
+
+ t->ctx = wsl_create_context(t->ml, &cfg);
+
+ if (t->ctx != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int wsck_listen(mrp_transport_t *mt, int backlog)
+{
+ MRP_UNUSED(mt);
+ MRP_UNUSED(backlog);
+
+ mt->listened = TRUE;
+
+ return TRUE;
+}
+
+
+static int wsck_accept(mrp_transport_t *mt, mrp_transport_t *mlt)
+{
+ wsck_t *lt = (wsck_t *)mlt;
+ wsck_t *t = (wsck_t *)mt;
+
+ t->sck = wsl_accept_pending(lt->ctx, t);
+
+ if (t->sck != NULL) {
+ mrp_debug("accepted websocket connection %p", mlt);
+
+ /* default to mode inherited from listening transport */
+ t->send_mode = lt->send_mode;
+ wsl_set_sendmode(t->sck, t->send_mode);
+
+ /* inherit pure HTTP settings by default */
+ t->http_root = lt->http_root;
+ t->uri_table = lt->uri_table;
+ t->mime_table = lt->mime_table;
+
+ return TRUE;
+ }
+ else {
+ mrp_debug("failed to accept websocket connection on %p", mlt);
+
+ return FALSE;
+ }
+}
+
+
+static int wsck_connect(mrp_transport_t *mt, mrp_sockaddr_t *addr,
+ socklen_t addrlen)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_proto_t proto = {
+ .name = "murphy",
+ .cbs = { .connection = connection_cb,
+ .closed = closed_cb,
+ .recv = recv_cb,
+ .check = check_cb, },
+ .framed = FALSE,
+ .proto_data = NULL
+ };
+
+ wsl_ctx_cfg_t cfg;
+ mrp_wsckaddr_t *wa;
+ struct sockaddr *sa;
+ if (addr->any.sa_family != MRP_AF_WSCK || addrlen != sizeof(*wa))
+ return FALSE;
+
+ if (t->ctx != NULL)
+ return FALSE;
+
+ wa = (mrp_wsckaddr_t *)addr;
+
+ switch (wa->wsck_addr.family) {
+ case AF_INET: sa = (struct sockaddr *)&wa->wsck_addr.v4; break;
+ case AF_INET6: sa = (struct sockaddr *)&wa->wsck_addr.v6; break;
+ default:
+ errno = EAFNOSUPPORT;
+ return FALSE;
+ }
+
+ if ((t->protocol = mrp_strdup(wa->wsck_proto)) == NULL)
+ return FALSE;
+
+ proto.name = t->protocol;
+ t->proto[0] = proto;
+
+ mrp_clear(&cfg);
+ cfg.addr = NULL;
+ cfg.protos = &t->proto[0];
+ cfg.nproto = 1;
+ cfg.ssl_cert = t->ssl_cert;
+ cfg.ssl_pkey = t->ssl_pkey;
+ cfg.ssl_ca = t->ssl_ca;
+ cfg.gid = WSL_NO_GID;
+ cfg.uid = WSL_NO_UID;
+ cfg.user_data = t;
+
+ t->ctx = wsl_create_context(t->ml, &cfg);
+
+ if (t->ctx == NULL)
+ return FALSE;
+
+ t->sck = wsl_connect(t->ctx, sa, t->protocol, t->ssl, t);
+
+ if (t->sck != NULL) {
+ t->connected = TRUE;
+
+ return TRUE;
+ }
+ else {
+ wsl_unref_context(t->ctx);
+ t->ctx = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static int wsck_disconnect(mrp_transport_t *mt)
+{
+ wsck_t *t = (wsck_t *)mt;
+ wsl_ctx_t *ctx = t->ctx;
+ wsl_sck_t *sck = t->sck;
+ void *user_data;
+
+ t->sck = NULL;
+ t->ctx = NULL;
+
+ user_data = wsl_close(sck);
+
+ if (user_data == t) /* was our associated context */
+ wsl_unref_context(ctx);
+
+ return TRUE;
+}
+
+
+static int wsck_send(mrp_transport_t *mt, mrp_msg_t *msg)
+{
+ wsck_t *t = (wsck_t *)mt;
+ void *buf;
+ ssize_t size;
+ int success;
+
+ size = mrp_msg_default_encode(msg, &buf);
+
+ if (wsl_send(t->sck, buf, size))
+ success = TRUE;
+ else
+ success = FALSE;
+
+ mrp_free(buf);
+
+ return success;
+}
+
+
+static int wsck_sendraw(mrp_transport_t *mt, void *data, size_t size)
+{
+ wsck_t *t = (wsck_t *)mt;
+
+ return wsl_send(t->sck, data, size);
+}
+
+
+static int wsck_senddata(mrp_transport_t *mt, void *data, uint16_t tag)
+{
+ wsck_t *t = (wsck_t *)mt;
+ mrp_data_descr_t *type;
+ void *buf;
+ size_t size, reserve;
+ uint16_t *tagp;
+ int status;
+
+ type = mrp_msg_find_type(tag);
+
+ if (type != NULL) {
+ reserve = sizeof(*tagp);
+ size = mrp_data_encode(&buf, data, type, reserve);
+
+ if (size > 0) {
+ tagp = buf;
+ *tagp = htobe16(tag);
+
+ status = wsl_send(t->sck, buf, size);
+
+ mrp_free(buf);
+ return status;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int wsck_sendcustom(mrp_transport_t *mt, void *data)
+{
+ wsck_t *t = (wsck_t *)mt;
+ mrp_json_t *json = (mrp_json_t *)data;
+ const char *s;
+ int status;
+
+ s = mrp_json_object_to_string(json);
+
+ /*
+ * Notes:
+ * Although json-c internally counts the length of the serialized
+ * object, it does not provide an API to get it out together with
+ * the string. Great...
+ */
+
+ if (s != NULL)
+ status = wsl_send(t->sck, (void *)s, strlen(s));
+ else
+ status = FALSE;
+
+ return status;
+}
+
+
+static inline int looks_ipv4(const char *p)
+{
+ if (isdigit(p[0])) {
+ if (p[1] == '.')
+ return TRUE;
+
+ if (isdigit(p[1])) {
+ if (p[2] == '.')
+ return TRUE;
+
+ if (isdigit(p[2])) {
+ if (p[3] == '.')
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int resolve_address(const char *str, mrp_wsckaddr_t *wa, socklen_t alen)
+{
+ struct addrinfo *ai, hints;
+ const char *node, *port, *proto;
+ char nbuf[256], pbuf[32];
+ int family, status;
+ size_t len;
+
+ if (strncmp(str, WSCKP":", WSCKL + 1) != 0)
+ return 0;
+ else
+ str += WSCKL + 1;
+
+ node = (char *)str;
+
+ if (node[0] == '[') {
+ node++;
+ family = AF_INET6;
+ port = strchr(node, ']');
+ }
+ else if (looks_ipv4(node)) {
+ family = AF_INET;
+ port = strchr(node, ':');
+ }
+ else {
+ family = AF_UNSPEC;
+ port = strrchr(node, ':');
+ }
+
+ if (port == NULL || (*port != ':' && *port != ']')) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ len = port - node;
+
+ if (len > sizeof(nbuf) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ strncpy(nbuf, node, len);
+ nbuf[len] = '\0';
+
+ if (*port == ']')
+ port++;
+
+ if (*port != ':') {
+ errno = EINVAL;
+ return -1;
+ }
+
+ port++;
+ proto = strchr(port, '/');
+
+ if (proto != NULL) {
+ len = proto - port;
+
+ if (len > sizeof(pbuf) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ strncpy(pbuf, port, len);
+ pbuf[len] = '\0';
+
+ proto++;
+ if (strlen(proto) > sizeof(wa->wsck_proto) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+ }
+ else {
+ proto = MRP_WSCK_DEFPROTO;
+ len = strlen(port);
+
+ if (len > sizeof(pbuf) - 1) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ strcpy(pbuf, port);
+ }
+
+ mrp_clear(&hints);
+ hints.ai_family = family;
+
+ status = getaddrinfo(nbuf, pbuf, &hints, &ai);
+
+ switch (status) {
+ case 0:
+ if (ai->ai_addrlen <= alen) {
+ wa->wsck_family = MRP_AF_WSCK;
+ memcpy(&wa->wsck_addr, ai->ai_addr, ai->ai_addrlen);
+ strcpy(wa->wsck_proto, proto);
+
+ len = sizeof(*wa);
+ }
+ else {
+ errno = EOVERFLOW;
+ len = -1;
+ }
+
+ freeaddrinfo(ai);
+ return len;
+
+#define MAP_ERROR(ai_err, err) \
+ case EAI_##ai_err: \
+ errno = err; \
+ return -1
+
+ MAP_ERROR(AGAIN , EAGAIN);
+ MAP_ERROR(BADFLAGS , EADDRNOTAVAIL);
+ MAP_ERROR(FAIL , EHOSTUNREACH);
+ MAP_ERROR(FAMILY , EPFNOSUPPORT);
+ MAP_ERROR(MEMORY , ENOMEM);
+ MAP_ERROR(NONAME , EHOSTUNREACH);
+ MAP_ERROR(SERVICE , EAFNOSUPPORT);
+ MAP_ERROR(SOCKTYPE , EHOSTUNREACH);
+ MAP_ERROR(SYSTEM , EHOSTUNREACH);
+#ifdef EAI_ADDRFAMILY
+ MAP_ERROR(ADDRFAMILY, EHOSTUNREACH);
+#endif
+#ifdef EAI_NODATA
+ MAP_ERROR(NODATA , EHOSTUNREACH);
+#endif
+
+ default:
+ errno = EHOSTUNREACH;
+ }
+
+ return -1;
+}
+
+
+#if 0
+static int print_address(char *buf, size_t size, mrp_wsckaddr_t *wa)
+{
+ struct sockaddr *saddr;
+ socklen_t salen;
+ char nbuf[256], pbuf[32], *b, *e;
+ int status;
+
+ if (wa->wsck_family != MRP_AF_WSCK) {
+ invalid:
+ errno = EINVAL;
+ return -1;
+ }
+
+ switch (wa->wsck_addr.family) {
+ case AF_INET:
+ saddr = (struct sockaddr *)&wa->wsck_addr.v4;
+ salen = sizeof(wa->wsck_addr.v4);
+ b = "";
+ e = "";
+ break;
+ case AF_INET6:
+ saddr = (struct sockaddr *)&wa->wsck_addr.v6;
+ salen = sizeof(wa->wsck_addr.v6);
+ b = "[";
+ e = "]";
+ break;
+ default:
+ goto invalid;
+ }
+
+ status = getnameinfo(saddr, salen, nbuf, sizeof(nbuf), pbuf, sizeof(pbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV);
+
+ if (status == 0)
+ return snprintf(buf, size, "wsck:%s%s%s:%s/%s",
+ b, nbuf, e, pbuf, wa->wsck_proto);
+ else {
+ printf("error: %d: %s\n", status, gai_strerror(status));
+
+ errno = EINVAL;
+ return -1;
+ }
+}
+#endif
+
+static void connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(addr);
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("incoming connection (%s) for context %p", protocol, ctx);
+
+ if (t->listened) {
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.connection((mrp_transport_t *)t, t->user_data);
+ });
+ }
+ else
+ mrp_log_error("connection attempt on non-listened transport %p", t);
+}
+
+
+static void closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("websocket %p closed", sck);
+
+ if (t->evt.closed != NULL)
+ MRP_TRANSPORT_BUSY(t, {
+ t->evt.closed((mrp_transport_t *)t, error, t->user_data);
+ });
+}
+
+
+static void recv_cb(wsl_sck_t *sck, void *data, size_t size, void *user_data,
+ void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("%zu bytes on websocket %p", size, sck);
+
+ MRP_TRANSPORT_BUSY(t, {
+ if (t->mode != MRP_TRANSPORT_MODE_CUSTOM)
+ t->recv_data((mrp_transport_t *)t, data, size, NULL, 0);
+ else {
+ mrp_json_t *json = mrp_json_string_to_object(data, size);
+
+ if (json != NULL) {
+ t->recv_data((mrp_transport_t *)t, json, 0, NULL, 0);
+ mrp_json_unref(json);
+ }
+ }
+ });
+}
+
+
+static int check_cb(wsl_sck_t *sck, void *user_data, void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("checking if transport %p (%p) has been destroyed", t, sck);
+
+ if (t != NULL) {
+ if (t->check_destroy((mrp_transport_t *)t)) {
+ mrp_debug("transport has been destroyed");
+ return TRUE;
+ }
+ else
+ mrp_debug("transport has not been destroyed");
+ }
+
+ return FALSE;
+}
+
+
+static http_client_t *http_create_client(wsck_t *lt)
+{
+ http_client_t *c;
+
+ c = mrp_allocz(sizeof(*c));
+
+ if (c != NULL) {
+ mrp_list_init(&c->hook);
+ c->sck = wsl_accept_pending(lt->ctx, c);
+
+ if (c->sck != NULL) {
+ c->http_root = lt->http_root;
+ c->uri_table = lt->uri_table;
+ c->mime_table = lt->mime_table;
+
+ return c;
+ }
+ else {
+ mrp_free(c);
+ c = NULL;
+ }
+ }
+
+ return c;
+}
+
+
+static void http_destroy_client(http_client_t *c)
+{
+ if (c != NULL) {
+ mrp_list_delete(&c->hook);
+ wsl_close(c->sck);
+ mrp_free(c);
+ }
+}
+
+
+const char *http_mapuri(http_client_t *c, const char *uri,
+ char *buf, size_t size)
+{
+ mrp_wsck_urimap_t *um;
+ mrp_wsck_mimemap_t *mm;
+ const char *suff, *root, *r, *s;
+
+ root = c->http_root ? c->http_root : "/";
+
+ if (c->uri_table != NULL) {
+ for (um = c->uri_table; um->uri != NULL; um++) {
+ if (!strcmp(uri, um->uri)) {
+ if (um->path[0] != '/') {
+ r = root;
+ s = "/";
+ }
+ else {
+ r = "";
+ s = "";
+ }
+
+ if (snprintf(buf, size, "%s%s%s", r, s, um->path) < (int)size)
+ return um->type;
+ else
+ return NULL;
+ }
+ }
+ }
+
+ if (c->http_root != NULL) {
+ if (snprintf(buf, size, "%s/%s", root, uri) >= (int)size)
+ return NULL;
+
+ suff = strrchr(uri, '.');
+
+ if (suff == NULL)
+ return "text/plain";
+ else
+ suff++;
+
+ if (c->mime_table != NULL) {
+ for (mm = c->mime_table; mm->suffix != NULL; mm++) {
+ if (!strcmp(mm->suffix, suff))
+ return mm->type;
+ }
+ }
+
+ for (mm = mime_table; mm->suffix != NULL; mm++) {
+ if (!strcmp(mm->suffix, suff))
+ return mm->type;
+ }
+ }
+
+ return NULL;
+}
+
+
+static void http_connection_cb(wsl_ctx_t *ctx, char *addr, const char *protocol,
+ void *user_data, void *proto_data)
+{
+ wsck_t *t = (wsck_t *)user_data;
+ http_client_t *c;
+
+ MRP_UNUSED(addr);
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("incoming %s connection for context %p", protocol, ctx);
+
+ if (t->http_root != NULL || t->uri_table != NULL) {
+ c = http_create_client(t);
+
+ if (c != NULL)
+ mrp_debug("accepted pure HTTP client for context %p", ctx);
+ else
+ mrp_log_error("failed to create new HTTP client");
+ }
+ else
+ mrp_debug("rejecting pure HTTP client for context %p", ctx);
+}
+
+
+static void http_closed_cb(wsl_sck_t *sck, int error, void *user_data,
+ void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+ MRP_UNUSED(error);
+
+ if (error)
+ mrp_debug("HTTP client socket %p closed with error %d", sck, error);
+ else
+ mrp_debug("HTTP client socket %p closed", sck);
+
+ http_destroy_client(c);
+}
+
+
+static void http_req_cb(wsl_sck_t *sck, void *data, size_t size,
+ void *user_data, void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+ const char *uri = (const char *)data;
+ const char *type;
+ char path[PATH_MAX];
+
+ MRP_UNUSED(size);
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("HTTP request for URI '%s' on socket %p", uri, c->sck);
+
+ type = http_mapuri(c, uri, path, sizeof(path));
+
+ if (type != NULL) {
+ mrp_debug("mapped to '%s' (%s)", path, type);
+ wsl_serve_http_file(sck, path, type);
+ }
+ else
+ mrp_debug("failed to map URI");
+}
+
+
+static int http_check_cb(wsl_sck_t *sck, void *user_data, void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(sck);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(proto_data);
+
+ return FALSE;
+}
+
+
+static void http_done_cb(wsl_sck_t *sck, const char *uri, void *user_data,
+ void *proto_data)
+{
+ http_client_t *c = (http_client_t *)user_data;
+
+ MRP_UNUSED(proto_data);
+
+ mrp_debug("HTTP request for '%s' done, closing socket %p.", uri, sck);
+
+ http_destroy_client(c);
+}
+
+
+MRP_REGISTER_TRANSPORT(wsck, WSCKP, wsck_t, wsck_resolve,
+ wsck_open, wsck_createfrom, wsck_close, wsck_setopt,
+ wsck_bind, wsck_listen, wsck_accept,
+ wsck_connect, wsck_disconnect,
+ wsck_send, NULL,
+ wsck_sendraw, NULL,
+ wsck_senddata, NULL,
+ wsck_sendcustom, NULL,
+ NULL, NULL,
+ NULL, NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_WEBSOCKET_TRANSPORT_H__
+#define __MURPHY_WEBSOCKET_TRANSPORT_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/transport.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_AF_WSCK 0xDC /* stolen address family */
+
+
+/*
+ * websocket transport address
+ */
+
+#define MRP_WSCKADDR_BASE \
+ __SOCKADDR_COMMON(wsck_); /* wsck_family: MRP_AF_WSCK */ \
+ union { /* websocket address */ \
+ sa_family_t family; \
+ struct sockaddr_in v4; \
+ struct sockaddr_in6 v6; \
+ } wsck_addr \
+
+typedef struct {
+ MRP_WSCKADDR_BASE;
+} _mrp_wsckaddr_base_t;
+
+
+#define MRP_WSCK_DEFPROTO "murphy"
+#define MRP_WSCK_PROTOLEN (MRP_SOCKADDR_SIZE - sizeof(_mrp_wsckaddr_base_t))
+
+
+typedef struct {
+ MRP_WSCKADDR_BASE; /* websocket address */
+ char wsck_proto[MRP_WSCK_PROTOLEN]; /* websocket protocol */
+} mrp_wsckaddr_t;
+
+
+/*
+ * websocket transport options and values
+ */
+
+#define MRP_WSCK_OPT_SENDMODE "send-mode" /* sendmode option name */
+#define MRP_WSCK_SENDMODE_TEXT "text" /* sendmode text option */
+#define MRP_WSCK_SENDMODE_BINARY "binary" /* sendmode blob option */
+
+
+#define MRP_WSCK_OPT_HTTPDIR "http-dir" /* HTTP content root */
+#define MRP_WSCK_OPT_MIMEMAP "mime-map" /* suffix-MIME table */
+#define MRP_WSCK_OPT_URIMAP "uri-map" /* URI-path table */
+#define MRP_WSCK_OPT_SSL_CERT "ssl-cert" /* path to SSL certificate */
+#define MRP_WSCK_OPT_SSL_PKEY "ssl-pkey" /* path to SSL priv. key */
+#define MRP_WSCK_OPT_SSL_CA "ssl-ca" /* path to SSL CA */
+#define MRP_WSCK_OPT_SSL "ssl" /* whether to connect with SSL */
+
+/*
+ * It is also possible to serve content over HTTP on a websocket transport.
+ *
+ * This is primarily intended for serving javascript API libraries to
+ * clients talking to you via the same websocket transport. The served
+ * libraries hide the details of the underlying communication protocol
+ * and present a more developer-friendly conventional javascript API.
+ *
+ * Currently the websocket transport provides two mechanisms for
+ * configuring HTTP content serving.
+ *
+ * 1) You can put all the files you're willing to expose via HTTP to a
+ * dedicated directory and configure it to the transport as the
+ * MRP_WSCK_OPT_HTTPROOT option. If you serve any other types of
+ * files than HTML (*.htm, *.html), javascript (*.js), or text
+ * (*.txt) files than you should also push down a table to map
+ * the extra file suffices to MIME types. You can do this using
+ * the MRP_SCK_OPT_MIMEMAP transport option.
+ *
+ * 2) You can use a mapping table that maps URIs to file path / mime
+ * type pairs. You can push this table down to the transport as
+ * the MRP_WSCK_URIMAP transport option.
+ *
+ * HTTPROOT takes a char *, URIMAP takes a mrp_wsck_urimap_t *, and
+ * MIMEMAP takes a mrp_wsck_mimemap_t * as their values. Both URI
+ * and MIME type tables need to be NULL-terminated. If you set both
+ * HTTPROOT and URIMAP, URIMAP entries with relative path names will
+ * be treated relative to HTTPROOT.
+ *
+ * Notes:
+ *
+ * If you push down any of these options, the websocket backend
+ * will use the provided values as such __without__ making an
+ * internal copy. IOW, you better make sure that the passed values
+ * are valid throughout the full lifetime of the transport (and
+ * if that is a transport you listen on also the lifetime of all
+ * transports accepted on that transport) otherwise you'll end up
+ * with severe memory corruption.
+ *
+ */
+
+typedef struct {
+ const char *uri; /* exported URI */
+ const char *path; /* path to file */
+ const char *type; /* MIME type to use */
+} mrp_wsck_urimap_t;
+
+typedef struct {
+ const char *suffix; /* filename suffix */
+ const char *type; /* MIME type */
+} mrp_wsck_mimemap_t;
+
+
+
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_WEBSOCKET_TRANSPORT_H__ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <getopt.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <murphy/common.h>
+#include <murphy/plugins/console-protocol.h>
+
+#include <breedline/breedline-murphy.h>
+
+#define client_info mrp_log_info
+#define client_warn mrp_log_warning
+#define client_error mrp_log_error
+
+#define DEFAULT_PROMPT "murphy"
+#define DEFAULT_ADDRESS "unxs:@murphy-console"
+
+
+/*
+ * message types
+ */
+
+typedef enum {
+ MSG_UNKNOWN, /* unknown message */
+ MSG_PROMPT, /* set new prompt */
+ MSG_COMMAND, /* client command */
+ MSG_ECHO, /* output from server */
+ MSG_COMPLETIONS, /* get/set completion results */
+} msg_type_t;
+
+
+/*
+ * client receive buffer
+ */
+
+#define RECVBUF_MAXSIZE /* maximum buffer size */
+
+typedef struct {
+ char *buf; /* incoming data buffer */
+ int size; /* size of buffer */
+ char *in; /* write pointer */
+ char *out; /* read pointer */
+} recvbuf_t;
+
+
+/*
+ * client context
+ */
+
+typedef struct {
+ const char *server; /* server address */
+ int log_mask; /* log mask */
+ const char *log_target; /* log target */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_transport_t *t; /* transport to server */
+ int seqno; /* sequence number */
+ recvbuf_t buf; /* receive buffer */
+ brl_t *brl; /* breedline for terminal input */
+ char **cmds; /* commands to run */
+ int ncmd; /* number of commands */
+ int ccmd; /* current command */
+} client_t;
+
+
+int send_cmd(client_t *c, const char *cmd)
+{
+ mrp_msg_t *msg;
+ uint16_t tag, type;
+ uint32_t len;
+ int success;
+
+ len = cmd ? strlen(cmd) + 1 : 0;
+
+ if (len > 1) {
+ tag = MRP_CONSOLE_INPUT;
+ type = MRP_MSG_FIELD_BLOB;
+ msg = mrp_msg_create(tag, type, len, cmd, NULL);
+
+ if (msg != NULL) {
+ success = mrp_transport_send(c->t, msg);
+ mrp_msg_unref(msg);
+ return success;
+ }
+
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+void input_cb(brl_t *brl, const char *input, void *user_data)
+{
+ client_t *c = (client_t *)user_data;
+ int len = input ? strlen(input) + 1 : 0;
+
+ if (len > 1) {
+ brl_add_history(brl, input);
+ brl_hide_prompt(brl);
+
+ send_cmd(c, input);
+
+ brl_show_prompt(brl);
+ }
+}
+
+
+static int input_setup(client_t *c)
+{
+ int fd;
+ const char *prompt;
+
+ fd = fileno(stdin);
+ prompt = DEFAULT_PROMPT;
+ c->brl = brl_create_with_murphy(fd, prompt, c->ml, input_cb, c);
+
+ if (c->brl != NULL) {
+ brl_show_prompt(c->brl);
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Failed to breedline for console input.");
+ return FALSE;
+ }
+}
+
+
+static void input_cleanup(client_t *c)
+{
+ if (c->brl != NULL) {
+ brl_destroy(c->brl);
+ c->brl = NULL;
+ }
+}
+
+
+static void hide_prompt(client_t *c)
+{
+ if (c->brl)
+ brl_hide_prompt(c->brl);
+}
+
+
+static void set_prompt(client_t *c, const char *prompt)
+{
+ if (c->brl)
+ brl_set_prompt(c->brl, prompt);
+}
+
+
+static void show_prompt(client_t *c)
+{
+ if (c->brl)
+ brl_show_prompt(c->brl);
+}
+
+
+void recvfrom_evt(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
+{
+ client_t *c = (client_t *)user_data;
+ mrp_msg_field_t *f;
+ char *prompt, *output;
+ size_t size;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ hide_prompt(c);
+
+ if ((f = mrp_msg_find(msg, MRP_CONSOLE_OUTPUT)) != NULL) {
+ output = f->str;
+ size = f->size[0];
+ printf("%.*s", (int)size, output);
+ }
+ else if ((f = mrp_msg_find(msg, MRP_CONSOLE_PROMPT)) != NULL) {
+ prompt = f->str;
+ set_prompt(c, prompt);
+ }
+ else if ((f = mrp_msg_find(msg, MRP_CONSOLE_BYE)) != NULL) {
+ mrp_mainloop_quit(c->ml, 0);
+ return;
+ }
+
+ if (c->cmds != NULL) {
+ if (c->ccmd < c->ncmd)
+ send_cmd(c, c->cmds[c->ccmd++]);
+ else
+ mrp_mainloop_quit(c->ml, 0);
+ }
+
+ show_prompt(c);
+}
+
+
+
+void recv_evt(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ recvfrom_evt(t, msg, NULL, 0, user_data);
+}
+
+
+void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ client_t *c = (client_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(c);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s).", error,
+ strerror(error));
+ exit(1);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection.");
+ mrp_mainloop_quit(c->ml, 0);
+ }
+}
+
+
+int client_setup(client_t *c)
+{
+ static mrp_transport_evt_t evt;
+
+ mrp_sockaddr_t addr;
+ socklen_t addrlen;
+ const char *type;
+
+ addrlen = mrp_transport_resolve(NULL, c->server,
+ &addr, sizeof(addr), &type);
+
+ if (addrlen > 0) {
+ evt.closed = closed_evt;
+ evt.recvmsg = recv_evt;
+ evt.recvmsgfrom = recvfrom_evt;
+
+ c->t = mrp_transport_create(c->ml, type, &evt, c, 0);
+
+ if (c->t == NULL) {
+ mrp_log_error("Failed to create new transport.");
+ return FALSE;
+ }
+
+ if (!mrp_transport_connect(c->t, &addr, addrlen)) {
+ mrp_log_error("Failed to connect to %s.", c->server);
+ mrp_transport_destroy(c->t);
+ c->t = NULL;
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+ else
+ mrp_log_error("Failed to resolve address '%s'.", c->server);
+
+ return FALSE;
+}
+
+
+static void client_cleanup(client_t *c)
+{
+ mrp_transport_destroy(c->t);
+ c->t = NULL;
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+
+ MRP_UNUSED(user_data);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+static void client_set_defaults(client_t *c)
+{
+ mrp_clear(c);
+ c->seqno = 1;
+ c->server = DEFAULT_ADDRESS;
+ c->log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
+ c->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+ const char *exe;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ exe = strrchr(argv0, '/');
+
+ printf("usage: %s [options] [console-commands]\n\n"
+ "The possible options are:\n"
+ " -s, --server <address> server transport to connect to\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable debug messages\n"
+ " -h, --help show help on usage\n",
+ argv0);
+ printf("\n");
+ printf("If commands are given on the command line, the console will ");
+ printf("first execute\nthem then exit after receiving a response to ");
+ printf("the last command. If no commands\n");
+ printf("are given on the command line, the console will prompt for ");
+ printf("commands to execute.\nFor a short summary of commands ");
+ printf("try running '%s help'.\n", exe ? exe + 1 : argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+int parse_cmdline(client_t *c, int argc, char **argv)
+{
+# define OPTIONS "s:l:t:v:d:h"
+ struct option options[] = {
+ { "server" , required_argument, NULL, 's' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ c->server = optarg;
+ break;
+
+ case 'v':
+ c->log_mask <<= 1;
+ c->log_mask |= 1;
+ break;
+
+ case 'l':
+ c->log_mask = mrp_log_parse_levels(optarg);
+ if (c->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ c->log_target = mrp_log_parse_target(optarg);
+ if (!c->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ c->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return optind;
+}
+
+
+int main(int argc, char *argv[])
+{
+ client_t c;
+ int next;
+
+ client_set_defaults(&c);
+ next = parse_cmdline(&c, argc, argv);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ c.seqno = 1;
+
+ if ((c.ml = mrp_mainloop_create()) == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ exit(1);
+ }
+
+ mrp_add_sighandler(c.ml, SIGINT, signal_handler, &c);
+
+ if (next >= argc) {
+ if (!input_setup(&c))
+ goto fail;
+ c.cmds = NULL;
+ c.ncmd = 0;
+ c.ccmd = 0;
+ }
+ else {
+ c.cmds = argv + next;
+ c.ncmd = argc - next;
+ c.ccmd = 0;
+ }
+
+ if (!client_setup(&c))
+ goto fail;
+
+ mrp_mainloop_run(c.ml);
+
+ client_cleanup(&c);
+
+ if (next >= argc)
+ input_cleanup(&c);
+
+ return 0;
+
+ fail:
+ client_cleanup(&c);
+ input_cleanup(&c);
+ exit(1);
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CORE_H__
+#define __MURPHY_CORE_H__
+
+#include <murphy/core/context.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/console.h>
+
+#endif
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/core/context.h>
+#include <murphy/core/auth.h>
+
+
+static int deny_auth(const char *target, mrp_auth_mode_t mode, const char *id,
+ const char *token, void *auth_data)
+{
+ MRP_UNUSED(target);
+ MRP_UNUSED(mode);
+ MRP_UNUSED(id);
+ MRP_UNUSED(token);
+ MRP_UNUSED(auth_data);
+
+ return MRP_AUTH_RESULT_DENY;
+}
+
+
+MRP_REGISTER_AUTHENTICATOR("deny", NULL, deny_auth);
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/smack.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/core/context.h>
+#include <murphy/core/auth.h>
+
+
+static int smack_auth(const char *target, mrp_auth_mode_t mode, const char *id,
+ const char *token, void *auth_data)
+{
+ char access[4];
+ int status;
+
+ MRP_UNUSED(token);
+ MRP_UNUSED(auth_data);
+
+ if (target == NULL || id == NULL)
+ goto error;
+
+ access[0] = (mode & MRP_AUTH_MODE_READ) ? 'r' : '-';
+ access[1] = (mode & MRP_AUTH_MODE_WRITE) ? 'w' : '-';
+ access[2] = (mode & MRP_AUTH_MODE_EXEC) ? 'x' : '-';
+ access[3] = '\0';
+
+ status = smack_have_access(target, id, access);
+
+ mrp_debug("SMACK '%s' access of %s to %s: %d", access, id, target, status);
+
+ switch (status) {
+ case 1:
+ return MRP_AUTH_RESULT_GRANT;
+ case 0:
+ return MRP_AUTH_RESULT_DENY;
+ default:
+ error:
+ return MRP_AUTH_RESULT_ERROR;
+ }
+}
+
+
+MRP_REGISTER_AUTHENTICATOR("smack", NULL, smack_auth);
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include <murphy/core/context.h>
+#include <murphy/core/auth.h>
+
+
+typedef struct {
+ char *name; /* backend name */
+ mrp_auth_cb_t cb; /* backend method */
+ void *auth_data; /* backend data */
+ mrp_list_hook_t hook; /* to list of backends */
+} auth_backend_t;
+
+
+static MRP_LIST_HOOK(pending);
+
+
+static auth_backend_t *find_auth(mrp_list_hook_t *backends, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ auth_backend_t *auth;
+
+ mrp_list_foreach(backends, p, n) {
+ auth = mrp_list_entry(p, typeof(*auth), hook);
+
+ if (!strcmp(auth->name, name))
+ return auth;
+ }
+
+ return NULL;
+}
+
+
+static int register_auth(mrp_list_hook_t *backends, const char *name,
+ mrp_auth_cb_t cb, void *auth_data)
+{
+ auth_backend_t *auth;
+
+ if (find_auth(backends, name) != NULL)
+ return FALSE;
+
+ auth = mrp_allocz(sizeof(*auth));
+
+ if (auth != NULL) {
+ mrp_list_init(&auth->hook);
+
+ auth->name = mrp_strdup(name);
+ auth->cb = cb;
+ auth->auth_data = auth_data;
+
+ if (auth->name != NULL) {
+ /*
+ * Notes:
+ * Prepending here is a crude hack to make sure the first
+ * registered backend, which is 'deny', ends up being the
+ * last in the list of authenticators. Maybe we should add
+ * a priority to the backend registration interface and
+ * use it to make this more explicit...
+ */
+
+ mrp_list_prepend(backends, &auth->hook);
+
+ mrp_debug("registered authentication backend %s", auth->name);
+
+ return TRUE;
+ }
+
+ mrp_free(auth);
+ }
+
+ return FALSE;
+}
+
+
+static void unregister_auth(mrp_list_hook_t *backends, const char *name)
+{
+ auth_backend_t *auth;
+
+ auth = find_auth(backends, name);
+
+ if (auth != NULL) {
+ mrp_list_delete(&auth->hook);
+ mrp_free(auth->name);
+ mrp_free(auth);
+ }
+}
+
+
+int mrp_register_authenticator(mrp_context_t *ctx, const char *name,
+ mrp_auth_cb_t cb, void *auth_data)
+{
+ mrp_list_hook_t *backends;
+
+ if (ctx != NULL) {
+ if (MRP_UNLIKELY(!mrp_list_empty(&pending)))
+ mrp_list_move(&ctx->auth, &pending);
+
+ backends = &ctx->auth;
+ }
+ else
+ backends = &pending;
+
+ if (register_auth(backends, name, cb, auth_data) == 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+void mrp_unregister_authenticator(mrp_context_t *ctx, const char *name)
+{
+ mrp_list_hook_t *backends = ctx ? &ctx->auth : &pending;
+
+ unregister_auth(backends, name);
+}
+
+
+int mrp_authenticate(mrp_context_t *ctx, const char *backend,
+ const char *target, mrp_auth_mode_t mode,
+ const char *id, const char *token)
+{
+ auth_backend_t *auth;
+ mrp_list_hook_t *p, *n;
+ int status, result;
+
+ if (MRP_UNLIKELY(!mrp_list_empty(&pending)))
+ mrp_list_move(&ctx->auth, &pending);
+
+ /*
+ * Notes:
+ *
+ * Currently we let the caller request authentication by any available
+ * backend by using MRP_AUTH_ANY. If requested so, access is granted
+ * if any of the backends grants access.
+ *
+ * We might want to change this in the future, probably by either
+ * requiring the caller to always specify a valid authentication backend,
+ * or by having one of the backends be marked as default which then would
+ * be used for authentication in these cases. Either of those would make
+ * it more difficult to grant unwanted access accidentially in the case
+ * of multiple available backends.
+ */
+
+ result = MRP_AUTH_RESULT_ERROR;
+
+ mrp_list_foreach(&ctx->auth, p, n) {
+ auth = mrp_list_entry(p, typeof(*auth), hook);
+
+ if (backend == MRP_AUTH_ANY || !strcmp(backend, auth->name)) {
+ status = auth->cb(target, mode, id, token, auth->auth_data);
+
+ mrp_debug("backend %s, access 0x%x of %s/%s to %s: %d", auth->name,
+ mode, id, token ? token : "<none>", target, status);
+
+ if (backend != MRP_AUTH_ANY)
+ return status;
+
+ switch (status) {
+ case MRP_AUTH_RESULT_GRANT:
+ return MRP_AUTH_RESULT_GRANT;
+ case MRP_AUTH_RESULT_DENY:
+ result = MRP_AUTH_RESULT_DENY;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ return result;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_AUTH_H__
+#define __MURPHY_AUTH_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/core/context.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_AUTH_ANY NULL /* any authenticator */
+
+/*
+ * authentication access modes
+ */
+typedef enum {
+ MRP_AUTH_MODE_UNKNOWN = 0x0, /* mode unknown / not applicabe */
+ MRP_AUTH_MODE_NA = 0x0, /* alias for unknown */
+ MRP_AUTH_MODE_READ = 0x1, /* 'read' access */
+ MRP_AUTH_MODE_WRITE = 0x2, /* 'write' access */
+ MRP_AUTH_MODE_EXEC = 0x4, /* 'execution' access */
+} mrp_auth_mode_t;
+
+
+/*
+ * authentication results
+ */
+typedef enum {
+ MRP_AUTH_RESULT_ERROR = -1, /* authentiation failed with error */
+ MRP_AUTH_RESULT_DENY = 0, /* requested access denied */
+ MRP_AUTH_RESULT_GRANT = 1, /* requested access granted */
+} mrp_auth_result_t;
+
+
+/** Type for authenticator backend callback. */
+typedef int (*mrp_auth_cb_t)(const char *target, mrp_auth_mode_t mode,
+ const char *id, const char *token,
+ void *user_data);
+
+/** Register an authentication backend. */
+int mrp_register_authenticator(mrp_context_t *ctx, const char *name,
+ mrp_auth_cb_t cb, void *user_data);
+
+/** Unregister an authentication backend. */
+void mrp_unregister_authenticator(mrp_context_t *ctx, const char *name);
+
+/** Check if the given id has the reqested access to the given target. */
+int mrp_authenticate(mrp_context_t *ctx, const char *backend,
+ const char *target, mrp_auth_mode_t mode,
+ const char *id, const char *token);
+
+/** Convenience macro for autoregistering an authentication backend. */
+#define MRP_REGISTER_AUTHENTICATOR(name, init_cb, auth_cb) \
+ MRP_INIT static void register_authenticator(void) \
+ { \
+ int (*initfn)(void **) = init_cb; \
+ void *user_data = NULL; \
+ \
+ if (initfn == NULL || initfn(&user_data)) \
+ mrp_register_authenticator(NULL, name, auth_cb, user_data); \
+ else \
+ mrp_log_error("Failed to initialize user data for " \
+ "authenticator '%s'.", name); \
+ } \
+ struct __mrp_allow_trailing_semicolon
+
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_AUTH_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#define DOTS "........................................................" \
+ "......................."
+
+#define NPRINT(mc, fmt, args...) fprintf(mc->stdout, fmt , ## args)
+#define EPRINT(mc, fmt, args...) fprintf(mc->stderr, fmt , ## args)
+
+
+
+/*
+ * top-level console commands
+ */
+
+
+static void get_string_lengthes(mrp_console_t *mc,
+ size_t *nmaxp, size_t *smaxp, size_t *tmaxp)
+{
+ mrp_console_group_t *grp;
+ mrp_console_cmd_t *cmd;
+ mrp_list_hook_t *p, *n;
+ int i;
+ size_t nlen, slen, tmax, nmax, smax;
+
+ tmax = nmax = smax = 0;
+
+ mrp_list_foreach(&mc->ctx->cmd_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+
+ for (i = 0, cmd = grp->commands; i < grp->ncommand; i++, cmd++) {
+ nlen = strlen(cmd->name);
+ slen = strlen(cmd->summary);
+ nmax = MRP_MAX(nmax, nlen);
+ smax = MRP_MAX(smax, slen);
+ tmax = MRP_MAX(tmax, nlen + slen);
+ }
+ }
+
+ mrp_list_foreach(&core_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+
+ for (i = 0, cmd = grp->commands; i < grp->ncommand; i++, cmd++) {
+ nlen = strlen(cmd->name);
+ slen = strlen(cmd->summary);
+ nmax = MRP_MAX(nmax, nlen);
+ smax = MRP_MAX(smax, slen);
+ tmax = MRP_MAX(tmax, nlen + slen);
+ }
+ }
+
+ *nmaxp = nmax;
+ *smaxp = smax;
+ *tmaxp = tmax;
+}
+
+
+static void help_overview(mrp_console_t *mc)
+{
+ mrp_console_group_t *grp;
+ mrp_console_cmd_t *cmd;
+ mrp_list_hook_t *p, *n;
+ int i, l, dend;
+ size_t tmax, nmax, smax;
+
+ get_string_lengthes(mc, &nmax, &smax, &tmax);
+
+ if (4 + 2 + 2 + tmax < 79) {
+ dend = 79 - smax - 2;
+ }
+ else
+ dend = tmax + 20;
+
+ NPRINT(mc, "The following commands are available:\n\n");
+
+ mrp_list_foreach(&mc->ctx->cmd_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+
+ if (*grp->name)
+ NPRINT(mc, " commands in group '%s':\n", grp->name);
+ else
+ NPRINT(mc, " general commands:\n");
+
+ for (i = 0, cmd = grp->commands; i < grp->ncommand; i++, cmd++) {
+ NPRINT(mc, " %s %n", cmd->name, &l);
+ NPRINT(mc, "%*.*s %s\n", dend - l, dend - l, DOTS, cmd->summary);
+ }
+
+ NPRINT(mc, "\n");
+ }
+
+ mrp_list_foreach(&core_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+
+ if (*grp->name)
+ NPRINT(mc, " commands in group '%s':\n", grp->name);
+ else
+ NPRINT(mc, " general commands:\n");
+
+ for (i = 0, cmd = grp->commands; i < grp->ncommand; i++, cmd++) {
+ NPRINT(mc, " %s %n", cmd->name, &l);
+ NPRINT(mc, "%*.*s %s\n", dend - l, dend - l, DOTS, cmd->summary);
+ }
+
+ NPRINT(mc, "\n");
+ }
+}
+
+
+static void help_group(mrp_console_t *mc, const char *name)
+{
+ mrp_console_group_t *grp;
+ mrp_console_cmd_t *cmd;
+ mrp_list_hook_t *p, *n;
+ const char *t;
+ int i;
+
+ grp = find_group(mc->ctx, name);
+
+ if (grp != NULL) {
+ if (grp->descr != NULL)
+ NPRINT(mc, "%s\n", grp->descr);
+
+ NPRINT(mc, "The following commands are available:\n");
+ for (i = 0, cmd = grp->commands; i < grp->ncommand; i++, cmd++) {
+ NPRINT(mc, "- %s (syntax: %s%s%s)\n\n", cmd->name,
+ grp->name ? grp->name : "", grp->name ? " " : "",
+ cmd->syntax);
+ NPRINT(mc, "%s\n", cmd->description);
+ }
+ }
+ else {
+ EPRINT(mc, "Command group '%s' does not exist.\n", name);
+ EPRINT(mc, "The existing groups are: ");
+ t = "";
+ mrp_list_foreach(&mc->ctx->cmd_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+ if (*grp->name) {
+ EPRINT(mc, "%s'%s'", t, grp->name);
+ t = ", ";
+ }
+ }
+ EPRINT(mc, ".\n");
+ }
+
+
+}
+
+
+#define HELP_SYNTAX "help [group|command]"
+#define HELP_SUMMARY "print help on a command group or a command"
+#define HELP_DESCRIPTION \
+ "Give general help or help on a specific command group or a\n" \
+ "single command.\n"
+
+static void cmd_help(mrp_console_t *mc, void *user_data, int argc, char **argv)
+{
+ console_t *c = (console_t *)mc;
+ char *ha[2];
+
+ MRP_UNUSED(c);
+
+ switch (argc) {
+ case 2:
+ help_overview(mc);
+ break;
+
+ case 3:
+ help_group(mc, argv[2]);
+ break;
+
+ case 4:
+ fprintf(mc->stdout, "Help for command '%s/%s'.\n", argv[2], argv[3]);
+ break;
+
+ default:
+ ha[0] = "help";
+ ha[1] = "help";
+ fprintf(mc->stderr, "help: invalid arguments (%d).\n", argc);
+ fflush(mc->stderr);
+ cmd_help(mc, user_data, 2, ha);
+ }
+}
+
+
+#define EXIT_SYNTAX "exit"
+#define EXIT_SUMMARY "exit from a command group or the console"
+#define EXIT_DESCRIPTION \
+ "Exit current console mode, or close the console.\n"
+
+
+static void cmd_exit(mrp_console_t *mc, void *user_data, int argc, char **argv)
+{
+ console_t *c = (console_t *)mc;
+ char *ha[2];
+
+ switch (argc) {
+ case 2:
+ if (c->grp != NULL) {
+ if (c->cmd != NULL)
+ c->cmd = NULL;
+ else
+ c->grp = NULL;
+ }
+ else {
+ close_console:
+ fprintf(mc->stdout, "Bye.\n");
+ mrp_destroy_console(mc);
+ }
+ break;
+
+ case 3:
+ if (!strcmp(argv[2], "console"))
+ goto close_console;
+ /* intentional fall-through */
+
+ default:
+ ha[0] = "help";
+ ha[1] = "exit";
+ fprintf(mc->stderr, "exit: invalid arguments\n");
+ cmd_help(mc, user_data, 2, ha);
+ }
+}
+
+
+MRP_CONSOLE_GROUP(builtin_cmd_group, "", NULL, NULL, {
+ MRP_TOKENIZED_CMD("help", cmd_help, FALSE,
+ HELP_SYNTAX, HELP_SUMMARY, HELP_DESCRIPTION),
+ MRP_TOKENIZED_CMD("exit", cmd_exit, FALSE,
+ EXIT_SYNTAX, EXIT_SUMMARY, EXIT_DESCRIPTION),
+});
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "console-builtin.c"
+#include "console-debug.c"
+#include "console-db.c"
+#include "console-log.c"
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CONSOLE_COMMAND_H__
+#define __MURPHY_CONSOLE_COMMAND_H__
+
+
+/** Macro to declare an array of console commands. */
+#define MRP_CONSOLE_COMMANDS(_var, ...) \
+ static mrp_console_cmd_t _var[] = __VA_ARGS__
+
+/** Macro to declare a console command group. */
+#define MRP_CONSOLE_GROUP(_var, _name, _descr, _data, ...) \
+ MRP_CONSOLE_COMMANDS(_var##_cmds, __VA_ARGS__); \
+ static mrp_console_group_t _var = { \
+ .name = (char *)_name, \
+ .descr = _descr, \
+ .user_data = _data, \
+ .commands = _var##_cmds, \
+ .ncommand = MRP_ARRAY_SIZE(_var##_cmds), \
+ .hook = MRP_LIST_INIT(_var.hook), \
+ };
+
+/** Macro to declare a console command that wants tokenized input. */
+#define MRP_TOKENIZED_CMD(_name, _cb, _flags, _syntax, _summ, _descr) { \
+ .name = _name, \
+ .syntax = _syntax, \
+ .summary = _summ, \
+ .description = _descr, \
+ .flags = ((_flags) == 0x1 ? MRP_CONSOLE_SELECTABLE : _flags), \
+ { .tok = _cb, } \
+ }
+
+/** Macro to declare a console command that wants a raw input. */
+#define MRP_RAWINPUT_CMD(_name, _cb, _flags, _syntax, _summ, _descr) { \
+ .name = _name, \
+ .syntax = _syntax, \
+ .summary = _summ, \
+ .description = _descr, \
+ .flags = MRP_CONSOLE_RAWINPUT | \
+ ((_flags) == 0x1 ? MRP_CONSOLE_SELECTABLE : _flags), \
+ { .raw = _cb, } \
+ }
+
+typedef struct mrp_console_s mrp_console_t;
+
+
+/*
+ * console command flags
+ */
+
+typedef enum {
+ MRP_CONSOLE_TOKENIZE = 0x0, /* wants tokenized input */
+ MRP_CONSOLE_RAWINPUT = 0x2, /* wants raw input */
+ MRP_CONSOLE_SELECTABLE = 0x4, /* selectable as command mode */
+ MRP_CONSOLE_CATCHALL = 0x8, /* catch-all command handler */
+} mrp_console_flag_t;
+
+
+/*
+ * a console command
+ */
+
+typedef struct {
+ const char *name; /* command name */
+ const char *syntax; /* command syntax */
+ const char *summary; /* short help */
+ const char *description; /* long command description */
+ mrp_console_flag_t flags; /* command flags */
+ union { /* tokenized or raw input cb */
+ void (*tok)(mrp_console_t *c, void *user_data, int argc, char **argv);
+ void (*raw)(mrp_console_t *c, void *user_data, const char *grp,
+ const char *cmd, char *args);
+ };
+} mrp_console_cmd_t;
+
+
+/*
+ * a group of console commands
+ */
+
+typedef struct {
+ char *name; /* command group name/prefix */
+ char *descr; /* group description */
+ void *user_data; /* opaque callback data */
+ mrp_console_cmd_t *commands; /* commands in this group */
+ int ncommand; /* number of commands */
+ mrp_list_hook_t hook; /* to list of command groups */
+} mrp_console_group_t;
+
+
+/** Register a console command group. */
+int mrp_console_add_group(mrp_context_t *ctx, mrp_console_group_t *group);
+
+/** Unregister a console command group. */
+int mrp_console_del_group(mrp_context_t *ctx, mrp_console_group_t *group);
+
+/** Convenience macro to register a group of core commands. */
+#define MRP_CORE_CONSOLE_GROUP(_var, _name, _descr, _data, ...) \
+ MRP_CONSOLE_GROUP(_var, _name, _descr, _data, __VA_ARGS__); \
+ \
+ static void _var##_register_core_group(void) \
+ __attribute__((constructor)); \
+ \
+ static void __attribute__((constructor)) \
+ _var##_register_core_group(void) { \
+ mrp_console_add_core_group(&_var); \
+ } \
+ \
+ static void __attribute__((destructor)) \
+ _var##_unregister_core_group(void) { \
+ mrp_console_del_core_group(&_var); \
+ } \
+ struct mrp_allow_trailing_semicolon
+
+/** Pre-register a group of core commands to register later to any context. */
+int mrp_console_add_core_group(mrp_console_group_t *group);
+
+/** Unregister a pre-registered group of core commands. */
+int mrp_console_del_core_group(mrp_console_group_t *group);
+
+#endif /* __MURPHY_CONSOLE_COMMAND_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+ * DB commands
+ */
+
+#include <stdarg.h>
+
+#include <murphy-db/mql.h>
+#include <murphy-db/mqi.h>
+
+static void db_cmd(char *fmt, ...)
+{
+ mql_result_t *r;
+ char buf[1024];
+ va_list ap;
+ int n, error;
+ const char *msg;
+
+ va_start(ap, fmt);
+ n = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (n < (int)sizeof(buf) && n > 0) {
+ r = mql_exec_string(mql_result_string, buf);
+
+ if (mql_result_is_success(r))
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ error = mql_result_error_get_code(r);
+ msg = mql_result_error_get_message(r);
+
+ printf("DB error %d: %s\n", error, msg ? msg : "unknown error");
+ }
+
+ mql_result_free(r);
+ }
+}
+
+
+static void db_exec(mrp_console_t *c, void *user_data, const char *grp,
+ const char *cmd, char *args)
+{
+ mqi_handle_t tx;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(grp);
+ MRP_UNUSED(cmd);
+
+ tx = mqi_begin_transaction();
+ db_cmd(args);
+ mqi_commit_transaction(tx);
+}
+
+
+void db_source(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ mqi_handle_t tx;
+ int i, success;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ success = TRUE;
+ tx = mqi_begin_transaction();
+
+ for (i = 2; i < argc && success; i++) {
+ if (mql_exec_file(argv[i]) == 0)
+ printf("DB script '%s' OK\n", argv[i]);
+ else {
+ printf("DB script error %d: %s\n", errno, strerror(errno));
+ success = FALSE;
+ }
+ }
+
+ if (success)
+ mqi_commit_transaction(tx);
+ else {
+ mqi_rollback_transaction(tx);
+ printf("DB rolled back.\n");
+ }
+}
+
+
+#define DB_GROUP_DESCRIPTION \
+ "Database commands provide means to manipulate the Murphy database\n" \
+ "from the console. Commands are provided for listing, describing,\n" \
+ "and removing tables as well as for issuing arbitrary high-level\n" \
+ "MQL commands. Note that these commands are intended for debugging\n" \
+ "and debugging purposes. Extra care should to be taken when directly\n" \
+ "manipulating the database."
+
+#define DBEXEC_SYNTAX "<DB command>"
+#define DBEXEC_SUMMARY "execute the given database MQL command"
+#define DBEXEC_DESCRIPTION "Executes the given MQL command and prints the\n" \
+ "result.\n"
+
+#define DBSRC_SYNTAX "source <file>"
+#define DBSRC_SUMMARY "evaluate the MQL script in the given <file>"
+#define DBSRC_DESCRIPTION "Read and evaluate the contents of <file>.\n"
+
+
+MRP_CORE_CONSOLE_GROUP(db_group, "db", DB_GROUP_DESCRIPTION, NULL, {
+ MRP_TOKENIZED_CMD("source", db_source, FALSE,
+ DBSRC_SYNTAX, DBSRC_SUMMARY, DBSRC_DESCRIPTION),
+ MRP_RAWINPUT_CMD("eval", db_exec,
+ MRP_CONSOLE_CATCHALL | MRP_CONSOLE_SELECTABLE,
+ DBEXEC_SYNTAX, DBEXEC_SUMMARY, DBEXEC_DESCRIPTION),
+});
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/core/console.h>
+#include <errno.h>
+
+/*
+ * debug commands
+ */
+
+static void debug_enable(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+ MRP_UNUSED(user_data);
+
+ mrp_debug_enable(TRUE);
+
+ printf("Debugging is now enabled.\n");
+}
+
+
+static void debug_disable(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+ MRP_UNUSED(user_data);
+
+ mrp_debug_enable(FALSE);
+
+ printf("Debugging is now disabled.\n");
+}
+
+
+static void debug_show(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ mrp_debug_dump_config(c->stdout);
+}
+
+
+static void debug_set(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ int i;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ for (i = 2; i < argc; i++)
+ mrp_debug_set_config(argv[i]);
+}
+
+
+static void debug_reset(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ mrp_debug_reset();
+
+ printf("Debugging configuration has been reset to default.");
+}
+
+
+static void debug_mm(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ MRP_UNUSED(user_data);
+
+ if (argc == 3 && !strcmp(argv[2], "dump"))
+ mrp_mm_dump(c->stdout);
+ else if (argc == 4 && !strcmp(argv[2], "dump")) {
+ char *path = argv[3];
+ FILE *fp = fopen(path, "w");
+
+ if (fp != NULL) {
+ printf("Producing mm-dump to '%s'...\n", path);
+ mrp_mm_dump(fp);
+ fclose(fp);
+ }
+ else
+ printf("Failed to open '%s' (%d: %s).", path,
+ errno, strerror(errno));
+ }
+ else
+ printf("Unknown command...\n");
+}
+
+
+#define DEBUG_GROUP_DESCRIPTION \
+ "Debugging commands provide fine-grained control over runtime\n" \
+ "debugging messages produced by the murphy daemon or any of the\n" \
+ "murphy plugins loaded. Each debug message that is generated by\n" \
+ "the standard murphy debug macro declares a debug site that can\n" \
+ "be turned on or off using debug rules. Debug rules come in two\n" \
+ "flavours, enabling and inhibiting. Enabling rules turn matching\n" \
+ "debug messages on, while inhibiting rules turn matching debug\n" \
+ "messages off. Debug rules are in one of the following formats:\n" \
+ "\n" \
+ " func[=on|off]: all messages from <func>\n" \
+ " @file[=on|off]: all messages in <file>\n" \
+ " @file:line=[on|off]: messages at <file>:<line>\n" \
+ " *[=on|off]: all messages\n" \
+ "\n" \
+ "Filenames without a directory can match filenames with one.\n" \
+ "Enabling rules are evaluated before inhibiting rules. All debug\n" \
+ "messages are suppressed if debugging is disabled.\n"
+
+#define ENABLE_SYNTAX "enable"
+#define ENABLE_SUMMARY "enable debugging"
+#define ENABLE_DESCRIPTION \
+ "Enable debugging globally. Unless debugging is enabled, all debug\n" \
+ "messages are suppressed, even those for which matching enabling\n" \
+ "rules exist.\n"
+
+#define DISABLE_SYNTAX "disable"
+#define DISABLE_SUMMARY "disable debugging"
+#define DISABLE_DESCRIPTION \
+ "Disable debugging globally. Unless debugging is enabled all debug\n" \
+ "messages are suppressed, even those for which matching enabling\n" \
+ "rules exist.\n"
+
+#define SHOW_SYNTAX "show"
+#define SHOW_SUMMARY "show debugging configuration"
+#define SHOW_DESCRIPTION \
+ "Show the current debugging configuration, and debug rules.\n"
+
+#define SET_SYNTAX "set [+|-]rule"
+#define SET_SUMMARY "change debugging rules"
+#define SET_DESCRIPTION \
+ "Install a new or remove an existing debugging rule. Debug rules\n" \
+ "are in one of the following formats:\n" \
+ "\n" \
+ " func[=on|off]: all messages from <func>\n" \
+ " @file[=on|off]: all messages in <file>\n" \
+ " @file:line[=on|off]: messages at <file>:<line>\n" \
+ " *[=on|off]: all messages\n" \
+
+#define RESET_SYNTAX "reset"
+#define RESET_SUMMARY "reset debugging configuration"
+#define RESET_DESCRIPTION \
+ "Reset the debugging configuration to the defaults. This will turn" \
+ "disable debugging globally and flush all debugging rules.\n"
+
+#define MM_SYNTAX "mm dump [file]"
+#define MM_SUMMARY "produce an mm-dump"
+#define MM_DESCRIPTION \
+ "Produce an mm-dump of all currently allocated objects to the given\n" \
+ "or to the console.\n"
+
+MRP_CORE_CONSOLE_GROUP(debug_group, "debug", DEBUG_GROUP_DESCRIPTION, NULL, {
+ MRP_TOKENIZED_CMD("enable", debug_enable, FALSE,
+ ENABLE_SYNTAX, ENABLE_SUMMARY, ENABLE_DESCRIPTION),
+ MRP_TOKENIZED_CMD("disable", debug_disable, FALSE,
+ DISABLE_SYNTAX, DISABLE_SUMMARY, DISABLE_DESCRIPTION),
+ MRP_TOKENIZED_CMD("show", debug_show, FALSE,
+ SHOW_SYNTAX, SHOW_SUMMARY, SHOW_DESCRIPTION),
+ MRP_TOKENIZED_CMD("set", debug_set, FALSE,
+ SET_SYNTAX, SET_SUMMARY, SET_DESCRIPTION),
+ MRP_TOKENIZED_CMD("reset", debug_reset, FALSE,
+ RESET_SYNTAX, RESET_SUMMARY, RESET_DESCRIPTION),
+ MRP_TOKENIZED_CMD("mm", debug_mm, FALSE,
+ MM_SYNTAX, MM_SUMMARY, MM_DESCRIPTION),
+});
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+static void log_level(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ mrp_log_mask_t mask;
+ char buf[256];
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ if (argc == 2)
+ mask = mrp_log_get_mask();
+ else {
+ mask = mrp_log_parse_levels(argv[2]);
+ mrp_log_set_mask(mask);
+ }
+
+ printf("current logging mask: %s\n",
+ mrp_log_dump_mask(mask, buf, sizeof(buf)));
+}
+
+
+static void log_target(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ const char *target;
+ const char *targets[32];
+ int i, n;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ if (argc == 2) {
+ target = mrp_log_get_target();
+ n = mrp_log_get_targets(targets, MRP_ARRAY_SIZE(targets));
+
+ printf("available log targets:\n");
+ for (i = 0; i < n; i++)
+ printf(" %s%s\n", targets[i],
+ !strcmp(targets[i], target) ? " (active)" : "");
+ }
+ else if (argc == 3) {
+ target = argv[2];
+
+ if (!mrp_log_set_target(target))
+ printf("failed to change logging target to %s\n", target);
+ else {
+ printf("changed log target to %s\n", target);
+ mrp_log_info("changed log target to %s", target);
+ }
+ }
+ else {
+ printf("%s/%s invoked with wrong number of arguments\n",
+ argv[0], argv[1]);
+ }
+}
+
+
+
+
+#define LOG_GROUP_DESCRIPTION \
+ "Log commands provide means to configure the active logging settings\n" \
+ "of Murphy. Commands are provided for changing the logging level,\n" \
+ "listing log targets, and settting the active target.\n"
+
+#define LEVEL_SYNTAX "[[info[,warning[,error]]]]"
+#define LEVEL_SUMMARY "change or show the active logging level"
+#define LEVEL_DESCRIPTION \
+ "Changes the logging level to the given one. Without arguments it\n" \
+ "prints out the current logging level.\n"
+
+#define TARGET_SYNTAX "[stdout|stderr|syslog|<other targets>]"
+#define TARGET_SUMMARY "change or show the active logging target"
+#define TARGET_DESCRIPTION \
+ "Changes the active logging target to the given one. Without arguments\n" \
+ "it lists the available targets and the currently active one."
+
+MRP_CORE_CONSOLE_GROUP(log_group, "log", LOG_GROUP_DESCRIPTION, NULL, {
+ MRP_TOKENIZED_CMD("level" , log_level , FALSE,
+ LEVEL_SYNTAX , LEVEL_SUMMARY , LEVEL_DESCRIPTION),
+ MRP_TOKENIZED_CMD("target", log_target, FALSE,
+ TARGET_SYNTAX, TARGET_SUMMARY, TARGET_DESCRIPTION)
+});
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CONSOLE_PRIV_H__
+#define __MURPHY_CONSOLE_PRIV_H__
+
+#include <murphy/core/console.h>
+
+int console_setup(mrp_context_t *ctx);
+void console_cleanup(mrp_context_t *ctx);
+
+#endif /* __MURPHY_CONSOLE_PRIV_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define _GNU_SOURCE /* we want fopencookie */
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+
+#include <murphy/core/console.h>
+
+#define MAX_PROMPT 64 /* ie. way too long */
+#define CMD_EXIT "exit"
+#define CMD_HELP "help"
+
+#define COLOR "\E"
+#define YELLOW "33m"
+#define WHITE "37m"
+#define RED "31m"
+
+#define CNORM COLOR""WHITE
+#define CWARN COLOR""YELLOW
+#define CERR COLOR""RED
+
+#define RFD 0
+#define WFD 1
+
+
+#define MRP_CFG_MAXLINE 4096 /* input line length limit */
+#define MRP_CFG_MAXARGS 64 /* command argument limit */
+
+typedef struct {
+ char buf[MRP_CFG_MAXLINE]; /* input buffer */
+ char raw[MRP_CFG_MAXLINE]; /* raw input */
+ char *token; /* current token */
+ char *in; /* filling pointer */
+ char *out; /* consuming pointer */
+ char *next; /* next token buffer position */
+ int error; /* whether has encounted and error */
+ char *file; /* file being processed */
+ int line; /* line number */
+ int next_newline;
+ int was_newline;
+} input_t;
+
+static int get_next_line(input_t *in, char **args, size_t size);
+
+static MRP_LIST_HOOK(core_groups);
+
+/*
+ * an active console
+ */
+
+typedef struct {
+ MRP_CONSOLE_PUBLIC_FIELDS; /* publicly visible fields */
+ mrp_console_group_t *grp; /* active group if any */
+ mrp_console_cmd_t *cmd; /* active command if any */
+ char prompt[MAX_PROMPT]; /* current prompt */
+ input_t in; /* input buffer */
+ mrp_list_hook_t hook; /* to list of active consoles */
+ int pout[2]; /* pipe for output proxying */
+ mrp_io_watch_t *wout; /* output watch */
+ int ofd; /* saved fileno(stdout) */
+ int oblk; /* saved O_NONBLOCK for ofd */
+ int efd; /* saved fileno(stderr) */
+ int eblk; /* saved O_NONBLOCK for efd */
+} console_t;
+
+
+static int check_destroy(mrp_console_t *mc);
+static int purge_destroyed(mrp_console_t *mc);
+static FILE *console_fopen(mrp_console_t *mc);
+static int console_read_output(console_t *c, void *buf, size_t size);
+static void console_flush_output(console_t *c, int copy_orig);
+static void console_release_output(console_t *c);
+
+static ssize_t input_evt(mrp_console_t *mc, void *buf, size_t size);
+static void disconnected_evt(mrp_console_t *c, int error);
+static ssize_t complete_evt(mrp_console_t *c, void *input, size_t isize,
+ char **completions, size_t csize);
+
+static void register_commands(mrp_context_t *ctx);
+static void unregister_commands(mrp_context_t *ctx);
+
+void console_setup(mrp_context_t *ctx)
+{
+ mrp_list_init(&ctx->cmd_groups);
+ mrp_list_init(&ctx->consoles);
+
+ register_commands(ctx);
+}
+
+
+void console_cleanup(mrp_context_t *ctx)
+{
+ mrp_list_hook_t *p, *n;
+ console_t *c;
+
+ mrp_list_foreach(&ctx->consoles, p, n) {
+ c = mrp_list_entry(p, typeof(*c), hook);
+ mrp_destroy_console((mrp_console_t *)c);
+ }
+
+ mrp_list_init(&ctx->cmd_groups);
+
+ unregister_commands(ctx);
+}
+
+
+static void output_cb(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ mrp_console_t *mc = (mrp_console_t *)user_data;
+ console_t *c = (console_t *)mc;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ console_flush_output(c, TRUE);
+}
+
+
+mrp_console_t *mrp_create_console(mrp_context_t *ctx, mrp_console_req_t *req,
+ void *backend_data)
+{
+ static mrp_console_evt_t evt = {
+ .input = input_evt,
+ .disconnected = disconnected_evt,
+ .complete = complete_evt
+ };
+
+ console_t *c;
+
+ if (ctx->disable_console) {
+ mrp_log_error("Usage of debug console has been explicitly disabled.");
+ errno = EPERM;
+ return NULL;
+ }
+
+ if (req->write == NULL || req->close == NULL ||
+ req->free == NULL || req->set_prompt == NULL)
+ return NULL;
+
+ if ((c = mrp_allocz(sizeof(*c))) != NULL) {
+ mrp_list_init(&c->hook);
+ c->ctx = ctx;
+ c->req = *req;
+ c->evt = evt;
+
+ c->stdout = console_fopen((mrp_console_t *)c);
+ c->stderr = console_fopen((mrp_console_t *)c);
+
+ if (c->stdout == NULL || c->stderr == NULL)
+ goto fail;
+
+ c->backend_data = backend_data;
+ c->check_destroy = check_destroy;
+
+ c->in.file = "<console input>";
+ c->in.line = 0;
+
+ if (pipe(c->pout) < 0)
+ mrp_log_warning("Failed to create console redirection pipe.");
+ else {
+ fcntl(c->pout[WFD], F_SETPIPE_SZ, 32 * 1024);
+ c->wout = mrp_add_io_watch(ctx->ml, c->pout[RFD],
+ MRP_IO_EVENT_IN, output_cb, c);
+ }
+ c->ofd = c->efd = -1;
+
+ mrp_list_append(&ctx->consoles, &c->hook);
+ mrp_set_console_prompt((mrp_console_t *)c);
+ }
+ else {
+ fail:
+ if (c != NULL) {
+ if (c->stdout != NULL)
+ fclose(c->stdout);
+ if (c->stderr != NULL)
+ fclose(c->stderr);
+ mrp_free(c);
+ c = NULL;
+ }
+ }
+
+ return (mrp_console_t *)c;
+}
+
+
+static int purge_destroyed(mrp_console_t *mc)
+{
+ console_t *c = (console_t *)mc;
+
+ if (c->destroyed && !c->busy) {
+ mrp_debug("Purging destroyed console %p...", c);
+
+ mrp_list_delete(&c->hook);
+
+ fclose(c->stdout);
+ fclose(c->stderr);
+
+ mrp_del_io_watch(c->wout);
+ c->wout = NULL;
+ console_release_output(c);
+ close(c->pout[0]);
+ close(c->pout[1]);
+
+ c->req.free(c->backend_data);
+ mrp_free(c);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+void mrp_destroy_console(mrp_console_t *mc)
+{
+ if (mc != NULL && !mc->destroyed) {
+ if (mc->stdout != NULL)
+ fflush(mc->stdout);
+ if (mc->stderr != NULL)
+ fflush(mc->stderr);
+
+ if (!mc->preserve) /* the Kludge of Death... */
+ mc->destroyed = TRUE;
+
+ if (mc->backend_data != NULL) {
+ MRP_CONSOLE_BUSY(mc, {
+ mc->req.close(mc);
+ });
+ }
+
+ purge_destroyed(mc);
+ }
+}
+
+
+static int check_destroy(mrp_console_t *c)
+{
+ return purge_destroyed(c);
+}
+
+
+void mrp_console_printf(mrp_console_t *mc, const char *fmt, ...)
+{
+ console_t *c = (console_t *)mc;
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(c->stdout, fmt, ap);
+ va_end(ap);
+
+ fflush(c->stdout);
+}
+
+
+void mrp_console_vprintf(mrp_console_t *mc, const char *fmt, va_list ap)
+{
+ console_t *c = (console_t *)mc;
+ va_list cp;
+
+ va_copy(cp, ap);
+ vfprintf(c->stdout, fmt, cp);
+ va_end(cp);
+
+ fflush(c->stdout);
+}
+
+
+void mrp_set_console_prompt(mrp_console_t *mc)
+{
+ console_t *c = (console_t *)mc;
+ char *prompt, buf[MAX_PROMPT];
+
+ if (c->destroyed)
+ return;
+
+ if (c->grp != NULL) {
+ prompt = buf;
+
+ if (c->cmd != NULL)
+ snprintf(buf, sizeof(buf), "murphy %s/%s",
+ c->grp->name, c->cmd->name);
+ else
+ snprintf(buf, sizeof(buf), "murphy %s", c->grp->name);
+ }
+ else
+ prompt = "murphy";
+
+ if (strcmp(prompt, c->prompt)) {
+ strcpy(c->prompt, prompt);
+ c->req.set_prompt(mc, prompt);
+ }
+}
+
+
+static mrp_console_group_t *find_group(mrp_context_t *ctx, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_console_group_t *grp;
+
+ if (*name == '/') {
+ name++;
+
+ if (!*name)
+ return NULL;
+ }
+
+ if (ctx != NULL) {
+ mrp_list_foreach(&ctx->cmd_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+ if (!strcmp(grp->name, name))
+ return grp;
+ }
+ }
+
+ mrp_list_foreach(&core_groups, p, n) {
+ grp = mrp_list_entry(p, typeof(*grp), hook);
+ if (!strcmp(grp->name, name))
+ return grp;
+ }
+
+ return NULL;
+}
+
+
+static mrp_console_cmd_t *find_command(mrp_console_group_t *group,
+ const char *command, int *fallback)
+{
+ mrp_console_cmd_t *any = NULL;
+ mrp_console_cmd_t *cmd;
+ int i;
+
+ if (fallback != NULL)
+ *fallback = FALSE;
+
+ if (group != NULL) {
+ for (i = 0, cmd = group->commands; i < group->ncommand; i++, cmd++) {
+ if (!strcmp(cmd->name, command))
+ return cmd;
+ if (cmd->flags & MRP_CONSOLE_CATCHALL) {
+ any = cmd;
+ if (fallback != NULL)
+ *fallback = TRUE;
+ }
+ }
+ }
+
+ return any;
+}
+
+
+int mrp_console_add_group(mrp_context_t *ctx, mrp_console_group_t *group)
+{
+ mrp_console_cmd_t *cmd, *catchall;
+ int i;
+
+ if (group != NULL && find_group(ctx, group->name) == NULL) {
+ mrp_list_append(&ctx->cmd_groups, &group->hook);
+
+ catchall = NULL;
+ for (i = 0, cmd = group->commands; i < group->ncommand; i++, cmd++) {
+ if (cmd->flags & MRP_CONSOLE_CATCHALL) {
+ if (catchall == NULL)
+ catchall = cmd;
+ else
+ mrp_log_warning("Console group '%s' has multiple "
+ "catch-all commands: (%s, %s).",
+ group->name, catchall->name, cmd->name);
+ }
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_console_del_group(mrp_context_t *ctx, mrp_console_group_t *group)
+{
+ if (group != NULL && find_group(ctx, group->name) == group) {
+ mrp_list_delete(&group->hook);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_console_add_core_group(mrp_console_group_t *group)
+{
+ mrp_console_cmd_t *cmd, *catchall;
+ int i;
+
+ if (group != NULL && find_group(NULL, group->name) == NULL) {
+ mrp_list_append(&core_groups, &group->hook);
+
+ catchall = NULL;
+ for (i = 0, cmd = group->commands; i < group->ncommand; i++, cmd++) {
+ if (cmd->flags & MRP_CONSOLE_CATCHALL) {
+ if (catchall == NULL)
+ catchall = cmd;
+ else
+ mrp_log_warning("Console group '%s' has multiple "
+ "catch-all commands: (%s, %s).",
+ group->name, catchall->name, cmd->name);
+ }
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_console_del_core_group(mrp_console_group_t *group)
+{
+ if (group != NULL && find_group(NULL, group->name) == group) {
+ mrp_list_delete(&group->hook);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void console_grab_output(console_t *c)
+{
+ int ofd = fileno(stdout);
+ int efd = fileno(stderr);
+ int blk;
+
+ if (c->ofd == -1 && c->pout[RFD] != -1) {
+ blk = fcntl(ofd, F_GETFL, 0);
+ c->oblk = (blk > 0 && (blk & O_NONBLOCK));
+ blk = fcntl(efd, F_GETFL, 0);
+ c->eblk = (blk > 0 && (blk & O_NONBLOCK));
+
+ c->ofd = dup(ofd);
+ dup2(c->pout[WFD], ofd);
+ fcntl(c->pout[RFD], F_SETFL, O_NONBLOCK);
+
+ c->efd = dup(efd);
+ dup2(c->pout[WFD], efd);
+ fcntl(c->pout[WFD], F_SETFL, O_NONBLOCK);
+ }
+}
+
+
+static void console_release_output(console_t *c)
+{
+ int ofd = fileno(stdout);
+ int efd = fileno(stderr);
+
+ if (c->ofd >= 0) {
+ dup2(c->ofd, ofd);
+ c->ofd = -1;
+ fcntl(ofd, F_SETFL, c->oblk);
+ }
+
+ if (c->efd >= 0) {
+ dup2(c->efd, efd);
+ c->efd = -1;
+ fcntl(efd, F_SETFL, c->eblk);
+ }
+}
+
+
+static int console_read_output(console_t *c, void *buf, size_t size)
+{
+ return read(c->pout[RFD], buf, size);
+}
+
+
+static void console_flush_output(console_t *c, int copy_orig)
+{
+ char data[1024];
+ int size;
+
+ fflush(stdout);
+ fflush(stderr);
+
+ while ((size = console_read_output(c, data, sizeof(data))) > 0) {
+ if (copy_orig && c->ofd >= 0)
+ dprintf(c->ofd, "%*.*s", size, size, data);
+ mrp_console_printf((mrp_console_t *)c, "%*.*s", size, size, data);
+ }
+}
+
+
+static char *raw_argument(char *raw, const char *grp, const char *cmd)
+{
+#define SKIP_WHITESPACE(_p) \
+ while (*_p == ' ' || *_p == '\t') \
+ _p++
+
+#define SKIP_PREFIX(_p, _prfx) do { \
+ int _l = strlen(_prfx); \
+ \
+ if (!strncmp(_p, _prfx, _l) && (_p[_l] == ' ' || _p[_l] == '\t')) \
+ _p += _l; \
+ } while (0)
+
+ while (*raw == '/')
+ raw++;
+
+ SKIP_WHITESPACE(raw);
+ SKIP_PREFIX(raw, grp);
+ SKIP_WHITESPACE(raw);
+ SKIP_PREFIX(raw, cmd);
+
+ return raw;
+
+#undef SKIP_WHITESPACE
+#undef SKIP_PREFIX
+}
+
+
+static ssize_t input_evt(mrp_console_t *mc, void *buf, size_t size)
+{
+ console_t *c = (console_t *)mc;
+ mrp_console_group_t *grp;
+ mrp_console_cmd_t *cmd;
+ char *args[MRP_CFG_MAXARGS];
+ int argc;
+ char **argv, *raw;
+ int len, fallback;
+
+ /*
+ * parse the given command to tokens
+ */
+
+ len = size;
+ strncpy(c->in.buf, buf, len);
+ c->in.buf[len++] = '\n';
+ c->in.buf[len] = '\0';
+
+ c->in.token = c->in.buf;
+ c->in.out = c->in.buf;
+ c->in.next = c->in.buf;
+ c->in.in = c->in.buf + len;
+ c->in.line = 1;
+ c->in.error = 0;
+ *c->in.in = '\0';
+
+ argv = args + 2;
+ argc = get_next_line(&c->in, argv, MRP_ARRAY_SIZE(args) - 2);
+ grp = c->grp;
+ cmd = NULL;
+
+ /*
+ * Notes: Uhmmkay... so this will need to get replaced eventually with
+ * decent input processing.
+ */
+
+ if (argc < 0) {
+ fprintf(c->stderr, "failed to parse command: '%.*s'\n",
+ (int)size, (char *)buf);
+ return -1;
+ }
+ else if (argc == 0)
+ goto prompt;
+
+
+ /*
+ * take care of common top-level commands (exit, help)
+ */
+
+ grp = find_group(c->ctx, "");
+ cmd = find_command(grp, argv[0], NULL);
+
+ if (cmd != NULL) {
+ argv[-1] = "";
+ argv--;
+ argc++;
+ goto execute;
+ }
+
+
+ /*
+ * take care of group and command mode selection
+ */
+
+ if (argc == 1) {
+ if (c->grp == NULL) {
+ c->grp = find_group(c->ctx, argv[0]);
+
+ if (c->grp != NULL)
+ goto prompt;
+ }
+ else {
+ if (argv[0][0] == '/') {
+ grp = find_group(c->ctx, argv[0]);
+
+ if (grp != NULL) {
+ c->grp = grp;
+ goto prompt;
+ }
+ else if (argv[0][1] == '\0') {
+ c->grp = NULL;
+ c->cmd = NULL;
+ goto prompt;
+ }
+ else
+ goto unknown_command;
+ }
+
+ if (c->cmd == NULL) {
+ cmd = find_command(c->grp, argv[0], &fallback);
+
+ if (cmd != NULL &&
+ (cmd->flags & MRP_CONSOLE_SELECTABLE) && !fallback) {
+ c->cmd = cmd;
+ goto prompt;
+ }
+ }
+ }
+ }
+
+
+ /*
+ * take care of commands while in group or command mode
+ */
+
+ if (c->grp != NULL && *argv[0] != '/') {
+ if (c->cmd != NULL) {
+ grp = c->grp;
+ cmd = c->cmd;
+ argv[-2] = grp->name;
+ argv[-1] = (char *)cmd->name;
+ argv -= 2;
+ argc += 2;
+ }
+ else {
+ grp = c->grp;
+ cmd = find_command(grp, argv[0], NULL);
+
+ if (cmd == NULL)
+ goto unknown_command;
+
+ argv[-1] = grp->name;
+ argv--;
+ argc++;
+ }
+
+ goto execute;
+ }
+
+ /*
+ * take care of commands while at the top-level
+ */
+
+ if (argc > 1) {
+ grp = find_group(c->ctx, argv[0]);
+ cmd = find_command(grp, argv[1], NULL);
+ }
+
+ execute:
+ if (cmd != NULL) {
+ console_grab_output(c);
+
+ clearerr(stdout);
+ clearerr(stderr);
+
+ MRP_CONSOLE_BUSY(mc, {
+ if (cmd->flags & MRP_CONSOLE_RAWINPUT) {
+ raw = raw_argument(buf, grp->name, cmd->name);
+ cmd->raw(mc, grp->user_data, grp->name, cmd->name, raw);
+ }
+ else
+ cmd->tok(mc, grp->user_data, argc, argv);
+ });
+
+ /*
+ * Although our watch for c->pout[RFD]/output_cb should take
+ * care of flushing any output over to the console, since we
+ * know there is very probably pending output we might as well
+ * take care of proxying it right away...
+ */
+ console_flush_output(c, TRUE);
+
+ console_release_output(c);
+ }
+ else {
+ unknown_command:
+ fprintf(mc->stderr, "invalid command '%.*s'\n", (int)size, (char *)buf);
+ }
+
+ prompt:
+ if (mc->check_destroy(mc))
+ return size;
+
+ fflush(mc->stdout);
+ fflush(mc->stderr);
+
+ mrp_set_console_prompt(mc);
+
+ return size;
+}
+
+
+static void disconnected_evt(mrp_console_t *c, int error)
+{
+ mrp_log_info("Console %p has been disconnected (error: %d).", c, error);
+}
+
+
+static ssize_t complete_evt(mrp_console_t *c, void *input, size_t isize,
+ char **completions, size_t csize)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(input);
+ MRP_UNUSED(isize);
+ MRP_UNUSED(completions);
+ MRP_UNUSED(csize);
+
+ return 0;
+}
+
+
+/*
+ * stream-based console I/O
+ */
+
+static ssize_t cookie_write(void *cptr, const char *buf, size_t size)
+{
+ console_t *c = (console_t *)cptr;
+ ssize_t ssize;
+
+ if (c->destroyed)
+ return size;
+
+ MRP_CONSOLE_BUSY(c, {
+ ssize = c->req.write((mrp_console_t *)c, (char *)buf, size);
+ });
+
+ return ssize;
+}
+
+
+static int cookie_close(void *cptr)
+{
+ MRP_UNUSED(cptr);
+
+ return 0;
+}
+
+
+static FILE *console_fopen(mrp_console_t *mc)
+{
+ static cookie_io_functions_t io_func = {
+ .read = NULL,
+ .write = cookie_write,
+ .seek = NULL,
+ .close = cookie_close
+ };
+
+ return fopencookie((void *)mc, "w", io_func);
+}
+
+
+/*
+ * builtin console commands
+ */
+
+#include "console-command.c"
+
+static void register_commands(mrp_context_t *ctx)
+{
+ mrp_console_add_group(ctx, &builtin_cmd_group);
+}
+
+
+static void unregister_commands(mrp_context_t *ctx)
+{
+ mrp_console_del_group(ctx, &builtin_cmd_group);
+}
+
+
+/*
+ * XXX TODO Verbatim copy of config.c tokenizer. Separate this out
+ * to common (maybe common/text-utils.c), generalize and
+ * clean it up.
+ */
+
+#define MRP_START_COMMENT '#'
+
+static char *get_next_token(input_t *in);
+
+static int get_next_line(input_t *in, char **args, size_t size)
+{
+ char *token;
+ int narg;
+
+ narg = 0;
+ while ((token = get_next_token(in)) != NULL && narg < (int)size) {
+ if (in->error)
+ return -1;
+
+ if (token[0] != '\n')
+ args[narg++] = token;
+ else {
+ if (*args[0] != MRP_START_COMMENT && narg && *args[0] != '\n')
+ return narg;
+ else
+ narg = 0;
+ }
+ }
+
+ if (in->error)
+ return -1;
+
+ if (narg >= (int)size) {
+ mrp_log_error("Too many tokens on line %d of %s.",
+ in->line - 1, in->file);
+ return -1;
+ }
+ else {
+ if (*args[0] != MRP_START_COMMENT && *args[0] != '\n')
+ return narg;
+ else
+ return 0;
+ }
+}
+
+
+static inline void skip_whitespace(input_t *in)
+{
+ while ((*in->out == ' ' || *in->out == '\t') && in->out < in->in)
+ in->out++;
+}
+
+
+static char *get_next_token(input_t *in)
+{
+ int diff, size;
+ int quote, quote_line;
+ char *p, *q;
+
+ /*
+ * Newline:
+ *
+ * If the previous token was terminated by a newline,
+ * take care of properly returning and administering
+ * the newline token here.
+ */
+
+ if (in->next_newline) {
+ in->next_newline = FALSE;
+ in->was_newline = TRUE;
+ in->line++;
+
+ return "\n";
+ }
+
+
+ /*
+ * if we just finished a line, discard all old data/tokens
+ */
+
+ if (*in->token == '\n' || in->was_newline) {
+ diff = in->out - in->buf;
+ size = in->in - in->out;
+ memmove(in->buf, in->out, size);
+ in->out -= diff;
+ in->in -= diff;
+ in->next = in->buf;
+ *in->in = '\0';
+ }
+
+ if (in->out >= in->in)
+ return NULL;
+
+ skip_whitespace(in);
+
+ quote = FALSE;
+ quote_line = 0;
+
+ p = in->out;
+ q = in->next;
+ in->token = q;
+
+ while (p < in->in) {
+ /*printf("[%c]\n", *p == '\n' ? '.' : *p);*/
+ switch (*p) {
+ /*
+ * Quoting:
+ *
+ * If we're not within a quote, mark a quote started.
+ * Otherwise if quote matches, close quoting. Otherwise
+ * copy the quoted quote verbatim.
+ */
+ case '\'':
+ case '\"':
+ if (!quote) {
+ quote = *p++;
+ quote_line = in->line;
+ }
+ else {
+ if (*p == quote) {
+ quote = FALSE;
+ quote_line = 0;
+ p++;
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Whitespace:
+ *
+ * If we're quoting, copy verbatim. Otherwise mark the end
+ * of the token.
+ */
+ case ' ':
+ case '\t':
+ if (quote)
+ *q++ = *p++;
+ else {
+ p++;
+ *q++ = '\0';
+
+ in->out = p;
+ in->next = q;
+
+ return in->token;
+ }
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Escaping:
+ *
+ * If the last character in the input, copy verbatim.
+ * Otherwise if it escapes a '\n', skip both. Otherwise
+ * copy the escaped character verbatim.
+ */
+ case '\\':
+ if (p < in->in - 1) {
+ p++;
+ if (*p != '\n')
+ *q++ = *p++;
+ else {
+ p++;
+ in->line++;
+ in->out = p;
+ skip_whitespace(in);
+ p = in->out;
+ }
+ }
+ else
+ *q++ = *p++;
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Newline:
+ *
+ * We don't allow newlines to be quoted. Otherwise
+ * if the token is not the newline itself, we mark
+ * the next token to be newline and return the token
+ * it terminated.
+ */
+ case '\n':
+ if (quote) {
+ mrp_log_error("%s:%d: Unterminated quote (%c) started "
+ "on line %d.", in->file, in->line, quote,
+ quote_line);
+ in->error = TRUE;
+
+ return NULL;
+ }
+ else {
+ *q = '\0';
+ p++;
+
+ in->out = p;
+ in->next = q;
+
+ if (in->token == q) {
+ in->line++;
+ in->was_newline = TRUE;
+ return "\n";
+ }
+ else {
+ in->next_newline = TRUE;
+ return in->token;
+ }
+ }
+ break;
+
+ /*
+ * CR: just ignore it
+ */
+ case '\r':
+ p++;
+ break;
+
+ default:
+ *q++ = *p++;
+ in->was_newline = FALSE;
+ }
+ }
+
+ *q = '\0';
+ in->out = p;
+ in->in = q;
+
+ return in->token;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CONSOLE_H__
+#define __MURPHY_CONSOLE_H__
+
+#include <murphy/common/list.h>
+#include <murphy/common/msg.h>
+#include <murphy/core/context.h>
+
+#include <murphy/core/console-command.h>
+
+
+
+/*
+ * console requests
+ *
+ * Console request correspond to top-down event propagation in the console
+ * communication stack. These requests are made by the core console to the
+ * underlying actual console implementation, typically either as a result
+ * of calls to the console abstraction layer, or in reponse to requests
+ * (ie. input) coming from the actual console implementation.
+ */
+
+typedef struct {
+ /** Deliver a buffer of data to the given console. */
+ ssize_t (*write)(mrp_console_t *c, void *buf, size_t size);
+ /** Console being closed, close the backend (do not release memory yet). */
+ void (*close)(mrp_console_t *c);
+ /** Console has been destroyed, release resources allocated by backend. */
+ void (*free)(void *data);
+ /** Set the prompt shown to the user at the console. */
+ void (*set_prompt)(mrp_console_t *c, const char *prompt);
+} mrp_console_req_t;
+
+
+/*
+ * console events
+ *
+ * Console events correspond to bottom-up event propagation in the console
+ * communication stack. These callbacks are made by the console backend to
+ * the core console to inform about relevant console events, such as new
+ * console input or disconnect by the peer.
+ */
+
+typedef struct {
+ /** New input available from console. */
+ ssize_t (*input)(mrp_console_t *c, void *buf, size_t size);
+ /** Peer has disconnected from the console. */
+ void (*disconnected)(mrp_console_t *c, int error);
+ /** Generate possible completions for the given input. */
+ ssize_t (*complete)(mrp_console_t *c, void *input, size_t insize,
+ char **completions, size_t csize);
+} mrp_console_evt_t;
+
+
+#define MRP_CONSOLE_PUBLIC_FIELDS \
+ mrp_context_t *ctx; \
+ mrp_console_req_t req; \
+ mrp_console_evt_t evt; \
+ int (*check_destroy)(mrp_console_t *c); \
+ FILE *stdout; \
+ FILE *stderr; \
+ void *backend_data; \
+ int busy; \
+ int destroyed : 1; \
+ int preserve : 1 /* the Kludge of Death, Sir Robin... */
+
+struct mrp_console_s {
+ MRP_CONSOLE_PUBLIC_FIELDS;
+};
+
+
+/**
+ * Macro to mark a console busy while running a block of code.
+ *
+ * The backend needs to make sure the console is not freed while any console
+ * request or event callback function is active. Similarly, the backend needs
+ * to check if the console has been marked for destruction whenever an event
+ * callback returns and trigger destruction if it is necessary and possible
+ * (ie. the above criterium of not being active is fullfilled).
+ *
+ * These are the easiest to accomplish using the provided MRP_CONSOLE_BUSY
+ * macro and the check_destroy callback member provided by mrp_console_t.
+ *
+ * 1) Use the provided MRP_CONSOLE_BUSY macro to enclose al blocks of
+ * code that invoke event callbacks. Do not do a return directly
+ * from within the enclosed call blocks, rather just set a flag
+ * within the block, check it after the block and do the return
+ * there if necessary.
+ *
+ * 2) Call mrp_console_t->check_destroy after any call to an console
+ * event callback. check_destroy will check for any pending destroy
+ * request and perform the actual destruction if it is both necessary
+ * and possible. If the console has been left intact, check_destroy
+ * returns FALSE. However, if the console has been destroyed and freed
+ * it returns TRUE, in which case the caller must not attempt to use
+ * or dereference the console any more.
+ */
+
+#ifndef __MRP_CONSOLE_DISABLE_CODE_CHECK__
+# define W mrp_log_error
+# define __CONSOLE_CHK_BLOCK(...) do { \
+ static int __checked = FALSE, __warned = FALSE; \
+ \
+ if (MRP_UNLIKELY(!__checked)) { \
+ __checked = TRUE; \
+ if (MRP_UNLIKELY(!__warned && \
+ strstr(#__VA_ARGS__, "return") != NULL)) { \
+ W("********************* WARNING *********************"); \
+ W("* You seem to directly do a return from a block *"); \
+ W("* of code protected by MRP_CONSOLE_BUSY. Are *"); \
+ W("* you absolutely sure you know what you are doing *"); \
+ W("* and that you are also doing it correctly ? *"); \
+ W("***************************************************"); \
+ W("The suspicious code block is located at: "); \
+ W(" %s@%s:%d", __FUNCTION__, __FILE__, __LINE__); \
+ W("and it looks like this:"); \
+ W("---------------------------------------------"); \
+ W("%s", #__VA_ARGS__); \
+ W("---------------------------------------------"); \
+ W("If you understand what MRP_CONSOLE_BUSY does"); \
+ W("and how, and you are sure about the correctness of"); \
+ W("your code you can disable this error message by"); \
+ W("#defining __MRP_CONSOLE_DISABLE_CODE_CHECK__"); \
+ W("when compiling %s.", __FILE__); \
+ __warned = TRUE; \
+ } \
+ } \
+ } while (0)
+#else
+# define __CONSOLE_CHK_BLOCK(...) do { } while (0)
+#endif
+
+#define MRP_CONSOLE_BUSY(c, ...) do { \
+ __CONSOLE_CHK_BLOCK(__VA_ARGS__); \
+ (c)->busy++; \
+ __VA_ARGS__ \
+ (c)->busy--; \
+ } while (0)
+
+
+/** Create a new console instance. */
+mrp_console_t *mrp_create_console(mrp_context_t *ctx, mrp_console_req_t *req,
+ void *backend_data);
+
+/** Close and mark a console for destruction. */
+void mrp_destroy_console(mrp_console_t *mc);
+
+/** Send (printf-compatible) formatted output to a console. */
+void mrp_console_printf(mrp_console_t *mc, const char *fmt, ...);
+
+/** Send (vprintf-compatible) formatted output to a console. */
+void mrp_console_vprintf(mrp_console_t *mc, const char *fmt, va_list ap);
+
+/** Set the prompt of a console. */
+void mrp_set_console_prompt(mrp_console_t *mc);
+
+#endif /* __MURPHY_CONSOLE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/core/context.h>
+#include <murphy/core/console-priv.h>
+#include <murphy/core/domain.h>
+
+mrp_context_t *mrp_context_create(void)
+{
+ mrp_context_t *c;
+
+ if ((c = mrp_allocz(sizeof(*c))) != NULL) {
+ mrp_list_init(&c->plugins);
+ console_setup(c);
+ domain_setup(c);
+
+ mrp_list_init(&c->auth);
+
+
+ if ((c->ml = mrp_mainloop_create()) == NULL) {
+ mrp_log_error("Failed to create mainloop.");
+ mrp_free(c);
+ c = NULL;
+ }
+ }
+
+ return c;
+}
+
+
+void mrp_context_destroy(mrp_context_t *c)
+{
+ if (c != NULL) {
+ console_cleanup(c);
+ mrp_mainloop_destroy(c->ml);
+ mrp_free(c);
+ }
+
+
+}
+
+
+void mrp_context_setstate(mrp_context_t *c, mrp_context_state_t state)
+{
+ c->state = state;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CONTEXT_H__
+#define __MURPHY_CONTEXT_H__
+
+#include <stdbool.h>
+
+typedef struct mrp_context_s mrp_context_t;
+
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/resolver/resolver.h>
+
+
+typedef enum {
+ MRP_STATE_INITIAL = 0,
+ MRP_STATE_LOADING,
+ MRP_STATE_STARTING,
+ MRP_STATE_RUNNING,
+ MRP_STATE_STOPPING
+} mrp_context_state_t;
+
+
+struct mrp_context_s {
+ /* logging settings, path configuration, etc. */
+ int log_mask; /* what to log */
+ const char *log_target; /* and where to log to */
+
+ const char *config_file; /* configuration file */
+ const char *config_dir; /* plugin configuration directory */
+ const char *plugin_dir; /* plugin directory */
+ bool foreground; /* whether to stay in foreground*/
+
+ char *resolver_ruleset; /* resolver ruleset file */
+
+ const char *blacklist_plugins; /* blacklisted plugins */
+ const char *blacklist_builtin; /* blacklisted builtin plugins */
+ const char *blacklist_dynamic; /* blacklisted dynamic plugins */
+ const char *whitelist_plugins; /* whitelisted plugins */
+ const char *whitelist_builtin; /* whitelisted builtin plugins */
+ const char *whitelist_dynamic; /* whitelisted dynamic plugins */
+ bool disable_runtime_load; /* disallow post-startup loading */
+ bool disable_console; /* disable murphy console */
+
+ /* actual runtime context data */
+ int state; /* context/daemon state */
+ mrp_mainloop_t *ml; /* mainloop */
+ mrp_list_hook_t plugins; /* list of loaded plugins */
+ mrp_event_bus_t *plugin_bus; /* bus for plugin events */
+ mrp_event_bus_t *daemon_bus; /* bus for daemon events */
+ mrp_list_hook_t cmd_groups; /* console command groups */
+ mrp_list_hook_t consoles; /* active consoles */
+ mrp_resolver_t *r; /* resolver context */
+ void *lua_state; /* state for Lua bindings */
+ mrp_list_hook_t auth; /* authenticator backends */
+
+ /*
+ * Hmm, this is not very nice. Most of the domain handling code (in
+ * practice all) used to live in the domain-control plugin. To avoid
+ * loading order dependencies on plugin-domain-control we now started
+ * collecting registered handlers of proxied functions here. Calls by
+ * the core to proxied functions of domain controllers and by domain-
+ * controllers to the core are still handled in the domain-control
+ * plugin (and in the domain-controller client library).
+ *
+ * It would be perhaps the cleanest not to have a domain-controller
+ * specific function export mechanism at all. Instead the various
+ * import/export mechanisms (at least plugins, resolver, and this) should
+ * be replaced by / built on a single core implementation that is flexible
+ * enough to handle all the needs of all these.
+ */
+
+ mrp_list_hook_t domain_methods; /* functions for domain controllers */
+ void *domain_invoke; /* domain invoke handler */
+ void *domain_data; /* domain invoke handler data */
+};
+
+/** Create a new murphy context. */
+mrp_context_t *mrp_context_create(void);
+
+/** Destroy an existing murphy context. */
+void mrp_context_destroy(mrp_context_t *c);
+
+/** Set the context state to the given state. */
+void mrp_context_setstate(mrp_context_t *c, mrp_context_state_t state);
+
+#endif /* __MURPHY_CONTEXT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CORE_DOMAIN_TYPES_H__
+#define __MURPHY_CORE_DOMAIN_TYPES_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/msg.h>
+
+MRP_CDECL_BEGIN
+
+
+/*
+ * passable data types to/from domain controllers
+ */
+typedef enum {
+ MRP_DOMCTL_END = MRP_MSG_FIELD_INVALID,
+ MRP_DOMCTL_STRING = MRP_MSG_FIELD_STRING,
+ MRP_DOMCTL_INTEGER = MRP_MSG_FIELD_INT32,
+ MRP_DOMCTL_UNSIGNED = MRP_MSG_FIELD_UINT32,
+ MRP_DOMCTL_DOUBLE = MRP_MSG_FIELD_DOUBLE,
+ MRP_DOMCTL_BOOL = MRP_MSG_FIELD_BOOL,
+ MRP_DOMCTL_UINT8 = MRP_MSG_FIELD_UINT8,
+ MRP_DOMCTL_INT8 = MRP_MSG_FIELD_INT8,
+ MRP_DOMCTL_UINT16 = MRP_MSG_FIELD_UINT16,
+ MRP_DOMCTL_INT16 = MRP_MSG_FIELD_INT16,
+ MRP_DOMCTL_UINT32 = MRP_MSG_FIELD_UINT32,
+ MRP_DOMCTL_INT32 = MRP_MSG_FIELD_INT32,
+ MRP_DOMCTL_UINT64 = MRP_MSG_FIELD_UINT64,
+ MRP_DOMCTL_INT64 = MRP_MSG_FIELD_INT64,
+
+#define MRP_DOMCTL_ARRAY(_type) MRP_MSG_FIELD_ARRAY_OF(_type)
+#define MRP_DOMCTL_IS_ARRAY(_type) MRP_MSG_FIELD_IS_ARRAY(_type)
+#define MRP_DOMCTL_ARRAY_TYPE(_type) MRP_MSG_FIELD_ARRAY_TYPE(_type)
+} mrp_domctl_type_t;
+
+
+/*
+ * a single data value passed to/from a domain controller
+ */
+
+typedef struct {
+ mrp_domctl_type_t type; /* data type */
+ union {
+ /* these are usable both in DB operations and proxied invocations */
+ const char *str; /* MRP_DOMCTL_STRING */
+ uint32_t u32; /* MRP_DOMCTL_{UNSIGNED,UINT32} */
+ int32_t s32; /* MRP_DOMCTL_{INTEGER,INT32} */
+ double dbl; /* MRP_DOMCTL_DOUBLE */
+ /* these are only usable in proxied invocations */
+ int bln; /* MRP_DOMCTL_BOOL */
+ uint8_t u8; /* MRP_DOMCTL_UINT8 */
+ int8_t s8; /* MRP_DOMCTL_INT8 */
+ uint16_t u16; /* MRP_DOMCTL_UINT16 */
+ int16_t s16; /* MRP_DOMCTL_INT16 */
+ uint64_t u64; /* MRP_DOMCTL_UINT64 */
+ int64_t s64; /* MRP_DOMCTL_INT64 */
+ void *arr; /* MRP_DOMCTL_ARRAY(*) */
+ };
+ uint32_t size; /* size for arrays */
+} mrp_domctl_value_t;
+
+
+/*
+ * proxied invokation errors
+ */
+
+typedef enum {
+ MRP_DOMAIN_OK = 0, /* no errors */
+ MRP_DOMAIN_NOTFOUND, /* domain not found */
+ MRP_DOMAIN_NOMETHOD, /* call domain method not found */
+ MRP_DOMAIN_FAILED, /* called method remotely failed */
+} mrp_domain_error_t;
+
+/* Type for a proxied invocation argument. */
+typedef mrp_domctl_value_t mrp_domctl_arg_t;
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_CORE_DOMAIN_TYPES_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+
+#include <murphy/core/context.h>
+#include <murphy/core/domain.h>
+
+typedef struct {
+ mrp_list_hook_t hook; /* to list of registered methods */
+ char *name; /* method name */
+ int max_out; /* max returned arguments */
+ mrp_domain_invoke_cb_t cb; /* actual callback */
+ void *user_data; /* callback user data */
+} method_t;
+
+
+void domain_setup(mrp_context_t *ctx)
+{
+ mrp_list_init(&ctx->domain_methods);
+}
+
+
+int mrp_set_domain_invoke_handler(mrp_context_t *ctx,
+ mrp_domain_invoke_handler_t handler,
+ void *handler_data)
+{
+ if (ctx->domain_invoke != NULL)
+ return FALSE;
+
+ ctx->domain_invoke = handler;
+ ctx->domain_data = handler_data;
+
+ return TRUE;
+}
+
+
+int mrp_register_domain_methods(mrp_context_t *ctx,
+ mrp_domain_method_def_t *defs, size_t ndef)
+{
+ mrp_domain_method_def_t *def;
+ method_t *m;
+ size_t i;
+
+ for (i = 0, def = defs; i < ndef; i++, def++) {
+ m = mrp_allocz(sizeof(*m));
+
+ if (m == NULL)
+ return FALSE;
+
+ mrp_list_init(&m->hook);
+
+ m->name = mrp_strdup(def->name);
+ m->max_out = def->max_out;
+ m->cb = def->cb;
+ m->user_data = def->user_data;
+
+ if (m->name == NULL) {
+ mrp_free(m);
+ return FALSE;
+ }
+
+ mrp_list_append(&ctx->domain_methods, &m->hook);
+ }
+
+ return TRUE;
+}
+
+
+static method_t *find_method(mrp_context_t *ctx, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ method_t *m;
+
+ mrp_list_foreach(&ctx->domain_methods, p, n) {
+ m = mrp_list_entry(p, typeof(*m), hook);
+
+ if (!strcmp(m->name, name))
+ return m;
+ }
+
+ return NULL;
+}
+
+
+int mrp_lookup_domain_method(mrp_context_t *ctx, const char *name,
+ mrp_domain_invoke_cb_t *cb, int *max_out,
+ void **user_data)
+{
+ method_t *m;
+
+ m = find_method(ctx, name);
+
+ if (m == NULL)
+ return FALSE;
+
+ *cb = m->cb;
+ *max_out = m->max_out;
+ *user_data = m->user_data;
+
+ return TRUE;
+}
+
+
+int mrp_invoke_domain(mrp_context_t *ctx, const char *domain,
+ const char *method, int narg, mrp_domctl_arg_t *args,
+ mrp_domain_return_cb_t return_cb, void *user_data)
+{
+ mrp_domain_invoke_handler_t handler = ctx->domain_invoke;
+ void *handler_data = ctx->domain_data;
+
+ if (handler == NULL)
+ return FALSE;
+
+ return handler(handler_data, domain,
+ method, narg, args, return_cb, user_data);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CORE_DOMAIN__
+#define __MURPHY_CORE_DOMAIN__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/msg.h>
+
+#include <murphy/core/context.h>
+#include <murphy/core/domain-types.h>
+
+MRP_CDECL_BEGIN
+
+/* Type for a proxied invocation handler. */
+typedef int (*mrp_domain_invoke_cb_t)(uint32_t narg, mrp_domctl_arg_t *args,
+ uint32_t *nout, mrp_domctl_arg_t *outs,
+ void *user_data);
+
+/* Type for a proxied invocation return/reply handler. */
+typedef void (*mrp_domain_return_cb_t)(int error, int retval, int narg,
+ mrp_domctl_arg_t *args, void *user_data);
+
+typedef struct {
+ char *name; /* method name */
+ int max_out; /* max. number of return arguments */
+ mrp_domain_invoke_cb_t cb; /* handler callback */
+ void *user_data; /* opaque callback user data */
+} mrp_domain_method_def_t;
+
+
+
+/* Type for handling proxied invocation to domain controllers. */
+typedef int (*mrp_domain_invoke_handler_t)(void *handler_data, const char *id,
+ const char *method, int narg,
+ mrp_domctl_arg_t *args,
+ mrp_domain_return_cb_t return_cb,
+ void *user_data);
+
+/* Initialize domain-specific context parts. */
+void domain_setup(mrp_context_t *ctx);
+
+/* Register a domain method. */
+int mrp_register_domain_methods(mrp_context_t *ctx,
+ mrp_domain_method_def_t *defs, size_t ndef);
+
+/* Find a registered domain method. */
+int mrp_lookup_domain_method(mrp_context_t *ctx, const char *method,
+ mrp_domain_invoke_cb_t *cb, int *max_out,
+ void **user_data);
+
+/* Invoke the named method of the specified domain. */
+int mrp_invoke_domain(mrp_context_t *ctx, const char *domain, const char *method,
+ int narg, mrp_domctl_arg_t *args,
+ mrp_domain_return_cb_t return_cb, void *user_data);
+
+/* Set the domain invoke handler. */
+int mrp_set_domain_invoke_handler(mrp_context_t *ctx,
+ mrp_domain_invoke_handler_t handler,
+ void *handler_data);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_CORE_DOMAIN__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/msg.h>
+
+#include <murphy/core/event.h>
+
+
+/*
+ * an event definition
+ */
+
+typedef struct {
+ char *name; /* event name */
+ int id; /* associated event id */
+ mrp_list_hook_t watches; /* single-event watches */
+} event_def_t;
+
+
+/*
+ * an event watch
+ */
+
+struct mrp_event_watch_s {
+ mrp_list_hook_t hook; /* hook to event watch list */
+ mrp_list_hook_t purge; /* hook to deleted event list */
+ mrp_event_cb_t cb; /* notification callback */
+ mrp_event_mask_t events; /* events to watch */
+ void *user_data; /* opaque callback data */
+};
+
+
+static event_def_t events[MRP_EVENT_MAX]; /* event table */
+static int nevent; /* number of events */
+static int nemit; /* events being emitted */
+static mrp_list_hook_t watches; /* multi-event watches */
+static mrp_list_hook_t deleted; /* events deleted during emit */
+
+
+static int single_event(mrp_event_mask_t *mask);
+
+
+MRP_INIT static void init_watch_lists(void)
+{
+ int i;
+
+ for (i = 0; i < MRP_EVENT_MAX; i++)
+ mrp_list_init(&events[i].watches);
+
+ mrp_list_init(&watches);
+ mrp_list_init(&deleted);
+}
+
+
+mrp_event_watch_t *mrp_add_event_watch(mrp_event_mask_t *mask,
+ mrp_event_cb_t cb, void *user_data)
+{
+ mrp_event_watch_t *w;
+ event_def_t *def;
+ int id;
+
+ if (cb != NULL) {
+ w = mrp_allocz(sizeof(*w));
+
+ if (w != NULL) {
+ mrp_list_init(&w->hook);
+ mrp_list_init(&w->purge);
+ w->cb = cb;
+ w->user_data = user_data;
+ w->events = *mask;
+
+ id = single_event(mask);
+
+ if (id != MRP_EVENT_UNKNOWN) {
+ if (MRP_EVENT_UNKNOWN < id && id <= nevent) {
+ def = events + id;
+
+ if (def->name != NULL)
+ mrp_list_append(&def->watches, &w->hook);
+ else {
+ mrp_free(w);
+ w = NULL;
+ }
+ }
+ else {
+ mrp_free(w);
+ w = NULL;
+ }
+ }
+ else
+ mrp_list_append(&watches, &w->hook);
+ }
+ }
+ else
+ w = NULL;
+
+ return w;
+
+#if 0
+ if (MRP_EVENT_UNKNOWN < id && id <= nevent) {
+ def = events + id;
+
+ if (def->name != NULL && cb != NULL) {
+ w = mrp_allocz(sizeof(*w));
+
+ if (w != NULL) {
+ mrp_list_init(&w->hook);
+ mrp_list_init(&w->purge);
+ w->cb = cb;
+ w->user_data = user_data;
+
+ if (single_event(mask)) {
+ id = single_event(mask);
+
+ mrp_list_append(&def->watches, &w->hook);
+
+ return w;
+ }
+ }
+ }
+
+ return NULL;
+#endif
+}
+
+
+static void delete_watch(mrp_event_watch_t *w)
+{
+ mrp_list_delete(&w->hook);
+ mrp_list_delete(&w->purge);
+ mrp_free(w);
+}
+
+
+static void purge_deleted(void)
+{
+ mrp_event_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ mrp_list_foreach(&deleted, p, n) {
+ w = mrp_list_entry(p, typeof(*w), purge);
+ delete_watch(w);
+ }
+}
+
+
+void mrp_del_event_watch(mrp_event_watch_t *w)
+{
+ if (w != NULL) {
+ if (nemit > 0)
+ mrp_list_append(&deleted, &w->purge);
+ else
+ delete_watch(w);
+ }
+}
+
+
+int mrp_get_event_id(const char *name, int create)
+{
+ event_def_t *def;
+ int i;
+
+ for (i = MRP_EVENT_UNKNOWN + 1, def = events + i; i <= nevent; i++, def++) {
+ if (!strcmp(name, def->name))
+ return i;
+ }
+
+ if (create) {
+ if (nevent < MRP_EVENT_MAX - 1) {
+ def = events + 1 + nevent;
+ def->name = mrp_strdup(name);
+
+ if (def->name != NULL) {
+ mrp_list_init(&def->watches);
+ def->id = 1 + nevent++;
+
+ return def->id;
+ }
+ }
+ }
+
+ return MRP_EVENT_UNKNOWN;
+}
+
+
+const char *mrp_get_event_name(int id)
+{
+ event_def_t *def;
+
+ if (MRP_EVENT_UNKNOWN < id && id <= nevent) {
+ def = events + id;
+
+ return def->name;
+ }
+
+ return "<unknown event>";
+}
+
+
+int mrp_emit_event_msg(int id, mrp_msg_t *event_data)
+{
+ event_def_t *def;
+ mrp_list_hook_t *p, *n;
+ mrp_event_watch_t *w;
+
+ if (MRP_EVENT_UNKNOWN < id && id <= nevent) {
+ def = events + id;
+
+ if (def->name != NULL) {
+ nemit++;
+ mrp_msg_ref(event_data);
+
+ mrp_debug("emitting event 0x%x (%s)", def->id, def->name);
+#if 0
+ mrp_msg_dump(event_data, stdout);
+#endif
+
+ mrp_list_foreach(&def->watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+ w->cb(w, def->id, event_data, w->user_data);
+ }
+
+ mrp_list_foreach(&watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), hook);
+ if (mrp_test_event(&w->events, id))
+ w->cb(w, def->id, event_data, w->user_data);
+ }
+
+ nemit--;
+ mrp_msg_unref(event_data);
+ }
+
+ if (nemit <= 0)
+ purge_deleted();
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int mrp_emit_event(int id, ...)
+{
+ mrp_msg_t *msg;
+ uint16_t tag;
+ va_list ap;
+ int success;
+
+ va_start(ap, id);
+ tag = va_arg(ap, unsigned int);
+ if (tag != MRP_MSG_FIELD_INVALID)
+ msg = mrp_msg_createv(tag, ap);
+ else
+ msg = NULL;
+ va_end(ap);
+
+ success = mrp_emit_event_msg(id, msg);
+ mrp_msg_unref(msg);
+
+ return success;
+}
+
+
+mrp_event_mask_t *mrp_set_events(mrp_event_mask_t *mask, ...)
+{
+ va_list ap;
+ int id;
+
+ mrp_reset_event_mask(mask);
+
+ va_start(ap, mask);
+ while ((id = va_arg(ap, int)) != MRP_EVENT_UNKNOWN) {
+ mrp_add_event(mask, id);
+ }
+ va_end(ap);
+
+ return mask;
+}
+
+
+mrp_event_mask_t *mrp_set_named_events(mrp_event_mask_t *mask, ...)
+{
+ va_list ap;
+ const char *name;
+ int id;
+
+ mrp_reset_event_mask(mask);
+
+ va_start(ap, mask);
+ while ((name = va_arg(ap, const char *)) != NULL) {
+ id = mrp_lookup_event(name);
+
+ if (id != MRP_EVENT_UNKNOWN)
+ mrp_add_event(mask, id);
+ }
+ va_end(ap);
+
+ return mask;
+}
+
+
+static int event_count(mrp_event_mask_t *mask)
+{
+ uint64_t bits = *mask;
+
+ return __builtin_popcountll(bits);
+}
+
+
+static int lowest_bit(mrp_event_mask_t *mask)
+{
+ uint64_t bits = *mask;
+
+ return __builtin_ffs(bits);
+}
+
+
+static int single_event(mrp_event_mask_t *mask)
+{
+ if (event_count(mask) == 1)
+ return lowest_bit(mask);
+ else
+ return MRP_EVENT_UNKNOWN;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_EVENT_H__
+#define __MURPHY_EVENT_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/msg.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_EVENT_UNKNOWN 0
+#define MRP_EVENT_MAX 64
+
+
+/*
+ * event declarations
+ */
+
+typedef struct {
+ const char *name; /* event name */
+ int id; /* associated event id */
+} mrp_event_t;
+
+
+/*
+ * Convenience macro for autoregistering a table of events on startup.
+ */
+
+#define MRP_REGISTER_EVENTS(_event_table, ...) \
+ static mrp_event_t _event_table[] = { \
+ __VA_ARGS__, \
+ { .name = NULL } \
+ }; \
+ MRP_INIT static void register_events(void) \
+ { \
+ mrp_event_t *e; \
+ int i; \
+ \
+ for (i = 0, e = _event_table; e->name != NULL; i++, e++) { \
+ if (e->id != i) { \
+ mrp_log_error("%s:%d: misinitialized event table %s.", \
+ __FILE__, __LINE__, #_event_table); \
+ mrp_log_error("This can result from passing a mis" \
+ "initialized event table to the macro"); \
+ mrp_log_error("MRP_REGISTER_EVENT, ie. a table where " \
+ "id != index for some element)."); \
+ return; \
+ } \
+ \
+ e->id = mrp_register_event(e->name); \
+ \
+ if (e->id != MRP_EVENT_UNKNOWN) \
+ mrp_log_info("Event '%s' registered as 0x%x.", \
+ e->name, e->id); \
+ else \
+ mrp_log_error("Failed to register event '%s'.", \
+ e->name); \
+ } \
+ } \
+ struct __mrp_allow_trailing_semicolon
+
+
+typedef uint64_t mrp_event_mask_t;
+
+
+typedef struct mrp_event_watch_s mrp_event_watch_t;
+
+
+/** Event watch notification callback type. */
+typedef void (*mrp_event_cb_t)(mrp_event_watch_t *w, int id,
+ mrp_msg_t *event_data, void *user_data);
+
+/** Subscribe for an event. */
+mrp_event_watch_t *mrp_add_event_watch(mrp_event_mask_t *events,
+ mrp_event_cb_t cb,
+ void *user_data);
+
+/** Unsubscribe from an event. */
+void mrp_del_event_watch(mrp_event_watch_t *w);
+
+/** Get the id of the given event, create missing events is asked for. */
+int mrp_get_event_id(const char *name, int create);
+
+/** Get the name of the event corresponding to the given id. */
+const char *mrp_get_event_name(int id);
+
+/** Register a new event. */
+#define mrp_register_event(name) mrp_get_event_id(name, TRUE)
+
+/** Look up the id of an existing event. */
+#define mrp_lookup_event(name) mrp_get_event_id(name, FALSE)
+
+/** Emit an event with the given message. */
+int mrp_emit_event_msg(int id, mrp_msg_t *event_data);
+
+/** Emit an event with a message constructed from the given parameters. */
+int mrp_emit_event(int id, ...) MRP_NULLTERM;
+
+/** Initialize an event mask to be empty. */
+static inline void mrp_reset_event_mask(mrp_event_mask_t *mask)
+{
+ *mask = 0;
+}
+
+/** Turn on the bit corresponding to id in mask. */
+static inline void mrp_add_event(mrp_event_mask_t *mask, int id)
+{
+ *mask |= (1 << (id - 1));
+}
+
+/** Turn off the bit corresponding to id in mask. */
+static inline void mrp_del_event(mrp_event_mask_t *mask, int id)
+{
+ *mask &= ~(1 << (id - 1));
+}
+
+/** Test if the bit corresponding to id in mask is on. */
+static inline int mrp_test_event(mrp_event_mask_t *mask, int id)
+{
+ return *mask & (1 << (id - 1));
+}
+
+/** Turn on the bit corresponding to the named event in mask. */
+static inline int mrp_add_named_event(mrp_event_mask_t *mask, const char *name)
+{
+ int id = mrp_lookup_event(name);
+
+ if (id != 0) {
+ mrp_add_event(mask, id);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/** Turn off the bit corresponding to the named event in mask. */
+static inline int mrp_del_named_event(mrp_event_mask_t *mask, const char *name)
+{
+ int id = mrp_lookup_event(name);
+
+ if (id != 0) {
+ mrp_del_event(mask, id);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/** Test the bit corresponding to the named event in mask. */
+static inline int mrp_test_named_event(mrp_event_mask_t *mask, const char *name)
+{
+ int id = mrp_lookup_event(name);
+
+ if (id != 0)
+ return mrp_test_event(mask, id);
+ else
+ return FALSE;
+}
+
+/** Set up the given mask with the given event ids (terminated by
+ MRP_EVENT_UNKNOWN). */
+mrp_event_mask_t *mrp_set_events(mrp_event_mask_t *mask, ...);
+
+/** Set up the given mask with the given named events (terminated by
+ NULL). */
+mrp_event_mask_t *mrp_set_named_events(mrp_event_mask_t *mask, ...);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_EVENT_H__ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+/*
+ * Lua bitwise operations
+ */
+
+static int bitwise_lua_and(lua_State *L);
+static int bitwise_lua_or(lua_State *L);
+static int bitwise_lua_xor(lua_State *L);
+static int bitwise_lua_neg(lua_State *L);
+
+static int bitwise_lua_and(lua_State *L)
+{
+ int narg = lua_gettop(L);
+ int offs, i, v;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ offs = 2;
+ break;
+ default:
+ offs = 1;
+ break;
+ }
+
+ v = lua_tointeger(L, offs);
+ for (i = offs + 1; i <= narg; i++)
+ v &= lua_tointeger(L, i);
+
+ lua_pushinteger(L, v);
+ return 1;
+}
+
+
+static int bitwise_lua_or(lua_State *L)
+{
+ int narg = lua_gettop(L);
+ int offs, i, v;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ offs = 2;
+ break;
+ default:
+ offs = 1;
+ break;
+ }
+
+ v = lua_tointeger(L, offs);
+ for (i = offs + 1; i <= narg; i++)
+ v |= lua_tointeger(L, i);
+
+ lua_pushinteger(L, v);
+ return 1;
+}
+
+
+static int bitwise_lua_xor(lua_State *L)
+{
+ int narg = lua_gettop(L);
+ int offs, i, v;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ offs = 2;
+ break;
+ default:
+ offs = 1;
+ break;
+ }
+
+ v = lua_tointeger(L, offs);
+ for (i = offs + 1; i <= narg; i++)
+ v ^= lua_tointeger(L, i);
+
+ lua_pushinteger(L, v);
+ return 1;
+}
+
+
+static int bitwise_lua_neg(lua_State *L)
+{
+ int narg = lua_gettop(L);
+ int arg, offs;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ offs = 2;
+ break;
+ default:
+ offs = 1;
+ break;
+ }
+
+ if (narg != offs)
+ return luaL_error(L, "bitwise NEG takes a single argument");
+
+ arg = lua_tointeger(L, offs);
+ lua_pushinteger(L, ~arg);
+
+ return 1;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, NULL,
+ { "AND" , bitwise_lua_and },
+ { "OR" , bitwise_lua_or },
+ { "XOR" , bitwise_lua_xor },
+ { "NEG" , bitwise_lua_neg },
+ { "NEGATE", bitwise_lua_neg });
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <alloca.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/core/console.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+static void eval_cb(mrp_console_t *c, void *user_data, const char *grp,
+ const char *cmd, char *code)
+{
+ lua_State *L;
+ int len, top;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(grp);
+ MRP_UNUSED(cmd);
+
+ L = mrp_lua_get_lua_state();
+
+ if (L == NULL) {
+ printf("Lua runtime not available or initialized.");
+ return;
+ }
+
+ top = lua_gettop(L);
+
+ len = strlen(code);
+ if (luaL_loadbuffer(L, code, len, "<console>") || lua_pcall(L, 0, 0, 0))
+ printf("Lua error: %s\n", lua_tostring(L, -1));
+
+ lua_settop(L, top);
+}
+
+
+static void source_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ lua_State *L;
+ struct stat st;
+ char *path, *code;
+ size_t size;
+ ssize_t len;
+ int fd;
+ int top;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ if (argc != 3) {
+ printf("Invalid arguments, expecting a single path.");
+ return;
+ }
+ else
+ path = argv[2];
+
+ L = mrp_lua_get_lua_state();
+
+ if (L == NULL) {
+ printf("Lua runtime not available or initialized.");
+ return;
+ }
+
+ if (path && *path) {
+ top = lua_gettop(L);
+
+ if (stat(path, &st) == 0) {
+ fd = open(path, O_RDONLY);
+
+ if (fd >= 0) {
+ size = st.st_size;
+ code = alloca(size);
+ len = read(fd, code, size);
+ close(fd);
+
+ if (len > 0) {
+ if (luaL_loadbuffer(L, code, len, path) != 0 ||
+ lua_pcall(L, 0, 0, 0) != 0)
+ printf("Lua error: %s\n", lua_tostring(L, -1));
+ }
+ }
+ else
+ printf("Failed to open %s (%d: %s).\n", path,
+ errno, strerror(errno));
+ }
+ else
+ printf("Failed to open %s (%d: %s).\n", path,
+ errno, strerror(errno));
+
+ lua_settop(L, top);
+ }
+}
+
+
+static void debug_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ mrp_lua_debug_t level;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ if (argc == 3) {
+ if (!strcmp(argv[2], "disable")) level = MRP_LUA_DEBUG_DISABLED;
+ else if (!strcmp(argv[2], "enable")) level = MRP_LUA_DEBUG_ENABLED;
+ else if (!strcmp(argv[2], "detailed")) level = MRP_LUA_DEBUG_DETAILED;
+ else {
+ printf("Invalid Lua debug level '%s'.\n", argv[2]);
+ printf("The valid levels are: disable, enable, detailed.\n");
+ return;
+ }
+
+ if (mrp_lua_set_debug(level))
+ printf("Lua debugging level set to '%s'.\n", argv[2]);
+ else
+ printf("Failed to set Lua debugging level to '%s'.\n", argv[2]);
+ }
+ else {
+ printf("Invalid usage.\n");
+ printf("Argument must be disable, enable, or detailed.\n");
+ }
+}
+
+
+static void dump_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_lua_tostr_mode_t mode;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ switch (argc) {
+ case 2:
+ mode = MRP_LUA_TOSTR_CHECKDUMP;
+ break;
+
+ case 3:
+ if (!strcmp(argv[2], "default")) mode = MRP_LUA_TOSTR_DEFAULT;
+ else if (!strcmp(argv[2], "stackdump")) mode = MRP_LUA_TOSTR_STACKDUMP;
+ else if (!strcmp(argv[2], "errordump")) mode = MRP_LUA_TOSTR_ERRORDUMP;
+ else if (!strcmp(argv[2], "checkdump")) mode = MRP_LUA_TOSTR_CHECKDUMP;
+ else {
+ printf("Unknown dump mode '%s', using default.\n", argv[2]);
+ mode = MRP_LUA_TOSTR_DEFAULT;
+ }
+ break;
+
+ case 4:
+#define MAP(var, name, value) if (!strcmp(var, name)) mode |= value
+ mode = 0;
+ MAP(argv[2], "lua" , MRP_LUA_TOSTR_LUA );
+ MAP(argv[2], "minimal", MRP_LUA_TOSTR_MINIMAL);
+ MAP(argv[2], "compact", MRP_LUA_TOSTR_COMPACT);
+ MAP(argv[2], "oneline", MRP_LUA_TOSTR_ONELINE);
+ MAP(argv[2], "short" , MRP_LUA_TOSTR_SHORT );
+ MAP(argv[2], "medium" , MRP_LUA_TOSTR_MEDIUM );
+ MAP(argv[2], "full" , MRP_LUA_TOSTR_FULL );
+ MAP(argv[2], "verbose", MRP_LUA_TOSTR_VERBOSE);
+ MAP(argv[2], "meta" , MRP_LUA_TOSTR_META );
+ MAP(argv[2], "data" , MRP_LUA_TOSTR_DATA );
+ MAP(argv[2], "both" , MRP_LUA_TOSTR_BOTH );
+
+ MAP(argv[3], "lua" , MRP_LUA_TOSTR_LUA );
+ MAP(argv[3], "minimal", MRP_LUA_TOSTR_MINIMAL);
+ MAP(argv[3], "compact", MRP_LUA_TOSTR_COMPACT);
+ MAP(argv[3], "oneline", MRP_LUA_TOSTR_ONELINE);
+ MAP(argv[3], "short" , MRP_LUA_TOSTR_SHORT );
+ MAP(argv[3], "medium" , MRP_LUA_TOSTR_MEDIUM );
+ MAP(argv[3], "full" , MRP_LUA_TOSTR_FULL );
+ MAP(argv[3], "verbose", MRP_LUA_TOSTR_VERBOSE);
+ MAP(argv[3], "meta" , MRP_LUA_TOSTR_META );
+ MAP(argv[3], "data" , MRP_LUA_TOSTR_DATA );
+ MAP(argv[3], "both" , MRP_LUA_TOSTR_BOTH );
+#undef MAP
+ break;
+
+ default:
+ printf("Invalid dump command.\n");
+ return;
+ }
+
+ if (L != NULL)
+ mrp_lua_dump_objects(MRP_LUA_TOSTR_CHECKDUMP, L, stdout);
+}
+
+
+static void gc_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ int pause, step;
+ char *e;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+ MRP_UNUSED(user_data);
+
+ if (L == NULL)
+ return;
+
+ switch (argc) {
+ case 2:
+ goto full;
+
+ case 3:
+ if (!strcmp(argv[2], "full")) {
+ full:
+ printf("Performing a full Lua garbage collection cycle...\n");
+ lua_gc(L, LUA_GCCOLLECT, 0);
+ }
+ else if (!strcmp(argv[2], "stop")) {
+ lua_gc(L, LUA_GCSTOP, 0);
+ printf("Lua garbage collector stopped...\n");
+ }
+ else if (!strcmp(argv[2], "start")) {
+ lua_gc(L, LUA_GCRESTART, 0);
+ printf("Lua garbage collector restarted...\n");
+ }
+ else
+ invalid:
+ printf("Invalid Lua garbage collector command.\n");
+ break;
+
+ case 5:
+ if (strcmp(argv[2], "set"))
+ goto invalid;
+
+ pause = (int)strtoul(argv[3], &e, 10);
+ if (*e) {
+ printf("Invalid Lua garbage collector pause '%s'.\n", argv[3]);
+ return;
+ }
+
+ step = strtoul(argv[4], &e, 10);
+ if (*e) {
+ printf("Invalid Lua garbage collector step '%s'.\n", argv[4]);
+ return;
+ }
+
+ printf("Setting Lua garbage collector pause=%d, step=%d...\n",
+ pause, step);
+
+ lua_gc(L, LUA_GCSETPAUSE, pause);
+ lua_gc(L, LUA_GCSETSTEPMUL, step);
+ break;
+
+ default:
+ goto invalid;
+ }
+}
+
+#define LUA_GROUP_DESCRIPTION \
+ "Lua commands allows one to evaluate Lua code either from\n" \
+ "the console command line itself, or from sourced files.\n"
+
+#define EVAL_SYNTAX "<lua-code>"
+#define EVAL_SUMMARY "evaluate the given snippet of Lua code"
+#define EVAL_DESCRIPTION \
+ "Evaluate the given snippet of Lua code. Currently you have to\n" \
+ "fully quote the Lua code you are trying to evaluate to protect\n" \
+ "it from the tokenizer of the console input parser. This is the\n" \
+ "easiest to accomplish by surrounding your Lua code snippet in\n" \
+ "single or double quotes unconditionally.\n"
+
+#define SOURCE_SYNTAX "source <lua-file>"
+#define SOURCE_SUMMARY "evaluate the Lua script from the given <lua-file>"
+#define SOURCE_DESCRIPTION "Read and evaluate the contents of <lua-file>.\n"
+
+#define DEBUG_SYNTAX "debug {disable, enable, detailed}"
+#define DEBUG_SUMMARY "configure Murphy Lua debugging"
+#define DEBUG_DESCRIPTION "Configure Murphy Lua debugging."
+
+#define DUMP_SYNTAX "dump [dump-flags]"
+#define DUMP_SUMMARY "dump active Murphy Lua objects"
+#define DUMP_DESCRIPTION \
+ "Dump unfreed Murphy Lua objects per object class. You need to enable\n" \
+ "object tracking for this to work. The easiest way to do this is to\n" \
+ "set the environment variable__MURPHY_MM_CONFIG=\"lua:true\" before\n" \
+ "starting the daemon. dump-flags control how much information gets\n" \
+ "printed about a single object. If you use a single dump-flag, it can\n" \
+ "be one of default, stackdump, errordump, or checkdump. If omitted,\n" \
+ "default is used. You can also give a pair of dump flags, the first\n" \
+ "of lua, minimal, compact, oneline, short, medium, full, or verbose\n" \
+ "and the second one of meta, data, or both. These correspond directly\n" \
+ "to the object to string conversion mode flags of the Murphy Lua\n" \
+ "object infrastructure. At the moment these flags have very little\n" \
+ "practical effect on the actual dump as most of the dump modes have\n" \
+ "not been implemented yet so now they are just aliased to the default.\n"
+
+#define GC_SYNTAX "gc [full|stop|start|set <pause> <step>"
+#define GC_SUMMARY "trigger or configure the Lua garbage collector"
+#define GC_DESCRIPTION "Trigger or configure the Lua garbage collector."
+
+MRP_CORE_CONSOLE_GROUP(lua_group, "lua", LUA_GROUP_DESCRIPTION, NULL, {
+ MRP_TOKENIZED_CMD("source", source_cb, FALSE,
+ SOURCE_SYNTAX, SOURCE_SUMMARY, SOURCE_DESCRIPTION),
+ MRP_RAWINPUT_CMD("eval", eval_cb,
+ MRP_CONSOLE_CATCHALL | MRP_CONSOLE_SELECTABLE,
+ EVAL_SYNTAX, EVAL_SUMMARY, EVAL_DESCRIPTION),
+ MRP_TOKENIZED_CMD("debug", debug_cb, FALSE,
+ DEBUG_SYNTAX, DEBUG_SUMMARY, DEBUG_DESCRIPTION),
+ MRP_TOKENIZED_CMD("dump", dump_cb, FALSE,
+ DUMP_SYNTAX, DUMP_SUMMARY, DUMP_DESCRIPTION),
+ MRP_TOKENIZED_CMD("gc", gc_cb, FALSE,
+ GC_SYNTAX, GC_SUMMARY, GC_DESCRIPTION),
+ });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/lua-utils/error.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#define DEFERRED_LUA_CLASS MRP_LUA_CLASS(deferred, lua)
+
+/*
+ * Lua deferred object
+ */
+
+typedef struct {
+ lua_State *L; /* Lua execution context */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ mrp_deferred_t *d; /* associated murphy deferred */
+ int callback; /* reference to callback */
+ bool disabled; /* true if disabled */
+ bool oneshot; /* true for one-shot deferreds */
+} deferred_lua_t;
+
+
+static int deferred_lua_create(lua_State *L);
+static void deferred_lua_destroy(void *data);
+static void deferred_lua_changed(void *data, lua_State *L, int member);
+static ssize_t deferred_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+static int deferred_lua_enable(lua_State *L);
+static int deferred_lua_disable(lua_State *L);
+
+/*
+ * Lua deferred class
+ */
+
+#define OFFS(m) MRP_OFFSET(deferred_lua_t, m)
+#define RDONLY MRP_LUA_CLASS_READONLY
+#define NOTIFY MRP_LUA_CLASS_NOTIFY
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+#define USESTACK MRP_LUA_CLASS_USESTACK
+
+MRP_LUA_METHOD_LIST_TABLE(deferred_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(deferred_lua_create)
+ MRP_LUA_METHOD(disable, deferred_lua_disable)
+ MRP_LUA_METHOD(enable , deferred_lua_enable));
+
+MRP_LUA_METHOD_LIST_TABLE(deferred_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (deferred_lua_create));
+
+MRP_LUA_MEMBER_LIST_TABLE(deferred_lua_members,
+ MRP_LUA_CLASS_LFUNC ("callback" , OFFS(callback) , NULL, NULL, NOTIFY )
+ MRP_LUA_CLASS_BOOLEAN("disabled" , OFFS(disabled) , NULL, NULL, NOTIFY )
+ MRP_LUA_CLASS_BOOLEAN("oneshot" , OFFS(oneshot) , NULL, NULL, NOTIFY ));
+
+typedef enum {
+ DEFERRED_MEMBER_CALLBACK,
+ DEFERRED_MEMBER_DISABLED,
+ DEFERRED_MEMBER_ONESHOT,
+} deferred_member_t;
+
+MRP_LUA_DEFINE_CLASS(deferred, lua, deferred_lua_t, deferred_lua_destroy,
+ deferred_lua_methods, deferred_lua_overrides,
+ deferred_lua_members, NULL, deferred_lua_changed,
+ deferred_lua_tostring, NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_DYNAMIC);
+
+
+static void deferred_lua_cb(mrp_deferred_t *deferred, void *user_data)
+{
+ deferred_lua_t *d = (deferred_lua_t *)user_data;
+ int one = d->oneshot;
+ int top;
+
+ MRP_UNUSED(deferred);
+
+ top = lua_gettop(d->L);
+
+ if (mrp_lua_object_deref_value(d, d->L, d->callback, false)) {
+ mrp_lua_push_object(d->L, d);
+
+ if (lua_pcall(d->L, 1, 0, 0) != 0) {
+ mrp_log_error("failed to invoke Lua deferred callback, disabling");
+ mrp_disable_deferred(d->d);
+ d->disabled = true;
+ }
+ }
+
+ if (one) {
+ mrp_disable_deferred(d->d);
+ d->disabled = true;
+ }
+
+ lua_settop(d->L, top);
+}
+
+
+static void deferred_lua_changed(void *data, lua_State *L, int member)
+{
+ deferred_lua_t *d = (deferred_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("deferred member #%d (%s) changed", member,
+ deferred_lua_members[member].name);
+
+ switch (member) {
+ case DEFERRED_MEMBER_DISABLED:
+ if (d->disabled)
+ mrp_disable_deferred(d->d);
+ else
+ mrp_enable_deferred(d->d);
+ mrp_debug("deferred %p(%p) is now %sabled", d, d->d,
+ d->disabled ? "dis" : "en");
+ break;
+
+ case DEFERRED_MEMBER_CALLBACK:
+ if (!d->disabled) {
+ if (d->callback == LUA_NOREF)
+ mrp_disable_deferred(d->d);
+ else
+ mrp_enable_deferred(d->d);
+ mrp_debug("deferred %p(%p) is now %sabled", d, d->d,
+ d->disabled ? "dis" : "en");
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static int deferred_lua_create(lua_State *L)
+{
+
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ char e[128] = "";
+ deferred_lua_t *d;
+ int narg;
+
+ if (ctx == NULL)
+ luaL_error(L, "failed to get murphy context");
+
+ narg = lua_gettop(L);
+
+ d = (deferred_lua_t *)mrp_lua_create_object(L, DEFERRED_LUA_CLASS, NULL, 0);
+
+ d->L = L;
+ d->ml = ctx->ml;
+ d->d = mrp_add_deferred(d->ml, deferred_lua_cb, d);
+ d->callback = LUA_NOREF;
+
+ if (d->d == NULL)
+ return luaL_error(L, "failed to create Lua Murphy deferred");
+
+ switch (narg) {
+ case 1:
+ break;
+ case 2:
+ if (mrp_lua_init_members(d, L, -2, e, sizeof(e)) != 1)
+ return luaL_error(L, "failed to initialize deferred (%s)", e);
+ break;
+ default:
+ return luaL_error(L, "expecting 0 or 1 arguments, got %d", narg);
+ }
+
+ if (d->disabled || d->callback == LUA_NOREF || d->callback == LUA_REFNIL)
+ mrp_disable_deferred(d->d);
+
+ return 1;
+}
+
+
+static void deferred_lua_destroy(void *data)
+{
+ deferred_lua_t *d = (deferred_lua_t *)data;
+
+ mrp_debug("destroying Lua deferred %p", data);
+
+ mrp_del_deferred(d->d);
+ d->d = NULL;
+
+ mrp_lua_object_unref_value(d, d->L, d->callback);
+
+ d->callback = LUA_NOREF;
+}
+
+
+static deferred_lua_t *deferred_lua_check(lua_State *L, int idx)
+{
+ return (deferred_lua_t *)mrp_lua_check_object(L, DEFERRED_LUA_CLASS, idx);
+}
+
+
+static ssize_t deferred_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ deferred_lua_t *d = (deferred_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{%s %s deferred %p}",
+ d->disabled ? "disabled" : "enabled",
+ d->oneshot ? "oneshot" : "recurring",
+ d->d);
+ }
+}
+
+
+static int deferred_lua_enable(lua_State *L)
+{
+ deferred_lua_t *d = deferred_lua_check(L, -1);
+
+ if (d == NULL) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ if (d->d != NULL && d->callback != LUA_NOREF && d->callback != LUA_REFNIL) {
+ mrp_enable_deferred(d->d);
+ d->disabled = false;
+ }
+
+ lua_pushboolean(L, !d->disabled);
+
+ return 1;
+}
+
+
+static int deferred_lua_disable(lua_State *L)
+{
+ deferred_lua_t *d = deferred_lua_check(L, -1);
+
+ if (d == NULL) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ if (d->d != NULL) {
+ mrp_disable_deferred(d->d);
+ d->disabled = true;
+ }
+
+ lua_pushboolean(L, true);
+
+ return 1;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, DEFERRED_LUA_CLASS,
+ { "Deferred", deferred_lua_create });
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+static int env_lua_getenv(lua_State *L)
+{
+ int narg = lua_gettop(L);
+ int offs, i;
+ const char *v;
+
+ switch (lua_type(L, 1)) {
+ case LUA_TUSERDATA:
+ case LUA_TLIGHTUSERDATA:
+ offs = 2;
+ break;
+ default:
+ offs = 1;
+ break;
+ }
+
+ for (i = offs; i <= narg; i++) {
+ if (lua_type(L, offs) == LUA_TSTRING)
+ v = getenv(lua_tostring(L, offs));
+ else
+ v = NULL;
+
+ lua_remove(L, offs);
+
+ if (v)
+ lua_pushstring(L, v);
+ else
+ lua_pushnil(L);
+ }
+
+ return narg + 1 - offs;
+}
+
+
+static int env_lua_getpid(lua_State *L)
+{
+ lua_pushinteger(L, getpid());
+
+ return 1;
+}
+
+
+static int env_lua_getuid(lua_State *L)
+{
+ lua_pushinteger(L, getuid());
+
+ return 1;
+}
+
+
+static int env_lua_geteuid(lua_State *L)
+{
+ lua_pushinteger(L, geteuid());
+
+ return 1;
+}
+
+
+static int env_lua_getgid(lua_State *L)
+{
+ lua_pushinteger(L, getuid());
+
+ return 1;
+}
+
+
+static int env_lua_getuser(lua_State *L)
+{
+ struct passwd pwd, *r;
+ char buf[1024];
+
+ getpwuid_r(getuid(), &pwd, buf, sizeof(buf), &r);
+
+ if (r != NULL)
+ lua_pushstring(L, pwd.pw_name);
+ else
+ lua_pushnil(L);
+
+ return 1;
+}
+
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, NULL,
+ { "getenv" , env_lua_getenv },
+ { "getpid" , env_lua_getpid },
+ { "getuid" , env_lua_getuid },
+ { "geteuid", env_lua_geteuid },
+ { "getgid" , env_lua_getgid },
+ { "getuser", env_lua_getuser });
--- /dev/null
+/*
+ * Copyright (c) 2014 Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+/* This is a placeholder for proper Murphy event system. The facilities for
+ * sending/receiving messages along with the events have not been implemented
+ * yet, and neither is sending/receiving multiple events at once (as part of
+ * the event mask). Only the basic functionality for sending/receiving
+ * argumentless messages exists. */
+
+/*
+ * Lua EventWatch object
+ */
+
+#define EVTWATCH_LUA_CLASS MRP_LUA_CLASS(evtwatch, lua)
+
+typedef struct {
+ lua_State *L; /* Lua execution context */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_event_bus_t *bus; /* event bus to watch on */
+ mrp_event_mask_t mask; /* mask of events to watch */
+ mrp_event_watch_t *w; /* associated murphy event watch */
+ bool init; /* being initialized */
+
+ /* Lua members */
+ char *bus_name; /* name of the event bus to use */
+ char **events; /* name of the events to watch */
+ int nevent; /* number of event names */
+ int callback; /* reference to callback */
+ bool oneshot; /* disable after first matching event */
+} evtwatch_lua_t;
+
+
+static int evtwatch_lua_create(lua_State *L);
+static void evtwatch_lua_destroy(void *data);
+static void evtwatch_lua_changed(void *data, lua_State *L, int member);
+static ssize_t evtwatch_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+
+static int evtwatch_lua_start(lua_State *L);
+static int evtwatch_lua_stop(lua_State *L);
+
+static void evtwatch_lua_cb(mrp_event_watch_t *ew, uint32_t id,
+ int format, void *data, void *user_data);
+
+
+
+/*
+ * Lua EventWatch class
+ */
+
+#define OFFS(m) MRP_OFFSET(evtwatch_lua_t, m)
+#define RDONLY MRP_LUA_CLASS_READONLY
+#define NOTIFY MRP_LUA_CLASS_NOTIFY
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+
+MRP_LUA_METHOD_LIST_TABLE(evtwatch_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(evtwatch_lua_create)
+ MRP_LUA_METHOD(stop , evtwatch_lua_stop)
+ MRP_LUA_METHOD(start, evtwatch_lua_start));
+
+MRP_LUA_METHOD_LIST_TABLE(evtwatch_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL(evtwatch_lua_create));
+
+MRP_LUA_MEMBER_LIST_TABLE(evtwatch_lua_members,
+ MRP_LUA_CLASS_STRING ("bus" , OFFS(bus_name), NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_ARRAY ("events" , STRING, evtwatch_lua_t, events, nevent,
+ NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_LFUNC ("callback", OFFS(callback), NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_BOOLEAN("oneshot" , OFFS(oneshot) , NULL, NULL, NOTIFY));
+
+
+typedef enum {
+ EVENT_MEMBER_BUS,
+ EVENT_MEMBER_EVENTS,
+ EVENT_MEMBER_CALLBACK,
+ EVENT_MEMBER_ONESHOT
+} event_member_t;
+
+MRP_LUA_DEFINE_CLASS(evtwatch, lua, evtwatch_lua_t, evtwatch_lua_destroy,
+ evtwatch_lua_methods, evtwatch_lua_overrides,
+ evtwatch_lua_members, NULL, evtwatch_lua_changed,
+ evtwatch_lua_tostring, NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_DYNAMIC);
+
+
+static void evtwatch_stop(evtwatch_lua_t *w)
+{
+ mrp_event_del_watch(w->w);
+ w->w = NULL;
+}
+
+
+static bool evtwatch_start(evtwatch_lua_t *w)
+{
+ if (w->w == NULL) {
+ w->w = mrp_event_add_watch_mask(w->bus, &w->mask, evtwatch_lua_cb, w);
+ mrp_debug("started event watch %p (%p)", w, w->w);
+ }
+
+ return w->w != NULL;
+}
+
+
+static void evtwatch_lua_cb(mrp_event_watch_t *watch, uint32_t id,
+ int format, void *data, void *user_data)
+{
+ evtwatch_lua_t *w = (evtwatch_lua_t *)user_data;
+ int one = w->oneshot;
+ int top;
+
+ MRP_UNUSED(watch);
+ MRP_UNUSED(format);
+ MRP_UNUSED(data);
+
+ mrp_debug("got event 0x%x (%s)", id, mrp_event_name(id));
+
+ top = lua_gettop(w->L);
+
+ if (mrp_lua_object_deref_value(w, w->L, w->callback, false)) {
+ mrp_lua_push_object(w->L, w);
+ lua_pushinteger(w->L, id);
+
+ if (lua_pcall(w->L, 2, 0, 0) != 0) {
+ mrp_log_error("failed to invoke Lua event watch callback (%s), "
+ "stopping", lua_tostring(w->L, -1));
+ evtwatch_stop(w);
+ }
+
+ if (one)
+ evtwatch_stop(w);
+ }
+
+ lua_settop(w->L, top);
+}
+
+
+static void evtwatch_lua_changed(void *data, lua_State *L, int member)
+{
+ evtwatch_lua_t *w = (evtwatch_lua_t *)data;
+ int i;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("event watch member #%d (%s) changed", member,
+ evtwatch_lua_members[member].name);
+
+ switch (member) {
+ case EVENT_MEMBER_BUS:
+ if (!w->init)
+ evtwatch_stop(w);
+ if (!w->bus_name || !*w->bus_name || !strcmp(w->bus_name, "global"))
+ w->bus = NULL;
+ else
+ w->bus = mrp_event_bus_get(w->ctx->ml, w->bus_name);
+ if (!w->init)
+ evtwatch_start(w);
+ break;
+
+ case EVENT_MEMBER_EVENTS:
+ if (!w->init)
+ evtwatch_stop(w);
+ mrp_mask_reset(&w->mask);
+ for (i = 0; i < w->nevent; i++) {
+ mrp_debug("setting event %s in mask", w->events[i]);
+ mrp_mask_set(&w->mask, mrp_event_id(w->events[i]));
+ }
+ if (!w->init)
+ evtwatch_start(w);
+ break;
+
+ case EVENT_MEMBER_CALLBACK:
+ mrp_debug("callback set to (ref) %u", w->callback);
+ if (w->callback == LUA_NOREF || w->callback == LUA_REFNIL) {
+ if (!w->init)
+ evtwatch_stop(w);
+ }
+ else
+ if (!w->init)
+ evtwatch_start(w);
+ break;
+
+ case EVENT_MEMBER_ONESHOT:
+ break;
+
+ default:
+ break;
+ }
+
+
+
+}
+
+
+static int evtwatch_lua_create(lua_State *L)
+{
+ evtwatch_lua_t *w;
+ int narg;
+ char e[128], events[512];
+
+ narg = lua_gettop(L);
+
+ if (narg < 1 || narg > 2)
+ return luaL_error(L, "expected 0, or 1 arguments, got %d", narg - 1);
+
+ w = (evtwatch_lua_t *)mrp_lua_create_object(L, EVTWATCH_LUA_CLASS, NULL, 0);
+ w->L = L;
+ w->ctx = mrp_lua_get_murphy_context();
+ w->init = true;
+ w->callback = LUA_NOREF;
+
+ if (mrp_lua_init_members(w, L, -2, e, sizeof(e)) != 1)
+ return luaL_error(L, "failed to initialize event watch (%s)", e);
+
+ w->init = false;
+
+ evtwatch_start(w);
+
+ mrp_debug("created event watch %p (%p) for events %s", w, w->w,
+ mrp_event_dump_mask(&w->mask, events, sizeof(events)));
+
+ return 1;
+}
+
+
+static void evtwatch_lua_destroy(void *data)
+{
+ evtwatch_lua_t *w = (evtwatch_lua_t *) data;
+
+ mrp_debug("destroying Lua event watch %p", w);
+
+ evtwatch_stop(w);
+
+ mrp_lua_object_unref_value(w, w->L, w->callback);
+
+ w->callback = LUA_NOREF;
+}
+
+
+static evtwatch_lua_t *evtwatch_lua_check(lua_State *L, int idx)
+{
+ return (evtwatch_lua_t *)mrp_lua_check_object(L, EVTWATCH_LUA_CLASS, idx);
+}
+
+
+static ssize_t evtwatch_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ evtwatch_lua_t *w = (evtwatch_lua_t *) data;
+ char events[512];
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "event watch <%s>",
+ mrp_event_dump_mask(&w->mask, events, sizeof(events)));
+ }
+}
+
+
+static int evtwatch_lua_start(lua_State *L)
+{
+ evtwatch_lua_t *w = evtwatch_lua_check(L, -1);
+
+ lua_pushboolean(L, evtwatch_start(w));
+
+ return 1;
+}
+
+
+static int evtwatch_lua_stop(lua_State *L)
+{
+ evtwatch_lua_t *w = evtwatch_lua_check(L, -1);
+
+ evtwatch_stop(w);
+
+ return 0;
+}
+
+
+static int evtwatch_emit_event(lua_State *L)
+{
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ int narg = lua_gettop(L);
+ mrp_event_bus_t *bus;
+ const char *bus_name, *event;
+ uint32_t id;
+ int flags, r;
+
+ if (narg != 3 && narg != 4)
+ return luaL_error(L, "expected 2 or 3 arguments, got %d", narg - 1);
+
+ if (lua_type(L, 2) == LUA_TSTRING)
+ bus_name = lua_tostring(L, 2);
+ else if (lua_type(L, 2) == LUA_TNIL)
+ bus_name = NULL;
+ else
+ return luaL_error(L, "expected nil or bus name as 1st argument");
+
+ if (lua_type(L, 3) == LUA_TSTRING)
+ event = lua_tostring(L, 3);
+ else
+ return luaL_error(L, "expected event name string as 2nd argument");
+
+ flags = MRP_EVENT_SYNCHRONOUS;
+
+ if (narg == 4) {
+ if (lua_type(L, 4) != LUA_TBOOLEAN)
+ return luaL_error(L, "expected asynchronous bool as 3rd argument");
+ if (lua_toboolean(L, 4))
+ flags = MRP_EVENT_ASYNCHRONOUS;
+ }
+
+ bus = mrp_event_bus_get(ctx->ml, bus_name);
+ id = mrp_event_id(event);
+
+ mrp_debug("emitting event 0x%x (<%s>) on bus <%s>", id, event,
+ bus_name ? bus_name : "global");
+
+ r = mrp_event_emit_msg(bus, id, MRP_EVENT_SYNCHRONOUS, flags, MRP_MSG_END);
+
+ lua_pushboolean(L, r == 0);
+
+ return 1;
+}
+
+
+static int evtwatch_event_id(lua_State *L)
+{
+ int narg = lua_gettop(L);
+
+ if (narg != 2)
+ return luaL_error(L, "expected 1 event name argument, got %d", narg);
+
+ if (lua_type(L, 2) != LUA_TSTRING)
+ return luaL_error(L, "expected event name string argument");
+
+ lua_pushinteger(L, mrp_event_id(lua_tostring(L, 2)));
+
+ return 1;
+}
+
+
+static int evtwatch_event_name(lua_State *L)
+{
+ int narg = lua_gettop(L);
+
+ if (narg != 2)
+ return luaL_error(L, "expected 1 event id argument, got %d", narg);
+
+ if (lua_type(L, 2) != LUA_TNUMBER)
+ return luaL_error(L, "expected event id integer argument");
+
+ lua_pushstring(L, mrp_event_name(lua_tointeger(L, 2)));
+
+ return 1;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, EVTWATCH_LUA_CLASS,
+ { "EventWatch" , evtwatch_lua_create },
+ { "emit_event" , evtwatch_emit_event },
+ { "EventListener", evtwatch_lua_create },
+ { "send_event" , evtwatch_emit_event },
+ { "event_id" , evtwatch_event_id },
+ { "event_name" , evtwatch_event_name });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/json.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-bindings/lua-json.h>
+
+#define JSON_LUA_CLASS MRP_LUA_CLASS(json, lua)
+
+/*
+ * Lua JSON object
+ */
+
+typedef struct {
+ mrp_json_t *json;
+} json_lua_t;
+
+
+/*
+ * Lua JSON object methods
+ */
+
+static void json_lua_destroy(void *data);
+static int json_lua_getfield(lua_State *L);
+static int json_lua_setfield(lua_State *L);
+static int json_lua_stringify(lua_State *L);
+static json_lua_t *json_lua_get(lua_State *L, int idx);
+static mrp_json_t *json_lua_table_to_object(lua_State *L, int t);
+
+
+/*
+ * JSON Lua class
+ */
+
+MRP_LUA_METHOD_LIST_TABLE(json_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(mrp_json_lua_create));
+
+MRP_LUA_METHOD_LIST_TABLE(json_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (mrp_json_lua_create)
+ MRP_LUA_OVERRIDE_GETFIELD (json_lua_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (json_lua_setfield)
+ MRP_LUA_OVERRIDE_STRINGIFY(json_lua_stringify));
+
+MRP_LUA_CLASS_DEF_FLAGS(json, lua, json_lua_t,
+ json_lua_destroy, json_lua_methods, json_lua_overrides,
+ MRP_LUA_CLASS_DYNAMIC);
+
+
+int mrp_json_lua_create(lua_State *L)
+{
+ json_lua_t *lson;
+ mrp_json_t *json;
+ int t;
+
+ switch (lua_gettop(L)) {
+ noargs:
+ case 0:
+ lson = (json_lua_t *)mrp_lua_create_object(L, JSON_LUA_CLASS, NULL, 0);
+ lson->json = mrp_json_create(MRP_JSON_OBJECT);
+ break;
+
+ /*
+ * assume to be called as m:JSON(<initializer table>)
+ *
+ * Notes: We should check the argument to see if it happens
+ * to be of type murphy to catch calls of the form m.JSON()
+ * and redirect them to case 0.
+ */
+ case 1:
+ if (lua_type(L, 1) == LUA_TUSERDATA)
+ goto noargs;
+ t = 1;
+ init:
+ if (lua_type(L, t) != LUA_TTABLE)
+ return luaL_error(L, "invalid argument to JSON constructor");
+ json = json_lua_table_to_object(L, t);
+ lson = (json_lua_t *)mrp_lua_create_object(L, JSON_LUA_CLASS, NULL, 0);
+ lson->json = json;
+ break;
+
+ /*
+ * assume to be called as m:JSON(<initializer table>), ie.
+ * m.JSON(m, <initializer table>)
+ *
+ * Notes: We should check the first argument and make sure it
+ * is of type murphy.
+ */
+ case 2:
+ t = 2;
+ goto init;
+
+ default:
+ return luaL_error(L, "invalid arguments to JSON constructor (%d)",
+ lua_gettop(L));
+ }
+
+ return 1;
+}
+
+
+void *mrp_json_lua_wrap(lua_State *L, mrp_json_t *json)
+{
+ json_lua_t *lson = mrp_lua_create_object(L, JSON_LUA_CLASS, NULL, 0);
+
+ lson->json = mrp_json_ref(json);
+
+ return lson;
+}
+
+
+int mrp_json_lua_push(lua_State *L, mrp_json_t *json)
+{
+ json_lua_t *lson;
+
+ if ((lson = mrp_json_lua_wrap(L, json)) == NULL)
+ lua_pushnil(L);
+
+ return 1;
+}
+
+
+mrp_json_t *mrp_json_lua_get(lua_State *L, int idx)
+{
+ json_lua_t *lson = json_lua_get(L, idx);
+
+ if (lson != NULL)
+ return mrp_json_ref(lson->json);
+ else
+ return NULL;
+}
+
+
+mrp_json_t *mrp_json_lua_unwrap(void *lson)
+{
+ if (mrp_lua_pointer_of_type(lson, MRP_LUA_TYPE_ID(JSON_LUA_CLASS)))
+ return mrp_json_ref(((json_lua_t *)lson)->json);
+ else
+ return NULL;
+}
+
+
+static void json_lua_destroy(void *data)
+{
+ json_lua_t *lson = (json_lua_t *)data;
+
+ mrp_debug("destroying Lua JSON object %p (%p)", lson, lson->json);
+
+ mrp_json_unref(lson->json);
+}
+
+
+static inline json_lua_t *json_lua_check(lua_State *L, int idx)
+{
+ return (json_lua_t *)mrp_lua_check_object(L, JSON_LUA_CLASS, idx);
+}
+
+
+static json_lua_t *json_lua_get(lua_State *L, int idx)
+{
+ json_lua_t *lson;
+ void *userdata;
+
+ lua_pushvalue(L, idx);
+ lua_pushliteral(L, "userdata");
+
+ lua_rawget(L, -2);
+ userdata = lua_touserdata(L, -1);
+ lua_pop(L, 2);
+
+ if (userdata != NULL)
+ if ((lson = json_lua_check(L, idx)) != NULL)
+ return lson;
+
+ return NULL;
+}
+
+
+static int json_lua_getfield(lua_State *L)
+{
+ json_lua_t *lson = json_lua_check(L, 1);
+ const char *key;
+ int idx;
+ mrp_json_t *val;
+
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ key = lua_tostring(L, 2);
+ val = mrp_json_get(lson->json, key);
+ break;
+
+ case LUA_TNUMBER:
+ if (mrp_json_get_type(lson->json) != MRP_JSON_ARRAY)
+ return luaL_error(L, "trying to index non-array JSON object");
+
+ idx = lua_tointeger(L, 2);
+ val = mrp_json_array_get(lson->json, idx - 1);
+ break;
+
+ default:
+ return luaL_error(L, "invalid JSON field/index type (%s).",
+ lua_typename(L, lua_type(L, 2)));
+ }
+
+ lua_pop(L, 2);
+
+ switch (mrp_json_get_type(val)) {
+ case MRP_JSON_STRING:
+ lua_pushstring(L, mrp_json_string_value(val));
+ break;
+
+ case MRP_JSON_BOOLEAN:
+ lua_pushboolean(L, mrp_json_boolean_value(val));
+ break;
+
+ case MRP_JSON_INTEGER:
+ lua_pushinteger(L, mrp_json_integer_value(val));
+ break;
+
+ case MRP_JSON_DOUBLE:
+ lua_pushnumber(L, mrp_json_double_value(val));
+ break;
+
+ case MRP_JSON_OBJECT:
+ case MRP_JSON_ARRAY:
+ mrp_json_lua_push(L, val);
+ break;
+
+ default:
+ lua_pushnil(L);
+ }
+
+ return 1;
+}
+
+
+static mrp_json_t *json_lua_table_to_object(lua_State *L, int t)
+{
+ json_lua_t *lson;
+ mrp_json_t *json;
+ const char *key;
+ int idx, i;
+ double dbl;
+ mrp_json_t *val;
+
+ if ((lson = json_lua_get(L, t)) != NULL)
+ return mrp_json_clone(lson->json);
+
+ json = NULL;
+ val = NULL;
+
+ lua_pushnil(L);
+ while (lua_next(L, t) != 0) {
+ switch (lua_type(L, -2)) {
+ case LUA_TSTRING:
+ if (json != NULL && mrp_json_get_type(json) != MRP_JSON_OBJECT)
+ luaL_error(L, "trying to set member on a JSON array");
+
+ key = lua_tostring(L, -2);
+ idx = 0;
+ break;
+
+ case LUA_TNUMBER:
+ if (json != NULL && mrp_json_get_type(json) != MRP_JSON_ARRAY)
+ luaL_error(L, "trying to set array element on a JSON object");
+
+ if ((idx = lua_tointeger(L, -2)) < 1)
+ luaL_error(L, "invalid index (%d) for JSON array", idx);
+
+ idx--;
+ key = NULL;
+ break;
+
+ default:
+ luaL_error(L, "invalid member (key) for JSON object");
+ idx = 0;
+ key = NULL;
+ }
+
+ switch (lua_type(L, -1)) {
+ case LUA_TSTRING:
+ val = mrp_json_create(MRP_JSON_STRING, lua_tostring(L, -1), -1);
+ break;
+
+ case LUA_TNUMBER:
+ if ((i = lua_tointeger(L, -1)) == (dbl = lua_tonumber(L, -1)))
+ val = mrp_json_create(MRP_JSON_INTEGER, i);
+ else
+ val = mrp_json_create(MRP_JSON_DOUBLE, dbl);
+ break;
+
+ case LUA_TBOOLEAN:
+ val = mrp_json_create(MRP_JSON_BOOLEAN, lua_toboolean(L, -1));
+ break;
+
+ case LUA_TTABLE:
+ val = json_lua_table_to_object(L, lua_gettop(L));
+ break;
+
+ case LUA_TNIL:
+ goto next;
+
+ default:
+ luaL_error(L, "invalid value for JSON member");
+ }
+
+ if (val == NULL) {
+ mrp_json_unref(json);
+ luaL_error(L, "failed convert Lua value to JSON object");
+ }
+
+ if (json == NULL) {
+ json = mrp_json_create(key ? MRP_JSON_OBJECT : MRP_JSON_ARRAY);
+
+ if (json == NULL)
+ luaL_error(L, "failed to create JSON object");
+ }
+
+ if (key != NULL)
+ mrp_json_add(json, key, val);
+ else
+ if (!mrp_json_array_append(json, val))
+ luaL_error(L, "failed to set JSON array element [%d]", idx);
+
+ next:
+ lua_pop(L, 1);
+ }
+
+ return json;
+}
+
+
+static int json_lua_setfield(lua_State *L)
+{
+ json_lua_t *lson = json_lua_check(L, 1);
+ mrp_json_t *json = lson->json;
+ const char *key;
+ int idx, i;
+ double dbl;
+ mrp_json_t *val;
+
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ if (mrp_json_get_type(json) != MRP_JSON_OBJECT)
+ return luaL_error(L, "trying to set member on a JSON array");
+
+ key = lua_tostring(L, 2);
+ idx = 0;
+ break;
+
+ case LUA_TNUMBER:
+ if (mrp_json_get_type(json) != MRP_JSON_ARRAY)
+ return luaL_error(L, "trying to set array element on JSON object");
+
+ if ((idx = lua_tointeger(L, 2)) < 1)
+ return luaL_error(L, "invalid index (%d) for JSON array", idx);
+
+ idx--;
+ key = NULL;
+ break;
+
+ default:
+ return luaL_error(L, "invalid member (key) for JSON object");
+ }
+
+ switch (lua_type(L, 3)) {
+ case LUA_TSTRING:
+ val = mrp_json_create(MRP_JSON_STRING, lua_tostring(L, 3), -1);
+ break;
+
+ case LUA_TNUMBER:
+ if ((i = lua_tointeger(L, 3)) == (dbl = lua_tonumber(L, 3)))
+ val = mrp_json_create(MRP_JSON_INTEGER, i);
+ else
+ val = mrp_json_create(MRP_JSON_DOUBLE, dbl);
+ break;
+
+ case LUA_TBOOLEAN:
+ val = mrp_json_create(MRP_JSON_BOOLEAN, lua_toboolean(L, 3));
+ break;
+
+ case LUA_TTABLE:
+ if ((val = json_lua_table_to_object(L, 3)) == json)
+ return luaL_error(L, "can't set JSON object as a member of itself");
+ break;
+
+ case LUA_TNIL:
+ if (key != NULL)
+ mrp_json_del_member(json, key);
+ else
+ return luaL_error(L, "can't delete JSON array element by "
+ "setting to nil");
+ goto out;
+
+ default:
+ return luaL_error(L, "invalid value for JSON member");
+ }
+
+ if (val == NULL)
+ return luaL_error(L, "failed convert Lua value to JSON object");
+
+ if (key != NULL)
+ mrp_json_add(json, key, val);
+ else
+ if (!mrp_json_array_set(json, idx, val))
+ return luaL_error(L, "failed to set set JSON array element [%d]",
+ idx);
+
+ out:
+ lua_pop(L, 3);
+
+ return 0;
+}
+
+
+static int json_lua_stringify(lua_State *L)
+{
+ json_lua_t *lson = json_lua_check(L, 1);
+
+ lua_pushstring(L, mrp_json_object_to_string(lson->json));
+
+ return 1;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, JSON_LUA_CLASS,
+ { "JSON", mrp_json_lua_create });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_JSON_H__
+#define __MURPHY_LUA_JSON_H__
+
+#include <murphy/common/json.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+/** Create a Lua JSON object with an empty JSON object. */
+int mrp_json_lua_create(lua_State *L);
+
+/** Create a Lua JSON object, wrap the given JSON object, increase refcount. */
+void *mrp_json_lua_wrap(lua_State *L, mrp_json_t *json);
+
+/** Get and add a reference to a wrapped JSON object. */
+mrp_json_t *mrp_json_lua_unwrap(void *lson);
+
+/** Wrap the given JSON object, increase refcount and push it on the stack. */
+int mrp_json_lua_push(lua_State *L, mrp_json_t *json);
+
+/** Get the JSON object at the given stack position, increase refcount. */
+mrp_json_t *mrp_json_lua_get(lua_State *L, int idx);
+
+#endif /* __MURPHY_LUA_JSON_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/log.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+static int call_function(lua_State *L, const char *table, const char *method)
+{
+ int n, type, status;
+
+ if (table != NULL)
+ lua_getglobal(L, table);
+
+ if (lua_istable(L, -1)) {
+ lua_getfield(L, -1, method);
+ lua_remove(L, -2);
+
+ if (lua_isfunction(L, -1)) {
+ n = lua_gettop(L);
+ lua_insert(L, 1);
+
+ status = lua_pcall(L, n - 1, 1, 0);
+ }
+ else {
+ type = lua_type(L, -1);
+ lua_pop(L, 1);
+
+ if (type == LUA_TNIL)
+ lua_pushfstring(L, "non-existent member %s", method);
+ else
+ lua_pushfstring(L, "member %s is not a function", method);
+
+ status = -1;
+ }
+ }
+ else {
+ if (table != NULL)
+ lua_pushfstring(L, "%s is not a table", table);
+ else
+ lua_pushfstring(L, "requested field %s of a non-table", method);
+
+ status = -1;
+ }
+
+
+ return status;
+}
+
+
+static int log_msg(lua_State *L, int level)
+{
+ static int loaded = FALSE;
+ int n = lua_gettop(L);
+ lua_Debug caller;
+ const char *file, *func;
+ int line;
+ int top;
+
+ top = lua_gettop(L);
+
+ if (!loaded) {
+ luaopen_string(L);
+ loaded = TRUE;
+ }
+
+ if (lua_isuserdata(L, 1)) {
+ lua_remove(L, 1); /* remove self if any */
+ n--;
+ }
+
+ if (n > 1)
+ if (call_function(L, "string", "format") != 0)
+ goto out;
+
+ lua_getstack(L, 1, &caller);
+ if (lua_getinfo(L, "Snl", &caller)) {
+ func = caller.name ? caller.name : "<lua-function>";
+ file = caller.source ? caller.source : "<lua-source>";
+ line = caller.currentline;
+ }
+ else {
+ func = "<lua-function>";
+ line = 0;
+ file = "<lua-source>";
+ }
+
+ mrp_log_msg(level, file, line, func, "%s", lua_tostring(L, 1));
+
+ out:
+ lua_settop(L, top);
+
+ return 0;
+}
+
+
+static int log_info(lua_State *L)
+{
+ return log_msg(L, MRP_LOG_INFO);
+}
+
+
+static int log_warning(lua_State *L)
+{
+ return log_msg(L, MRP_LOG_WARNING);
+
+}
+
+
+static int log_error(lua_State *L)
+{
+ return log_msg(L, MRP_LOG_ERROR);
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, NULL,
+ { "info" , log_info },
+ { "warning", log_warning },
+ { "error" , log_error });
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/core/plugin.h>
+
+#include <murphy/core/lua-utils/include.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+static MRP_LIST_HOOK(included);
+static int include_disabled;
+
+static int include_lua(lua_State *L, const char *file, int try, int once)
+{
+ mrp_list_hook_t *files = once ? &included : NULL;
+ const char *dirs[2];
+
+ dirs[0] = mrp_lua_get_murphy_lua_config_dir();
+ dirs[1] = NULL;
+
+ if (mrp_lua_include_file(L, file, &dirs[0], files) == 0)
+ return 0;
+
+ if (try) {
+ if (errno == EINVAL) {
+ if (lua_type(L, -1) == LUA_TSTRING) {
+ mrp_log_warning("inclusion of '%s' failed with error '%s'",
+ file, lua_tostring(L, -1));
+ }
+ }
+
+ return 0;
+ }
+
+ return -1;
+
+}
+
+
+static int include_lua_file(lua_State *L, int try, int once)
+{
+ const char *file;
+ int narg, status;
+
+ if (include_disabled)
+ return luaL_error(L, "Lua inclusion is disabled.");
+
+ narg = lua_gettop(L);
+
+ switch (narg) {
+ case 1:
+ if (lua_type(L, -1) != LUA_TSTRING)
+ return luaL_error(L, "expecting <string> for inclusion");
+ break;
+ case 2:
+ if (lua_type(L, -2) != LUA_TUSERDATA ||
+ lua_type(L, -1) != LUA_TSTRING)
+ return luaL_error(L, "expecting <murphy>, <string> for inclusion");
+ break;
+ default:
+ return luaL_error(L, "expecting <string> for inclusion");
+ }
+
+ file = lua_tostring(L, -1);
+
+ status = include_lua(L, file, try, once);
+
+ if (status == 0 || try) {
+ lua_settop(L, 0);
+ return 0;
+ }
+ else {
+ mrp_log_error("failed to include%s Lua file '%s'.",
+ once ? "_once" : "", file);
+
+ return luaL_error(L, "failed to include file '%s' (%s)", file,
+ lua_type(L, -1) == LUA_TSTRING ?
+ lua_tostring(L, -1) : "<unknown error>");
+ }
+}
+
+
+static int try_luafile(lua_State *L)
+{
+ return include_lua_file(L, TRUE, FALSE);
+}
+
+
+static int try_once_luafile(lua_State *L)
+{
+ return include_lua_file(L, TRUE, TRUE);
+}
+
+
+static int include_luafile(lua_State *L)
+{
+ return include_lua_file(L, FALSE, FALSE);
+}
+
+
+static int include_once_luafile(lua_State *L)
+{
+ return include_lua_file(L, FALSE, TRUE);
+}
+
+
+static int disable_include(lua_State *L)
+{
+ MRP_UNUSED(L);
+
+ include_disabled = TRUE;
+
+ return 0;
+}
+
+
+static int open_lualib(lua_State *L)
+{
+ struct {
+ const char *name;
+ int (*loader)(lua_State *L);
+ } *lib, libs[] = {
+ { "math" , luaopen_math },
+ { "string" , luaopen_string },
+ { "io" , luaopen_io },
+ { "os" , luaopen_os },
+ { "table" , luaopen_table },
+ { "debug" , luaopen_debug },
+ { "package", luaopen_package },
+ { "base" , luaopen_base },
+ { NULL , NULL }
+ };
+ const char *name;
+ int i, n;
+
+ n = lua_gettop(L);
+
+ if (lua_isuserdata(L, 1)) {
+ lua_remove(L, 1); /* remove self if any */
+ n--;
+ }
+
+ if (n < 1)
+ return luaL_error(L, "%s called without any arguments", __FUNCTION__);
+
+ for (i = 1; i <= n; i++) {
+ luaL_checktype(L, 1, LUA_TSTRING);
+
+ name = lua_tostring(L, i);
+
+ for (lib = libs; lib->name != NULL; lib++)
+ if (!strcmp(lib->name, name))
+ break;
+
+ if (lib->loader != NULL) {
+ mrp_debug("loading Lua lib '%s' with %p...", name, lib->loader);
+ lib->loader(L);
+ }
+ else {
+ if (include_disabled)
+ return luaL_error(L, "Lua inclusion is disabled.");
+
+ if (include_lua(L, name, FALSE, TRUE) < 0)
+ return luaL_error(L, "failed to load unknown "
+ "Lua library '%s'", name);
+ }
+ }
+
+ lua_settop(L, 0);
+ return 0;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, NULL,
+ { "open_lualib" , open_lualib },
+ { "include" , include_luafile },
+ { "include_once" , include_once_luafile },
+ { "try_include" , try_luafile },
+ { "try_include_once", try_once_luafile },
+ { "disable_include" , disable_include });
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-decision/mdb.h>
+#include <murphy/core/lua-decision/element.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+static mrp_context_t *context;
+static MRP_LIST_HOOK(bindings);
+static MRP_LIST_HOOK(pending);
+static int debug_level;
+static char *config_file;
+static char *config_dir;
+
+static lua_Alloc setup_allocator(void);
+
+
+static int create_murphy_object(lua_State *L)
+{
+ mrp_lua_murphy_t *m;
+
+ m = (mrp_lua_murphy_t *)lua_newuserdata(L, sizeof(*m));
+
+ m->ctxp = &context;
+
+ luaL_getmetatable(L, "murphy");
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+
+static int register_murphy(mrp_context_t *ctx)
+{
+ static luaL_reg functions[] = {
+ { "get", create_murphy_object },
+ { NULL , NULL }
+ };
+ lua_State *L = ctx->lua_state;
+
+ luaL_newmetatable(L, "murphy");
+ lua_pushliteral(L, "__index"); /* murphy.__index = murphy */
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3);
+
+ luaL_openlib(L, "murphy", functions, 0);
+
+ return TRUE;
+}
+
+
+static int register_bindings(mrp_lua_bindings_t *b)
+{
+ lua_State *L = context->lua_state;
+ luaL_reg *m;
+
+ luaL_getmetatable(L, b->meta);
+
+ for (m = b->methods; m->name != NULL; m++) {
+ lua_pushstring(L, m->name);
+ lua_pushcfunction(L, m->func);
+ lua_rawset(L, -3);
+ }
+
+ if (b->classdef != NULL) {
+ if (mrp_lua_create_object_class(L, b->classdef) < 0) {
+ mrp_log_error("Object class registration failed.");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+int mrp_lua_register_murphy_bindings(mrp_lua_bindings_t *b)
+{
+ mrp_context_t *ctx;
+ lua_State *L;
+
+ mrp_list_init(&b->hook);
+ mrp_list_append(&bindings, &b->hook);
+
+ if ((ctx = context) != NULL && (L = ctx->lua_state) != NULL)
+ return register_bindings(b);
+ else
+ return TRUE;
+}
+
+
+static void init_lua_utils(lua_State *L)
+{
+ mrp_create_funcbridge_class(L);
+ mrp_create_funcarray_class(L);
+}
+
+
+static void init_lua_decision(lua_State *L)
+{
+ mrp_lua_create_mdb_class(L);
+ mrp_lua_create_element_class(L);
+}
+
+
+static lua_State *init_lua(void)
+{
+ lua_Alloc A = setup_allocator();
+ lua_State *L;
+
+ if (A == NULL)
+ L = luaL_newstate();
+ else
+ L = lua_newstate(A, NULL);
+
+ if (L != NULL) {
+ luaopen_base(L);
+ init_lua_utils(L);
+ init_lua_decision(L);
+ }
+
+ return L;
+}
+
+
+lua_State *mrp_lua_set_murphy_context(mrp_context_t *ctx)
+{
+ lua_State *L;
+ mrp_list_hook_t *p, *n;
+ mrp_lua_bindings_t *b;
+ int success;
+
+ if (context == NULL) {
+ L = init_lua();
+
+ if (L != NULL) {
+ ctx->lua_state = L;
+ context = ctx;
+
+ if (register_murphy(ctx)) {
+ success = TRUE;
+
+ init_lua_utils(L);
+ init_lua_decision(L);
+
+ mrp_list_foreach(&bindings, p, n) {
+ b = mrp_list_entry(p, typeof(*b), hook);
+ success &= register_bindings(b);
+ }
+
+ return L;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+
+void mrp_lua_set_murphy_lua_config_file(const char *path)
+{
+ if (config_file == NULL && path != NULL) {
+ config_file = mrp_strdup(path);
+ mrp_log_info("Lua config file is: '%s'.", config_file);
+ }
+}
+
+
+mrp_context_t *mrp_lua_check_murphy_context(lua_State *L, int index)
+{
+ mrp_lua_murphy_t *m;
+
+ m = (mrp_lua_murphy_t *)luaL_checkudata(L, index, "murphy");
+ luaL_argcheck(L, m, index, "murphy object expected");
+
+ if (*m->ctxp == NULL)
+ return (void *)(ptrdiff_t)luaL_error(L, "murphy context is not set");
+ else
+ return *m->ctxp;
+}
+
+
+mrp_context_t *mrp_lua_get_murphy_context(void)
+{
+ return context;
+}
+
+
+lua_State *mrp_lua_get_lua_state(void)
+{
+ if (context != NULL)
+ return context->lua_state;
+ else
+ return NULL;
+}
+
+const char *mrp_lua_get_murphy_lua_config_dir(void)
+{
+ char *base, dir[PATH_MAX];
+ size_t offs, len;
+
+ if (config_file == NULL)
+ return NULL;
+
+ if (config_dir == NULL) {
+ if (config_file[0] != '/') {
+ if (getcwd(dir, sizeof(dir)) == NULL)
+ return NULL;
+ else {
+ offs = strlen(dir);
+
+ if (offs >= sizeof(dir) - 1)
+ return NULL;
+
+ dir[offs++] = '/';
+ dir[offs] = '\0';
+ }
+ }
+ else
+ offs = 0;
+
+ base = strrchr(config_file, '/');
+
+ if (base != NULL)
+ while (base > config_file && base[-1] == '/')
+ base--;
+
+ if (base != NULL && base > config_file) {
+ len = base - config_file;
+
+ if (sizeof(dir) - offs - 1 <= len)
+ return NULL;
+
+ strncpy(dir + offs, config_file, len);
+ offs += len;
+ dir[offs] = '\0';
+
+ config_dir = mrp_strdup(dir);
+
+ mrp_log_info("Lua config directory is '%s'.", config_dir);
+ }
+ }
+
+ return config_dir;
+}
+
+
+/*
+ * runtime debugging
+ */
+
+void mrp_lua_dump_stack(lua_State *L, const char *prefix)
+{
+ char prebuf[256];
+ int i, n;
+
+ n = lua_gettop(L);
+
+ if (prefix != NULL && *prefix) {
+ snprintf(prebuf, sizeof(prebuf), "%s: ", prefix);
+ prefix = prebuf;
+ }
+ else
+ prefix = "";
+
+ if (n > 0) {
+ mrp_debug("%sLua stack dump (%d items):", prefix, n);
+
+ for (i = 1; i <= n; i++)
+ mrp_debug("%s#%d(%d): %s", prefix, -i, (n - i) + 1,
+ lua_typename(L, lua_type(L, -i)));
+ }
+ else
+ mrp_debug("%sLua stack is empty", prefix);
+}
+
+
+static void lua_debug(lua_State *L, lua_Debug *ar)
+{
+#define RUNNING(_ar, _what) ((_ar)->what != NULL && !strcmp((_ar)->what, _what))
+#define ALIGNFMT "%*.*s"
+#define ALIGNARG 4 * depth, 4 * depth, ""
+
+ static int depth = 0;
+
+ lua_Debug f;
+ const char *type, *name;
+ char loc[1024];
+
+ switch (ar->event) {
+ case LUA_HOOKRET:
+ depth--;
+ mrp_debug(ALIGNFMT"<= return", ALIGNARG);
+ break;
+
+#ifdef LUA_HOOKTAILRET
+ case LUA_HOOKTAILRET:
+ depth--;
+ mrp_debug(ALIGNFMT"<= tail return", ALIGNARG);
+ break;
+#endif
+
+ case LUA_HOOKCALL:
+#ifdef LUA_HOOKTAILCALL
+ case LUA_HOOKTAILCALL:
+#endif
+
+ mrp_clear(&f);
+ if (lua_getstack(L, 1, &f) && lua_getinfo(L, "Snl", &f)) {
+ if (RUNNING(&f, "C")) type = "Lua-C";
+ else if (RUNNING(&f, "Lua")) type = "Lua";
+ else if (RUNNING(&f, "main")) type = "Lua-main";
+ else if (RUNNING(&f, "tail")) {
+ mrp_debug(ALIGNFMT"<=> tail-call", ALIGNARG);
+#ifndef LUA_HOOKTAILRET
+ depth++;
+#endif
+ return;
+ }
+ else
+ type = "???";
+
+ name = f.name ? f.name : NULL;
+
+ if (f.currentline != -1 && f.short_src[0])
+ snprintf(loc, sizeof(loc), "@ %s:%d", f.short_src,
+ f.currentline);
+ else
+ loc[0] = '\0';
+
+ if (name)
+ mrp_debug(ALIGNFMT"=> %s %s %s", ALIGNARG, type, name, loc);
+ else
+ mrp_debug(ALIGNFMT"=> %s %s", ALIGNARG, type, loc);
+ }
+ else
+ mrp_debug(ALIGNFMT"=> Lua", ALIGNARG);
+
+ depth++;
+ break;
+
+ case LUA_HOOKLINE:
+ mrp_clear(&f);
+
+ if (lua_getstack(L, 1, &f) && lua_getinfo(L, "Snl", &f))
+ mrp_debug(ALIGNFMT" @ %s:%d", ALIGNARG, f.short_src, f.currentline);
+ else
+ mrp_debug(ALIGNFMT" @ line %d", ALIGNARG, ar->currentline);
+ break;
+
+ default:
+ break;
+ }
+
+#undef RUNNING
+#undef ALIGNFMT
+#undef ALIGNARG
+}
+
+
+static int setup_debug_hook(int mask)
+{
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ lua_State *L = ctx ? ctx->lua_state : NULL;
+
+ return (L != NULL && lua_sethook(L, lua_debug, mask, 0));
+}
+
+
+static void clear_debug_hook(void)
+{
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ lua_State *L = ctx ? ctx->lua_state : NULL;
+
+ if (L != NULL)
+ lua_sethook(L, lua_debug, 0, 0);
+}
+
+
+int mrp_lua_set_debug(mrp_lua_debug_t level)
+{
+ const char *cfg;
+ int ena, mask, success;
+
+ if (debug_level)
+ clear_debug_hook();
+
+ mask = 0;
+
+ switch (level) {
+ case MRP_LUA_DEBUG_DISABLED:
+ ena = FALSE;
+ cfg = "-lua_debug";
+ break;
+
+ case MRP_LUA_DEBUG_DETAILED:
+ mask |= LUA_MASKLINE;
+ case MRP_LUA_DEBUG_ENABLED:
+ mask |= LUA_MASKCALL | LUA_MASKRET;
+ ena = TRUE;
+ cfg = "+lua_debug";
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ if (ena) {
+ success = setup_debug_hook(mask);
+ mrp_debug_enable(TRUE);
+ }
+ else
+ success = TRUE;
+
+ mrp_debug_set_config(cfg);
+
+ if (success)
+ debug_level = level;
+
+ return success;
+}
+
+
+/*
+ * Lua memory allocation tracking
+ *
+ * This is intended for debugging and diagnostic purposes. By default
+ * tracking Lua allocations is off. Lua allocation tracking can be
+ * enabled by including lua=true in the environment variable controlling
+ * Murphy memory management (__MURPHY_MM_CONFIG).
+ */
+
+/*
+ * a tracked block of memory allocated for Lua by us
+ */
+
+#define MEMBLK_SIZE(lsize) \
+ ((lsize) ? ((void *)&((memblk_t *)NULL)->mem[(lsize)] - NULL) : 0)
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to hash bucket */
+ char mem[0]; /* memory passed on to Lua */
+} memblk_t;
+
+static MRP_LIST_HOOK(memblks); /* allocated blocks */
+
+
+static inline void *memblk_store(memblk_t *blk)
+{
+ mrp_list_init(&blk->hook);
+ mrp_list_append(&memblks, &blk->hook);
+
+ return &blk->mem[0];
+}
+
+
+static void memblk_clear(memblk_t *blk)
+{
+ mrp_list_delete(&blk->hook);
+}
+
+
+static inline memblk_t *ptr_to_memblk(void *ptr)
+{
+ memblk_t *blk;
+
+ if (ptr != NULL)
+ blk = (memblk_t *)(((char *)ptr) - MRP_OFFSET(typeof(*blk), mem[0]));
+ else
+ blk = NULL;
+
+ return blk;
+}
+
+
+static inline void *memblk_to_ptr(memblk_t *blk)
+{
+ if (blk != NULL)
+ return (void *)&blk->mem[0];
+ else
+ return NULL;
+}
+
+
+static inline void *memblk_alloc(size_t lsize)
+{
+ memblk_t *blk;
+
+ blk = mrp_alloc(MEMBLK_SIZE(lsize));
+
+ if (blk != NULL) {
+ return memblk_store(blk);
+ }
+ else
+ return NULL;
+}
+
+
+static inline void *memblk_resize(memblk_t *blk, size_t osize, size_t nsize)
+{
+ memblk_clear(blk);
+
+ if (mrp_reallocz(blk, osize, nsize)) {
+ return memblk_store(blk);
+ }
+ else {
+ mrp_free(blk);
+ return NULL;
+ }
+}
+
+
+static inline void memblk_free(memblk_t *blk)
+{
+ if (blk != NULL) {
+ memblk_clear(blk);
+ mrp_free(blk);
+ }
+}
+
+
+static void *lua_alloc(void *ud, void *optr, size_t olsize, size_t nlsize)
+{
+ memblk_t *oblk;
+ size_t obsize, nbsize;
+ void *nptr;
+
+ MRP_UNUSED(ud);
+
+ mrp_debug("Lua allocation request <%p, %zd, %zd>", optr, olsize, nlsize);
+
+ oblk = ptr_to_memblk(optr);
+
+ if (nlsize > 0) {
+ nbsize = MEMBLK_SIZE(nlsize);
+
+ if (oblk != NULL) {
+ obsize = MEMBLK_SIZE(olsize);
+ nptr = memblk_resize(oblk, obsize, nbsize);
+ }
+ else
+ nptr = memblk_alloc(nbsize);
+ }
+ else {
+ memblk_free(oblk);
+ nptr = NULL;
+ }
+
+ mrp_debug("Lua allocation reply %p", nptr);
+
+ return nptr;
+}
+
+
+static lua_Alloc setup_allocator(void)
+{
+ int debug;
+
+ debug = mrp_mm_config_bool("lua", FALSE);
+
+ if (!debug) {
+ mrp_debug("%s not set to debug*, using native Lua allocator",
+ MRP_MM_CONFIG_ENVVAR);
+ return NULL;
+ }
+ else {
+ mrp_debug("Lua memory tracking enabled, overriding native allocator");
+
+ mrp_list_init(&memblks);
+ mrp_lua_track_objects(true);
+
+ return lua_alloc;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+static int stringify_table(lua_State *L, int index,
+ char **bufp, int *sizep, int *offsp);
+
+static int plugin_exists(lua_State *L)
+{
+ mrp_context_t *ctx;
+ const char *name;
+
+ ctx = mrp_lua_check_murphy_context(L, 1);
+ luaL_checktype(L, 2, LUA_TSTRING);
+
+ name = lua_tostring(L, 2);
+
+ mrp_debug("lua: check if plugin '%s' exists", name);
+
+ lua_pushboolean(L, mrp_plugin_exists(ctx, name));
+
+ return 1;
+}
+
+
+static int plugin_loaded(lua_State *L)
+{
+ mrp_context_t *ctx;
+ const char *name;
+
+ ctx = mrp_lua_check_murphy_context(L, 1);
+ luaL_checktype(L, 2, LUA_TSTRING);
+
+ name = lua_tostring(L, 2);
+
+ mrp_debug("lua: check if plugin '%s' is loaded", name);
+
+ lua_pushboolean(L, mrp_plugin_running(ctx, name));
+
+ return 1;
+}
+
+
+static int ensure_buffer(char **bufp, int *sizep, int *offsp, int len)
+{
+ int spc, size, diff;
+
+ spc = *sizep - *offsp;
+ if (spc < len) {
+ diff = len - spc;
+
+ if (diff > *sizep)
+ size = *sizep + diff;
+ else
+ size = *sizep * 2;
+
+ if (!mrp_realloc(*bufp, size))
+ return -1;
+ else
+ *sizep = size;
+ }
+
+ return 0;
+}
+
+
+static int push_buffer(char **bufp, int *sizep, int *offsp,
+ const char *str, int len)
+{
+ if (len <= 0)
+ len = strlen(str);
+
+ if (ensure_buffer(bufp, sizep, offsp, len + 1) == 0) {
+ strcpy(*bufp + *offsp, str);
+ *offsp += len;
+
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+static int stringify_string(lua_State *L, int index,
+ char **bufp, int *sizep, int *offsp)
+{
+ const char *str;
+ size_t size;
+ int len;
+
+ str = lua_tolstring(L, index, &size);
+ len = (int)size;
+
+ if (ensure_buffer(bufp, sizep, offsp, len + 3) < 0)
+ return -1;
+
+ snprintf(*bufp + *offsp, len + 3, "'%s'", str);
+ *offsp += len + 2;
+
+ return 0;
+}
+
+
+static int stringify_number(lua_State *L, int index,
+ char **bufp, int *sizep, int *offsp)
+{
+ char num[64];
+ double d;
+ int i, len;
+
+ if ((d = lua_tonumber(L, index)) == 1.0 * (i = lua_tointeger(L, index)))
+ len = snprintf(num, sizeof(num), "%d", i);
+ else
+ len = snprintf(num, sizeof(num), "%f", d);
+
+ return push_buffer(bufp, sizep, offsp, num, len);
+}
+
+
+static int stringify_boolean(lua_State *L, int index,
+ char **bufp, int *sizep, int *offsp)
+{
+ const char *bln;
+ int len;
+
+ if (lua_toboolean(L, index)) {
+ bln = "true";
+ len = 4;
+ }
+ else {
+ bln = "false";
+ len = 5;
+ }
+
+ return push_buffer(bufp, sizep, offsp, bln, len);
+}
+
+
+static int stringify_object(lua_State *L, int index,
+ char **bufp, int *sizep, int *offsp)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TSTRING: return stringify_string(L, index, bufp, sizep, offsp);
+ case LUA_TNUMBER: return stringify_number(L, index, bufp, sizep, offsp);
+ case LUA_TBOOLEAN: return stringify_boolean(L, index, bufp, sizep, offsp);
+ case LUA_TTABLE: return stringify_table(L, index, bufp, sizep, offsp);
+ default:
+ errno = EINVAL;
+ }
+
+ return -1;
+}
+
+
+static int stringify_table(lua_State *L, int index,
+ char **bufp, int *sizep, int *offsp)
+{
+ const char *sep, *p;
+ char key[256];
+ int arr;
+ int i, d, idx, len;
+
+ arr = TRUE;
+
+ lua_pushnil(L);
+
+ /*
+ * check what the table should be converted to (array or dictionary)
+ */
+ idx = -1;
+ while (arr && lua_next(L, index - 1)) {
+ switch (lua_type(L, -2)) {
+ case LUA_TBOOLEAN:
+ case LUA_TTABLE:
+ default:
+ invalid:
+ lua_pop(L, 3);
+ return -1;
+
+ case LUA_TNUMBER:
+ d = lua_tonumber(L, -2);
+ i = lua_tointeger(L, -2);
+
+ if (d != 1.0 * i || (idx >= 0 && i != idx + 1))
+ goto invalid;
+ else
+ idx = i;
+ break;
+
+ case LUA_TSTRING:
+ lua_pop(L, 1);
+ arr = FALSE;
+ break;
+ }
+
+ lua_pop(L, 1);
+ }
+
+
+ /*
+ * convert either to an array or a dictionary
+ */
+
+ if (push_buffer(bufp, sizep, offsp, arr ? "[" : "{", 1))
+ return -1;
+
+ sep = "";
+ lua_pushnil(L);
+ while (lua_next(L, index - 1)) {
+ if (!arr) {
+ len = snprintf(key, sizeof(key), "%s'%s':", sep,
+ lua_tostring(L, -2));
+ p = key;
+ }
+ else {
+ p = (char *)sep;
+ len = strlen(sep);
+ }
+
+ if (push_buffer(bufp, sizep, offsp, p, len) < 0)
+ return -1;
+
+ if (stringify_object(L, -1, bufp, sizep, offsp) < 0) {
+ lua_pop(L, 3);
+ return -1;
+ }
+
+ lua_pop(L, 1);
+ sep = ",";
+ }
+
+ if (push_buffer(bufp, sizep, offsp, arr ? "]" : "}", 1) < 0)
+ return -1;
+ else
+ return 0;
+}
+
+
+static int load(lua_State *L, int may_fail)
+{
+ mrp_context_t *ctx;
+ mrp_plugin_t *plugin;
+ char name[256], instbuf[256];
+ const char *instance, *argerr;
+ mrp_plugin_arg_t args[256];
+ int narg, n, type, t, success;
+ char *json;
+ int size, offs;
+
+ ctx = mrp_lua_check_murphy_context(L, 1);
+ n = lua_gettop(L);
+
+ if (n < 2 || n > 4)
+ return luaL_error(L, "%s called with incorrect arguments",
+ __FUNCTION__);
+
+ luaL_checktype(L, 2, LUA_TSTRING);
+ snprintf(name, sizeof(name), "%s", lua_tostring(L, 2));
+ instance = NULL;
+ narg = 0;
+
+ mrp_debug("lua: %sload-plugin '%s'", may_fail ? "try-" : "", name);
+
+ switch (n) {
+ case 2:
+ break;
+
+ case 3:
+ type = lua_type(L, 3);
+
+ if (type == LUA_TTABLE) {
+ t = 3;
+ goto parse_arguments;
+ }
+ else if (type == LUA_TSTRING) {
+ snprintf(instbuf, sizeof(instbuf), "%s", lua_tostring(L, 3));
+ instance = instbuf;
+ }
+ else
+ return luaL_error(L, "%s expects string or table as 2nd argument",
+ __FUNCTION__);
+ break;
+
+ case 4:
+ default:
+ luaL_checktype(L, 3, LUA_TSTRING);
+ luaL_checktype(L, 4, LUA_TTABLE);
+ snprintf(instbuf, sizeof(instbuf), "%s", lua_tostring(L, 3));
+ instance = instbuf;
+ t = 4;
+ parse_arguments:
+ mrp_clear(&args);
+ lua_pushnil(L);
+ while (lua_next(L, t) != 0) {
+ if (narg >= (int)MRP_ARRAY_SIZE(args)) {
+ argerr = "too many plugin arguments";
+ goto arg_error;
+ }
+
+ if (lua_type(L, -2) != LUA_TSTRING) {
+ argerr = "non-string argument table key";
+ goto arg_error;
+ }
+
+ args[narg].type = MRP_PLUGIN_ARG_TYPE_STRING;
+ args[narg].key = mrp_strdup(lua_tostring(L, -2));
+
+ switch (lua_type(L, -1)) {
+ case LUA_TSTRING:
+ case LUA_TNUMBER:
+ args[narg].str = mrp_strdup(lua_tostring(L, -1));
+ break;
+ case LUA_TBOOLEAN:
+ args[narg].str = mrp_strdup(lua_toboolean(L, -1) ?
+ "true" : "false");
+ break;
+ case LUA_TTABLE:
+ json = NULL;
+ size = 0;
+ offs = 0;
+ if (stringify_table(L, -1, &json, &size, &offs) == 0)
+ args[narg].str = json;
+ else {
+ argerr = "failed to json-stringify Lua table";
+ goto arg_error;
+ }
+ break;
+ default:
+ argerr = "invalid argument table value";
+ goto arg_error;
+ }
+ mrp_debug("lua: argument #%d: '%s' = '%s'", narg,
+ args[narg].key, args[narg].str);
+ narg++;
+
+ lua_pop(L, 1);
+ }
+ break;
+ }
+
+ plugin = mrp_load_plugin(ctx, name, instance, narg ? args : NULL, narg);
+
+ if (plugin != NULL) {
+ plugin->may_fail = may_fail;
+
+ success = TRUE;
+ }
+ else {
+ success = FALSE;
+
+ if (!may_fail)
+ return luaL_error(L, "failed to load plugin %s (as instance %s)",
+ name, instance ? instance : name);
+ }
+
+ while (narg > 0) {
+ mrp_free(args[narg - 1].key);
+ mrp_free(args[narg - 1].str);
+ narg--;
+ }
+
+ lua_pushboolean(L, success);
+ return 1;
+
+ arg_error:
+ while (narg > 0) {
+ mrp_free(args[narg - 1].key);
+ mrp_free(args[narg - 1].str);
+ narg--;
+ }
+
+ return luaL_error(L, "plugin argument table error: %s", argerr);
+}
+
+
+static int load_plugin(lua_State *L)
+{
+ return load(L, FALSE);
+}
+
+
+static int try_load_plugin(lua_State *L)
+{
+ return load(L, TRUE);
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, NULL,
+ { "plugin_exists" , plugin_exists },
+ { "plugin_loaded" , plugin_loaded },
+ { "load_plugin" , load_plugin },
+ { "try_load_plugin", try_load_plugin });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#define SIGHANDLER_LUA_CLASS MRP_LUA_CLASS(sighandler, lua)
+
+/*
+ * Lua sighandler object
+ */
+
+typedef struct {
+ lua_State *L; /* Lua execution context */
+ mrp_mainloop_t *ml; /* Murphy mainloop */
+ mrp_sighandler_t *h; /* associated murphy sighandler */
+ int signum; /* signal number */
+ int callback; /* reference to callback */
+ bool oneshot; /* true for one-shot sighandlers */
+} sighandler_lua_t;
+
+
+static int sighandler_lua_create(lua_State *L);
+static void sighandler_lua_destroy(void *data);
+static void sighandler_lua_changed(void *data, lua_State *L, int member);
+static ssize_t sighandler_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+static int sighandler_lua_enable(lua_State *L);
+static int sighandler_lua_disable(lua_State *L);
+
+/*
+ * Lua sighandler class
+ */
+
+#define OFFS(m) MRP_OFFSET(sighandler_lua_t, m)
+#define RDONLY MRP_LUA_CLASS_READONLY
+#define NOTIFY MRP_LUA_CLASS_NOTIFY
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+
+MRP_LUA_METHOD_LIST_TABLE(sighandler_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(sighandler_lua_create)
+ MRP_LUA_METHOD(disable, sighandler_lua_disable)
+ MRP_LUA_METHOD(enable , sighandler_lua_enable));
+
+MRP_LUA_METHOD_LIST_TABLE(sighandler_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (sighandler_lua_create));
+
+MRP_LUA_MEMBER_LIST_TABLE(sighandler_lua_members,
+ MRP_LUA_CLASS_INTEGER("signal" , OFFS(signum) , NULL, NULL, RDONLY )
+ MRP_LUA_CLASS_LFUNC ("callback" , OFFS(callback) , NULL, NULL, NOFLAGS)
+ MRP_LUA_CLASS_BOOLEAN("oneshot" , OFFS(oneshot) , NULL, NULL, NOFLAGS));
+
+typedef enum {
+ SIGHANDLER_MEMBER_SIGNAL,
+ SIGHANDLER_MEMBER_CALLBACK,
+ SIGHANDLER_MEMBER_ONESHOT,
+} sighandler_member_t;
+
+MRP_LUA_DEFINE_CLASS(sighandler, lua, sighandler_lua_t, sighandler_lua_destroy,
+ sighandler_lua_methods, sighandler_lua_overrides,
+ sighandler_lua_members, NULL, sighandler_lua_changed,
+ sighandler_lua_tostring, NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_DYNAMIC);
+
+
+static void sighandler_lua_cb(mrp_sighandler_t *hlr, int sig, void *user_data)
+{
+ sighandler_lua_t *h = (sighandler_lua_t *)user_data;
+ int one = h->oneshot;
+ const char *s = strsignal(sig);
+ int top;
+
+ MRP_UNUSED(hlr);
+
+ top = lua_gettop(h->L);
+
+ if (mrp_lua_object_deref_value(h, h->L, h->callback, false)) {
+ mrp_lua_push_object(h->L, h);
+ if (s != NULL)
+ lua_pushstring(h->L, s);
+ else
+ lua_pushinteger(h->L, sig);
+
+ if (lua_pcall(h->L, 2, 0, 0) != 0)
+ mrp_log_error("failed to invoke Lua sighandler callback");
+ }
+
+ if (one) {
+ mrp_del_sighandler(h->h);
+ h->h = NULL;
+ }
+
+ lua_settop(h->L, top);
+}
+
+
+static void sighandler_lua_changed(void *data, lua_State *L, int member)
+{
+ sighandler_lua_t *h = (sighandler_lua_t *)data;
+
+ MRP_UNUSED(L);
+ MRP_UNUSED(h);
+
+ mrp_debug("sighandler member #%d (%s) changed", member,
+ sighandler_lua_members[member].name);
+}
+
+
+static int sighandler_lua_create(lua_State *L)
+{
+
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ char e[128] = "";
+ sighandler_lua_t *h;
+ int narg;
+
+ if (ctx == NULL)
+ luaL_error(L, "failed to get murphy context");
+
+ narg = lua_gettop(L);
+
+ h = (sighandler_lua_t *)mrp_lua_create_object(L, SIGHANDLER_LUA_CLASS,
+ NULL, 0);
+ h->L = L;
+ h->ml = ctx->ml;
+ h->callback = LUA_NOREF;
+
+ switch (narg) {
+ case 1:
+ break;
+ case 2:
+ if (mrp_lua_init_members(h, L, -2, e, sizeof(e)) != 1)
+ return luaL_error(L, "failed to initialize sighandler (%s)", e);
+ break;
+ default:
+ return luaL_error(L, "expecting 0 or 1 arguments, got %d", narg);
+ }
+
+ if (h->signum)
+ h->h = mrp_add_sighandler(h->ml, h->signum, sighandler_lua_cb, h);
+ else
+ return luaL_error(L, "signal number must be set in constructor");
+
+ if (h->h == NULL)
+ return luaL_error(L, "failed to create Murphy sighandler");
+
+ return 1;
+}
+
+
+static void sighandler_lua_destroy(void *data)
+{
+ sighandler_lua_t *h = (sighandler_lua_t *)data;
+
+ mrp_debug("destroying Lua sighandler %p", data);
+
+ mrp_del_sighandler(h->h);
+ h->h = NULL;
+
+ mrp_lua_object_unref_value(h, h->L, h->callback);
+
+ h->callback = LUA_NOREF;
+}
+
+
+static sighandler_lua_t *sighandler_lua_check(lua_State *L, int idx)
+{
+ return (sighandler_lua_t *)mrp_lua_check_object(L,
+ SIGHANDLER_LUA_CLASS, idx);
+}
+
+
+static ssize_t sighandler_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ sighandler_lua_t *h = (sighandler_lua_t *)data;
+ const char *s = strsignal(h->signum);
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{%ssighandler %p of '%s'}",
+ h->oneshot ? "oneshot " : "", h->h,
+ s ? s : "unknow signal");
+ }
+}
+
+
+static int sighandler_lua_enable(lua_State *L)
+{
+ sighandler_lua_t *h = sighandler_lua_check(L, -1);
+
+ if (h == NULL) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ if (h->h == NULL && h->signum != 0)
+ if (h->callback != LUA_NOREF && h->callback != LUA_REFNIL)
+ mrp_add_sighandler(h->ml, h->signum, sighandler_lua_cb, h);
+
+ lua_pushboolean(L, h->h != NULL);
+
+ return 1;
+}
+
+
+static int sighandler_lua_disable(lua_State *L)
+{
+ sighandler_lua_t *h = sighandler_lua_check(L, -1);
+
+ if (h == NULL) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ mrp_del_sighandler(h->h);
+ h->h = NULL;
+
+ lua_pushboolean(L, true);
+
+ return 1;
+}
+
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, SIGHANDLER_LUA_CLASS,
+ { "SigHandler", sighandler_lua_create });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+/*
+ * Lua timer object
+ */
+
+#define TIMER_LUA_CLASS MRP_LUA_CLASS(timer, lua)
+
+typedef struct {
+ lua_State *L; /* Lua execution context */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_timer_t *t; /* associated murphy timer */
+ unsigned int msecs; /* timer interval in milliseconds */
+ int callback; /* reference to callback */
+ bool oneshot; /* true for one-shot timers */
+} timer_lua_t;
+
+
+static int timer_lua_create(lua_State *L);
+static void timer_lua_destroy(void *data);
+static void timer_lua_changed(void *data, lua_State *L, int member);
+static ssize_t timer_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+static int timer_lua_start(lua_State *L);
+static int timer_lua_stop(lua_State *L);
+
+
+/*
+ * Lua timer class
+ */
+
+#define OFFS(m) MRP_OFFSET(timer_lua_t, m)
+#define RDONLY MRP_LUA_CLASS_READONLY
+#define NOTIFY MRP_LUA_CLASS_NOTIFY
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+
+MRP_LUA_METHOD_LIST_TABLE(timer_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(timer_lua_create)
+ MRP_LUA_METHOD(stop , timer_lua_stop)
+ MRP_LUA_METHOD(start, timer_lua_start));
+
+MRP_LUA_METHOD_LIST_TABLE(timer_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (timer_lua_create));
+
+MRP_LUA_MEMBER_LIST_TABLE(timer_lua_members,
+ MRP_LUA_CLASS_INTEGER("interval", OFFS(msecs) , NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_LFUNC ("callback", OFFS(callback), NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_BOOLEAN("oneshot" , OFFS(oneshot) , NULL, NULL, NOTIFY));
+
+
+typedef enum {
+ TIMER_MEMBER_INTERVAL,
+ TIMER_MEMBER_CALLBACK,
+ TIMER_MEMBER_ONESHOT
+} timer_member_t;
+
+MRP_LUA_DEFINE_CLASS(timer, lua, timer_lua_t, timer_lua_destroy,
+ timer_lua_methods, timer_lua_overrides,
+ timer_lua_members, NULL, timer_lua_changed,
+ timer_lua_tostring, NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_DYNAMIC);
+
+
+static void timer_lua_cb(mrp_timer_t *timer, void *user_data)
+{
+ timer_lua_t *t = (timer_lua_t *)user_data;
+ int one = t->oneshot;
+ int top;
+
+ MRP_UNUSED(timer);
+
+ top = lua_gettop(t->L);
+
+ if (mrp_lua_object_deref_value(t, t->L, t->callback, false)) {
+ mrp_lua_push_object(t->L, t);
+
+ if (lua_pcall(t->L, 1, 0, 0) != 0) {
+ mrp_log_error("failed to invoke Lua timer callback, stopping");
+ mrp_del_timer(t->t);
+ t->t = NULL;
+ }
+ }
+
+ if (one) {
+ mrp_del_timer(t->t);
+ t->t = NULL;
+ }
+
+ lua_settop(t->L, top);
+}
+
+
+static void timer_lua_changed(void *data, lua_State *L, int member)
+{
+ timer_lua_t *t = (timer_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("timer member #%d (%s) changed", member,
+ timer_lua_members[member].name);
+
+ switch (member) {
+ case TIMER_MEMBER_INTERVAL:
+ if (t->t != NULL)
+ mrp_mod_timer(t->t, t->msecs);
+ else {
+ enable:
+ t->t = mrp_add_timer(t->ctx->ml, t->msecs, timer_lua_cb, t);
+ if (t->t == NULL)
+ luaL_error(L, "failed to create Murphy timer");
+ }
+ break;
+
+ case TIMER_MEMBER_CALLBACK:
+ if (t->callback == LUA_NOREF || t->callback == LUA_REFNIL) {
+ mrp_del_timer(t->t);
+ t->t = NULL;
+ }
+ else {
+ if (t->t == NULL)
+ goto enable;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static int timer_lua_create(lua_State *L)
+{
+
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ char e[128] = "";
+ timer_lua_t *t;
+ int narg;
+
+ if (ctx == NULL)
+ luaL_error(L, "failed to get murphy context");
+
+ narg = lua_gettop(L);
+
+ t = (timer_lua_t *)mrp_lua_create_object(L, TIMER_LUA_CLASS, NULL, 0);
+
+ t->L = L;
+ t->ctx = ctx;
+ t->callback = LUA_NOREF;
+ t->msecs = 5000;
+
+ switch (narg) {
+ case 1:
+ break;
+ case 2:
+ if (mrp_lua_init_members(t, L, -2, e, sizeof(e)) != 1)
+ return luaL_error(L, "failed to initialize timer members (%s)", e);
+ break;
+ default:
+ return luaL_error(L, "expecting 0 or 1 constructor arguments, "
+ "got %d", narg);
+ }
+
+ if (t->callback != LUA_NOREF && t->callback != LUA_REFNIL && t->t == NULL) {
+ t->t = mrp_add_timer(t->ctx->ml, t->msecs, timer_lua_cb, t);
+
+ if (t->t == NULL)
+ return luaL_error(L, "failed to create Murphy timer");
+ }
+
+ return 1;
+}
+
+
+static void timer_lua_destroy(void *data)
+{
+ timer_lua_t *t = (timer_lua_t *)data;
+
+ mrp_debug("destroying Lua timer %p", data);
+
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ mrp_lua_object_unref_value(t, t->L, t->callback);
+
+ t->callback = LUA_NOREF;
+}
+
+
+static timer_lua_t *timer_lua_check(lua_State *L, int idx)
+{
+ return (timer_lua_t *)mrp_lua_check_object(L, TIMER_LUA_CLASS, idx);
+}
+
+
+static ssize_t timer_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ timer_lua_t *t = (timer_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{%s%stimer %p @ %d msecs}",
+ t->t ? "" : "disabled ",
+ t->oneshot ? "oneshot " : "",
+ t->t, t->msecs);
+ }
+}
+
+
+static int timer_lua_start(lua_State *L)
+{
+ timer_lua_t *t = timer_lua_check(L, -1);
+
+ if (t == NULL) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ if (t->t == NULL && t->callback != LUA_NOREF)
+ t->t = mrp_add_timer(t->ctx->ml, t->msecs, timer_lua_cb, t);
+
+ lua_pushboolean(L, t->t != NULL);
+
+ return 1;
+}
+
+
+static int timer_lua_stop(lua_State *L)
+{
+ timer_lua_t *t = timer_lua_check(L, -1);
+
+ if (t == NULL) {
+ lua_pushboolean(L, false);
+ return 1;
+ }
+
+ mrp_del_timer(t->t);
+ t->t = NULL;
+
+ lua_pushboolean(L, true);
+
+ return 1;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, TIMER_LUA_CLASS,
+ { "Timer", timer_lua_create });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/wsck-transport.h>
+
+#include <murphy/core/lua-utils/error.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+#include <murphy/core/lua-bindings/lua-json.h>
+
+
+/*
+ * Lua transport object
+ */
+
+#define TRANSPORT_LUA_CLASS MRP_LUA_CLASS(transport, lua)
+
+typedef struct {
+ lua_State *L; /* Lua execution context */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_transport_t *t; /* associated murphy transport */
+ char *address; /* transport address */
+ mrp_sockaddr_t addr; /* resolved address */
+ const char *atype; /* address type */
+ socklen_t alen; /* resolved length */
+ char *encoding; /* transport encoding mode */
+ bool closing; /* whether being closed */
+ struct {
+ int connect; /* connection event */
+ int closed; /* closed event */
+ int recv; /* connected recv event */
+ int recvfrom; /* unconnected recv event */
+ } callback; /* event callback references */
+ int data; /* referece to callback data */
+} transport_lua_t;
+
+
+/* native transport handling */
+static int transport_create(transport_lua_t *t, char *err, size_t elen);
+static int transport_listen(transport_lua_t *t, char *err, size_t elen);
+static int transport_connect(transport_lua_t *t, char *err, size_t elen);
+static transport_lua_t *transport_accept(transport_lua_t *lt);
+static void transport_disconnect(transport_lua_t *t);
+
+static void event_connect(mrp_transport_t *mt, void *user_data);
+static void event_closed(mrp_transport_t *mt, int error, void *user_data);
+static void event_recv(mrp_transport_t *mt, void *msg, void *user_data);
+static void event_recvfrom(mrp_transport_t *mt, void *msg, mrp_sockaddr_t *addr,
+ socklen_t alen, void *user_data);
+
+/* Lua transport handling */
+static int transport_lua_create(lua_State *L);
+static int transport_lua_listen(lua_State *L);
+static int transport_lua_connect(lua_State *L);
+static int transport_lua_accept(lua_State *L);
+static void transport_lua_destroy(void *data);
+static int transport_lua_disconnect(lua_State *L);
+static void transport_lua_changed(void *data, lua_State *L, int member);
+static ssize_t transport_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+
+
+/*
+ * Lua transport class
+ */
+
+#define OFFS(m) MRP_OFFSET(transport_lua_t, m)
+#define CB(m) OFFS(callback.m)
+#define RO MRP_LUA_CLASS_READONLY
+#define NOTIFY MRP_LUA_CLASS_NOTIFY
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+
+MRP_LUA_METHOD_LIST_TABLE(transport_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(transport_lua_create)
+ MRP_LUA_METHOD(listen , transport_lua_listen )
+ MRP_LUA_METHOD(connect , transport_lua_connect)
+ MRP_LUA_METHOD(accept , transport_lua_accept)
+ MRP_LUA_METHOD(disconnect, transport_lua_disconnect));
+
+MRP_LUA_METHOD_LIST_TABLE(transport_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (transport_lua_create));
+
+MRP_LUA_MEMBER_LIST_TABLE(transport_lua_members,
+ MRP_LUA_CLASS_LFUNC ("connect" , CB(connect) , NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_LFUNC ("closed" , CB(closed) , NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_LFUNC ("recv" , CB(recv) , NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_LFUNC ("recvfrom", CB(recvfrom) , NULL, NULL, NOTIFY)
+ MRP_LUA_CLASS_ANY ("data" , OFFS(data) , NULL, NULL, NOTIFY )
+ MRP_LUA_CLASS_STRING ("address" , OFFS(address) , NULL, NULL, NOTIFY|RO)
+ MRP_LUA_CLASS_STRING ("encoding", OFFS(encoding), NULL, NULL, NOTIFY|RO)
+);
+
+typedef enum {
+ TRANSPORT_MEMBER_CONNECT,
+ TRANSPORT_MEMBER_CLOSED,
+ TRANSPORT_MEMBER_RECV,
+ TRANSPORT_MEMBER_RECVFROM,
+ TRANSPORT_MEMBER_DATA,
+ TRANSPORT_MEMBER_ADDRESS,
+ TRANSPORT_MEMBER_ENCODING,
+} transport_member_t;
+
+
+MRP_LUA_DEFINE_CLASS(transport, lua, transport_lua_t, transport_lua_destroy,
+ transport_lua_methods, transport_lua_overrides,
+ transport_lua_members, NULL, transport_lua_changed,
+ transport_lua_tostring, NULL, MRP_LUA_CLASS_EXTENSIBLE);
+
+MRP_LUA_CLASS_CHECKER(transport_lua_t, transport_lua, TRANSPORT_LUA_CLASS);
+
+
+static int set_address(transport_lua_t *t, const char *address,
+ char *err, size_t elen, int overwrite)
+{
+ MRP_LUA_ERRUSE(err, elen);
+
+ if (t->address != NULL) {
+ if (t->address == address) {
+ if (t->alen > 0 && t->atype != NULL)
+ return 1;
+ }
+ else {
+ if (!overwrite)
+ return mrp_lua_error(-1, t->L,
+ "address already set ('%s')", t->address);
+ }
+ }
+
+ if (t->address != address) {
+ mrp_free(t->address);
+ t->address = NULL;
+ }
+
+ t->atype = NULL;
+ t->alen = 0;
+
+ if (address == NULL)
+ return 1;
+
+ if ((t->address = mrp_strdup(address)) == NULL)
+ return mrp_lua_error(-1, t->L,
+ "failed to store address '%s'", address);
+
+ t->alen = mrp_transport_resolve(NULL, t->address, &t->addr,
+ sizeof(t->addr), &t->atype);
+
+ if (t->alen <= 0) {
+ if (address != t->address)
+ mrp_free(t->address);
+
+ t->atype = NULL;
+ t->alen = 0;
+
+ return mrp_lua_error(-1, t->L, "failed to resolve '%s'", address);
+ }
+
+ return 0;
+}
+
+
+static int transport_create(transport_lua_t *t, char *err, size_t elen)
+{
+ MRP_LUA_ERRUSE(err, elen);
+
+ static mrp_transport_evt_t events = {
+ { .recvcustom = event_recv },
+ { .recvcustomfrom = event_recvfrom },
+ .connection = event_connect,
+ .closed = event_closed,
+ };
+ const char *opt, *val;
+ int flags;
+
+ if (t->alen <= 0) {
+ errno = EADDRNOTAVAIL;
+ return mrp_lua_error(-1, t->L, "no address specified");
+ }
+
+ if (t->t != NULL)
+ return 0;
+
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_MODE_CUSTOM;
+ t->t = mrp_transport_create(t->ctx->ml, t->atype, &events, t, flags);
+
+ if (t->t == NULL)
+ return mrp_lua_error(-1, t->L, "failed to create transport");
+
+
+ opt = MRP_WSCK_OPT_SENDMODE;
+ val = MRP_WSCK_SENDMODE_TEXT;
+ mrp_transport_setopt(t->t, opt, val);
+
+ return 0;
+}
+
+
+static int transport_listen(transport_lua_t *t, char *err, size_t elen)
+{
+ MRP_LUA_ERRUSE(err, elen);
+
+ if (t->alen <= 0) {
+ errno = EADDRNOTAVAIL;
+ return mrp_lua_error(-1, t->L, "no address specified");
+ }
+
+ if (transport_create(t, MRP_LUA_ERRPASS) < 0)
+ return -1;
+
+ if (!mrp_transport_bind(t->t, &t->addr, t->alen) ||
+ !mrp_transport_listen(t->t, 0))
+ return mrp_lua_error(-1, t->L, "failed to bind transport");
+
+ return 0;
+}
+
+
+static int transport_connect(transport_lua_t *t, char *err, size_t elen)
+{
+ MRP_LUA_ERRUSE(err, elen);
+
+ const char *opt, *val;
+
+ if (t->alen <= 0) {
+ errno = EADDRNOTAVAIL;
+ return mrp_lua_error(-1, t->L, "no address specified");
+ }
+
+ if (t->t != NULL) {
+ errno = EISCONN;
+ return mrp_lua_error(-1, t->L, "transport already active");
+ }
+
+ if (transport_create(t, MRP_LUA_ERRPASS) < 0)
+ return mrp_lua_error(-1, t->L, "failed to connect transport to %s",
+ t->address);
+
+ opt = MRP_WSCK_OPT_SENDMODE;
+ val = MRP_WSCK_SENDMODE_TEXT;
+ mrp_transport_setopt(t->t, opt, val);
+
+ if (!mrp_transport_connect(t->t, &t->addr, t->alen)) {
+ mrp_transport_destroy(t->t);
+ t->t = NULL;
+
+ return mrp_lua_error(-1, t->L, "failed to connect transport");
+ }
+
+ return 0;
+}
+
+
+static transport_lua_t *transport_accept(transport_lua_t *lt)
+{
+ transport_lua_t *t;
+
+ t = (transport_lua_t *)mrp_lua_create_object(lt->L, TRANSPORT_LUA_CLASS,
+ NULL, 0);
+
+ t->L = lt->L;
+ t->ctx = lt->ctx;
+ t->callback.connect = LUA_NOREF;
+ t->callback.closed = LUA_NOREF;
+ t->callback.recv = LUA_NOREF;
+ t->callback.recvfrom = LUA_NOREF;
+ t->data = LUA_NOREF;
+
+ t->t = mrp_transport_accept(lt->t, t, MRP_TRANSPORT_REUSEADDR);
+
+ if (t->t != NULL) {
+ t->callback.recv = mrp_lua_object_getref(lt, t, t->L,lt->callback.recv);
+ t->data = mrp_lua_object_getref(lt, t, t->L,lt->data);
+
+ return t;
+ }
+
+ /* XXX TODO
+ * Hmm, is it enough to just wait for the next gc cycle, or
+ * should we actively do something to destroy the object ?
+ */
+
+ return NULL;
+}
+
+
+static void transport_disconnect(transport_lua_t *t)
+{
+ mrp_transport_disconnect(t->t);
+ mrp_transport_destroy(t->t);
+ t->t = NULL;
+}
+
+
+
+
+static void transport_lua_changed(void *data, lua_State *L, int member)
+{
+ MRP_LUA_ERRBUF();
+
+ transport_lua_t *t = (transport_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("member <transport <%s> %p(%p)>.%s changed",
+ t->address ? t->address : "no address", t, t->t,
+ transport_lua_members[member].name);
+
+ switch (member) {
+ case TRANSPORT_MEMBER_CONNECT:
+ case TRANSPORT_MEMBER_CLOSED:
+ case TRANSPORT_MEMBER_RECV:
+ case TRANSPORT_MEMBER_RECVFROM:
+ case TRANSPORT_MEMBER_DATA:
+ break;
+
+ case TRANSPORT_MEMBER_ADDRESS:
+ if (set_address(t, t->address, MRP_LUA_ERRPASS, t->t == NULL) < 0)
+ mrp_lua_error(-1, L, "%s", MRP_LUA_ERR);
+ return;
+
+ case TRANSPORT_MEMBER_ENCODING:
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static int transport_lua_create(lua_State *L)
+{
+ MRP_LUA_ERRBUF();
+
+ mrp_context_t *ctx = mrp_lua_get_murphy_context();
+ int narg = lua_gettop(L);
+ transport_lua_t *t;
+
+ if (ctx == NULL)
+ return mrp_lua_error(-1, L, "failed to get murphy context");
+
+ t = (transport_lua_t *)mrp_lua_create_object(L, TRANSPORT_LUA_CLASS,
+ NULL, 0);
+ t->L = L;
+ t->ctx = ctx;
+
+ t->callback.connect = LUA_NOREF;
+ t->callback.closed = LUA_NOREF;
+ t->callback.recv = LUA_NOREF;
+ t->callback.recvfrom = LUA_NOREF;
+ t->data = LUA_NOREF;
+
+ switch (narg) {
+ case 1:
+ break;
+ case 2:
+ if (mrp_lua_init_members(t, L, -2, MRP_LUA_ERRPASS) != 1)
+ return mrp_lua_error(-1, L, "failed to initialize transport (%s)",
+ MRP_LUA_ERR);
+ break;
+ default:
+ return mrp_lua_error(-1, L, "expected 0 or 1 arguments, got %d", narg);
+ }
+
+ mrp_lua_push_object(L, t);
+
+ return 1;
+}
+
+
+static int transport_lua_listen(lua_State *L)
+{
+ MRP_LUA_ERRUSE(NULL, 0);
+
+ transport_lua_t *t = transport_lua_check(L, 1);
+ int narg;
+
+ if ((narg = lua_gettop(L)) != 1)
+ return mrp_lua_error(-1, L, "listen takes no arguments, got %d",
+ narg - 1);
+
+ return transport_listen(t, MRP_LUA_ERRPASS);
+}
+
+
+static int transport_lua_connect(lua_State *L)
+{
+ MRP_LUA_ERRBUF();
+
+ transport_lua_t *t = transport_lua_check(L, 1);
+ int narg = lua_gettop(L);
+
+ if (t->alen <= 0 || t->atype == NULL)
+ return mrp_lua_error(-1, L, "can't connect, no address set");
+
+ if (narg != 1)
+ return mrp_lua_error(-1, L, "connect takes no arguments, %d given",
+ narg - 1);
+
+ if (transport_connect(t, MRP_LUA_ERRPASS) < 0)
+ return mrp_lua_error(-1, L, "connection failed");
+
+ return 0;
+}
+
+
+static int transport_lua_accept(lua_State *L)
+{
+ MRP_LUA_ERRBUF();
+
+ transport_lua_t *lt = transport_lua_check(L, 1);
+ int narg = lua_gettop(L);
+ transport_lua_t *t;
+
+ if (narg != 1)
+ return mrp_lua_error(-1, L, "disconnect takes no arguments, got %d",
+ narg - 1);
+
+ t = transport_accept(lt);
+
+ if (t != NULL) {
+ mrp_lua_push_object(L, t);
+ return 1;
+ }
+
+ /* XXX TODO
+ * Hmm, is it enough to just wait for the next gc cycle, or
+ * should we actively do something to destroy the object ?
+ */
+
+ return mrp_lua_error(-1, L, "failed to accept connection");
+}
+
+
+static int transport_lua_disconnect(lua_State *L)
+{
+ MRP_LUA_ERRBUF();
+
+ transport_lua_t *t = transport_lua_check(L, 1);
+ int narg = lua_gettop(L);
+
+ if (narg != 1)
+ return mrp_lua_error(-1, L, "disconnect takes no arguments, got %d",
+ narg - 1);
+
+ transport_disconnect(t);
+
+ return 0;
+}
+
+
+static void transport_lua_destroy(void *data)
+{
+ transport_lua_t *t = (transport_lua_t *)data;
+
+ mrp_transport_disconnect(t->t);
+ t->t = NULL;
+ mrp_free(t->address);
+ t->address = NULL;
+
+ mrp_lua_object_unref_value(t, t->L, t->callback.connect);
+ mrp_lua_object_unref_value(t, t->L, t->callback.closed);
+ mrp_lua_object_unref_value(t, t->L, t->callback.recv);
+ mrp_lua_object_unref_value(t, t->L, t->callback.recvfrom);
+ mrp_lua_object_unref_value(t, t->L, t->data);
+ t->callback.connect = LUA_NOREF;
+ t->callback.closed = LUA_NOREF;
+ t->callback.recv = LUA_NOREF;
+ t->callback.recvfrom = LUA_NOREF;
+ t->data = LUA_NOREF;
+}
+
+
+static ssize_t transport_lua_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ transport_lua_t *t = (transport_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{%stransport <%s> %p}",
+ t->t && t->t->connected ? "connected" : "",
+ t->address ? t->address : "no address", t->t);
+ }
+}
+
+
+static void event_connect(mrp_transport_t *mt, void *user_data)
+{
+ transport_lua_t *t = (transport_lua_t *)user_data;
+ int top;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("incoming connection on <transport <%s> %p(%p)>",
+ t->address ? t->address : "no address", t, t->t);
+
+ top = lua_gettop(t->L);
+
+ if (mrp_lua_object_deref_value(t, t->L, t->callback.connect, false)) {
+ mrp_lua_push_object(t->L, t);
+ lua_pushliteral(t->L, "<remote address should be here>");
+ mrp_lua_object_deref_value(t, t->L, t->data, true);
+
+ if (lua_pcall(t->L, 3, 0, 0) != 0)
+ mrp_log_error("failed to invoke transport connect callback");
+ }
+
+ lua_settop(t->L, top);
+}
+
+
+static void event_closed(mrp_transport_t *mt, int error, void *user_data)
+{
+ transport_lua_t *t = (transport_lua_t *)user_data;
+ int top;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("<transport <%s> %p(%p)> has been closed",
+ t->address ? t->address : "no address", t, t->t);
+
+ top = lua_gettop(t->L);
+
+ if (mrp_lua_object_deref_value(t, t->L, t->callback.closed, false)) {
+ mrp_lua_push_object(t->L, t);
+ lua_pushinteger(t->L, error);
+ mrp_lua_object_deref_value(t, t->L, t->data, true);
+
+ if (lua_pcall(t->L, 3, 0, 0) != 0)
+ mrp_log_error("failed to invoke transport closed callback");
+
+ mrp_transport_destroy(t->t);
+ t->t = NULL;
+ }
+
+ lua_settop(t->L, top);
+}
+
+
+static void event_recv(mrp_transport_t *mt, void *msg, void *user_data)
+{
+ transport_lua_t *t = (transport_lua_t *)user_data;
+ int top;
+
+ MRP_UNUSED(mt);
+
+ mrp_debug("received message on <transport <%s> %p(%p)>",
+ t->address ? t->address : "no address", t, t->t);
+
+ top = lua_gettop(t->L);
+
+ if (mrp_lua_object_deref_value(t, t->L, t->callback.recv, false)) {
+ mrp_lua_push_object(t->L, t);
+ mrp_json_lua_push(t->L, msg);
+ mrp_lua_object_deref_value(t, t->L, t->data, true);
+
+ if (lua_pcall(t->L, 3, 0, 0) != 0)
+ mrp_log_error("failed to invoke transport recv callback");
+ }
+
+ lua_settop(t->L, top);
+}
+
+
+static void event_recvfrom(mrp_transport_t *mt, void *msg, mrp_sockaddr_t *addr,
+ socklen_t alen, void *user_data)
+{
+ transport_lua_t *t = (transport_lua_t *)user_data;
+ int top;
+
+ MRP_UNUSED(mt);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(alen);
+
+ mrp_debug("received message on <transport <%s> %p(%p)>",
+ t->address ? t->address : "no address", t, t->t);
+
+ top = lua_gettop(t->L);
+
+ if (mrp_lua_object_deref_value(t, t->L, t->callback.recvfrom, false)) {
+ mrp_lua_push_object(t->L, t);
+ mrp_json_lua_push(t->L, msg);
+ lua_pushliteral(t->L, "<remote address should be here>");
+ mrp_lua_object_deref_value(t, t->L, t->data, true);
+
+ if (lua_pcall(t->L, 4, 0, 0) != 0)
+ mrp_log_error("failed to invoke transport recvfrom callback");
+ }
+
+ lua_settop(t->L, top);
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, TRANSPORT_LUA_CLASS,
+ { "Transport", transport_lua_create });
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_BINDINGS_H__
+#define __MURPHY_LUA_BINDINGS_H__
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/list.h>
+#include <murphy/core/context.h>
+#include <murphy/core/lua-utils/object.h>
+
+
+typedef struct {
+ const char *meta; /* add method to this metatable */
+ luaL_reg *methods; /* Lua method table to register */
+ mrp_lua_classdef_t *classdef; /* class definition or NULL */
+ mrp_list_hook_t hook; /* to list of registered bindings */
+} mrp_lua_bindings_t;
+
+
+typedef struct {
+ mrp_context_t **ctxp; /* murphy context */
+} mrp_lua_murphy_t;
+
+
+/** Macro to automatically register murphy Lua bindings on startup. */
+#define MURPHY_REGISTER_LUA_BINDINGS(_metatbl, _classdef, ...) \
+ static void register_##_metatbl##_bindings(void) MRP_INIT; \
+ \
+ static void register_##_metatbl##_bindings(void) { \
+ static struct luaL_reg methods[] = { \
+ __VA_ARGS__, \
+ { NULL, NULL } \
+ }; \
+ static mrp_lua_bindings_t b = { \
+ .meta = #_metatbl, \
+ .methods = methods, \
+ .classdef = _classdef, \
+ }; \
+ \
+ mrp_list_init(&b.hook); \
+ mrp_lua_register_murphy_bindings(&b); \
+ }
+
+
+/** Set murphy context for the bindings. */
+lua_State *mrp_lua_set_murphy_context(mrp_context_t *ctx);
+
+/** Set the path to the main Lua configuration file. */
+void mrp_lua_set_murphy_lua_config_file(const char *path);
+
+/** Get murphy context for the bindings. */
+mrp_context_t *mrp_lua_get_murphy_context(void);
+
+/** Get the common Lua state for the bindings. */
+lua_State *mrp_lua_get_lua_state(void);
+
+/** Get the main Lua configuration directory. */
+const char *mrp_lua_get_murphy_lua_config_dir(void);
+
+/** Register the given lua murphy bindings. */
+int mrp_lua_register_murphy_bindings(mrp_lua_bindings_t *b);
+
+/** Check and get murphy context for the bindings. */
+mrp_context_t *mrp_lua_check_murphy_context(lua_State *L, int index);
+
+/** Produce a debugging dump of the Lua stack (using mrp_debug). */
+void mrp_lua_dump_stack(lua_State *L, const char *prefix);
+
+/*
+ * level of debugging detail
+ */
+
+typedef enum {
+ MRP_LUA_DEBUG_DISABLED = 0, /* debugging disabled */
+ MRP_LUA_DEBUG_ENABLED, /* debugging enabled */
+ MRP_LUA_DEBUG_DETAILED, /* detailed debugging enabled */
+} mrp_lua_debug_t;
+
+/** Configure murphy lua debugging. */
+int mrp_lua_set_debug(mrp_lua_debug_t level);
+
+#endif /* __MURPHY_LUA_BINDINGS_H__ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C ../.. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C ../.. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alloca.h>
+
+#include <murphy/common.h>
+#include <murphy/common/debug.h>
+
+#include <murphy/core/context.h>
+#include <murphy/core/scripting.h>
+#include <murphy/core/lua-decision/element.h>
+#include <murphy/core/lua-decision/mdb.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/strarray.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+#define ELEMENT_CLASS MRP_LUA_CLASS(element, lua)
+#define SINK_CLASS MRP_LUA_CLASS(sink, lua)
+
+#define ELEMENT_INPUT_CLASSID MRP_LUA_CLASSID_ROOT "element_input"
+#define ELEMENT_OUTPUT_CLASSID MRP_LUA_CLASSID_ROOT "element_output"
+
+#define ELEMENT_IDX 1
+#define INPUT_IDX 1
+#define OUTPUT_IDX 2
+
+#define INPUT_MAX (sizeof(mrp_lua_element_mask_t) * 8)
+#define INPUT_BIT(_i) (((mrp_lua_element_mask_t)1) << (_i))
+#define INPUT_MASK(_n) (INPUT_BIT(_n) - 1)
+
+typedef enum field_e field_t;
+typedef enum input_type_e input_type_t;
+
+
+enum field_e {
+ NAME = 1,
+ INPUTS,
+ OUTPUTS,
+ UPDATE,
+ OBJECT,
+ INTERFACE,
+ PROPERTY,
+ TYPE,
+ INITIATE,
+};
+
+enum input_type_e {
+ NUMBER = MRP_FUNCBRIDGE_FLOATING,
+ STRING = MRP_FUNCBRIDGE_STRING,
+ SELECT = MRP_FUNCBRIDGE_OBJECT,
+};
+
+struct mrp_lua_element_input_s {
+ const char *name;
+ input_type_t type;
+ union {
+ mrp_funcbridge_value_t constant;
+ mrp_lua_mdb_select_t *select;
+ };
+};
+
+
+struct mrp_lua_element_s {
+ MRP_LUA_ELEMENT_FIELDS;
+};
+
+struct mrp_lua_sink_s {
+ MRP_LUA_ELEMENT_FIELDS;
+ const char *object;
+ const char *interface;
+ const char *property;
+ const char *type;
+ mrp_funcbridge_t *initiate;
+};
+
+
+static int element_create_from_lua(lua_State *);
+static int element_getfield(lua_State *);
+static int element_setfield(lua_State *);
+static int element_tostring(lua_State *);
+static void element_destroy_from_lua(void *);
+static mrp_lua_element_t *element_check(lua_State *, int);
+static void element_install(lua_State *, void *);
+
+static int sink_create_from_lua(lua_State *);
+static int sink_getfield(lua_State *);
+static int sink_setfield(lua_State *);
+static int sink_tostring(lua_State *);
+static void sink_destroy_from_lua(void *);
+static mrp_lua_sink_t *sink_check(lua_State *, int);
+static void sink_install(lua_State *, void *);
+
+static void element_input_class_create(lua_State *);
+static int element_input_create_luatbl(lua_State *, int);
+static int element_input_getfield(lua_State *);
+static int element_input_setfield(lua_State *);
+static mrp_lua_element_input_t *element_input_create_userdata(lua_State *,
+ int, size_t *,
+ mrp_lua_element_mask_t *);
+
+static mrp_lua_mdb_table_t **element_output_check(lua_State *, int, size_t *);
+
+
+static field_t field_check(lua_State *, int, const char **);
+static field_t field_name_to_type(const char *, size_t);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ element_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (element_create_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ sink_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (sink_create_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ element_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (element_create_from_lua)
+ MRP_LUA_OVERRIDE_GETFIELD (element_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (element_setfield)
+ MRP_LUA_OVERRIDE_STRINGIFY (element_tostring)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ sink_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (sink_create_from_lua)
+ MRP_LUA_OVERRIDE_GETFIELD (sink_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (sink_setfield)
+ MRP_LUA_OVERRIDE_STRINGIFY (sink_tostring)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ element_input_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (element_input_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (element_input_setfield)
+);
+
+
+MRP_LUA_CLASS_DEF (
+ element, /* class name */
+ lua, /* constructor name */
+ mrp_lua_element_t, /* userdata type */
+ element_destroy_from_lua, /* userdata destructor */
+ element_methods, /* class methods */
+ element_overrides /* override methods */
+);
+
+MRP_LUA_CLASS_DEF (
+ sink, /* class name */
+ lua, /* constructor name */
+ mrp_lua_sink_t, /* userdata type */
+ sink_destroy_from_lua, /* userdata destructor */
+ sink_methods, /* class methods */
+ sink_overrides /* override methods */
+);
+
+
+void mrp_lua_create_element_class(lua_State *L)
+{
+ mrp_lua_create_object_class(L, ELEMENT_CLASS);
+ mrp_lua_create_object_class(L, SINK_CLASS);
+
+ element_input_class_create(L);
+}
+
+const char *mrp_lua_get_element_name(mrp_lua_element_t *el)
+{
+ return el ? el->name : "";
+}
+
+int mrp_lua_element_get_input_count(mrp_lua_element_t *el)
+{
+ return el ? (int)el->ninput : -1;
+}
+
+const char *mrp_lua_element_get_input_name(mrp_lua_element_t *el, int inpidx)
+{
+ mrp_lua_element_input_t *inp;
+
+ if (!el || inpidx < 0 || inpidx >= (int)el->ninput || !(inp = el->inputs))
+ return NULL;
+
+ return inp->name;
+}
+
+int mrp_lua_element_get_input_index(mrp_lua_element_t *el, const char *inpnam)
+{
+ mrp_lua_element_input_t *inp;
+ size_t inpidx;
+
+ if (el && inpnam && (inp = el->inputs)) {
+ for (inpidx = 0; inpidx < el->ninput; inpidx++) {
+ if (!strcmp(inpnam, el->inputs[inpidx].name))
+ return inpidx;
+ }
+ }
+
+ return -1;
+}
+
+
+int mrp_lua_element_get_column_index(mrp_lua_element_t *el,
+ int inpidx,
+ const char *colnam)
+{
+ mrp_lua_element_input_t *inp;
+
+ if (el && inpidx >= 0 && inpidx < (int)el->ninput &&
+ (inp = el->inputs + inpidx) && colnam)
+ {
+ if (inp->type != SELECT)
+ return (!colnam || strcmp(colnam, "single_value")) ? -1 : 0;
+ else
+ return mrp_lua_select_get_column_index(inp->select, colnam);
+ }
+
+ return -1;
+}
+
+int mrp_lua_element_get_column_count(mrp_lua_element_t *el, int inpidx)
+{
+ mrp_lua_element_input_t *inp;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type != SELECT)
+ return 1;
+ else
+ return mrp_lua_select_get_column_count(inp->select);
+ }
+
+ return -1;
+}
+
+mqi_data_type_t mrp_lua_element_get_column_type(mrp_lua_element_t *el,
+ int inpidx,
+ int colidx)
+{
+ mrp_lua_element_input_t *inp;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type == SELECT)
+ return mrp_lua_select_get_column_type(inp->select, colidx);
+ else {
+ switch (inp->type) {
+ case NUMBER: return mqi_floating;
+ case STRING: return mqi_string;
+ default: return -1;
+ }
+ }
+ }
+
+ return -1;
+}
+
+int mrp_lua_element_get_row_count(mrp_lua_element_t *el, int inpidx)
+{
+ mrp_lua_element_input_t *inp;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type != SELECT)
+ return 1;
+ else
+ return mrp_lua_select_get_row_count(inp->select);
+ }
+
+ return -1;
+}
+
+const char *mrp_lua_element_get_string(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx,
+ char *buf, int len)
+{
+ mrp_lua_element_input_t *inp;
+ const char *s = NULL;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type == SELECT)
+ s = mrp_lua_select_get_string(inp->select, colidx,rowidx, buf,len);
+ else {
+ if (!buf || len < 1)
+ s = (inp->type == STRING) ? inp->constant.string : "";
+ else {
+ s = buf;
+ switch (inp->type) {
+ case NUMBER:
+ snprintf(buf, len, "%lf", inp->constant.floating);
+ break;
+ case STRING:
+ snprintf(buf, len, "%s", inp->constant.string);
+ break;
+ default:
+ *buf = '\0';
+ break;
+ }
+ }
+ }
+ }
+
+ return s;
+}
+
+int32_t mrp_lua_element_get_integer(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx)
+{
+ mrp_lua_element_input_t *inp;
+ int32_t i = 0;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type == SELECT)
+ i = mrp_lua_select_get_integer(inp->select, colidx,rowidx);
+ else {
+ switch (inp->type) {
+ case NUMBER: i = inp->constant.floating; break;
+ case STRING: i = strtol(inp->constant.string, NULL, 10); break;
+ default: i = 0; break;
+ }
+ }
+ }
+
+ return i;
+}
+
+uint32_t mrp_lua_element_get_unsigned(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx)
+{
+ mrp_lua_element_input_t *inp;
+ uint32_t u = 0;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type == SELECT)
+ u = mrp_lua_select_get_unsigned(inp->select, colidx,rowidx);
+ else {
+ switch (inp->type) {
+ case NUMBER: u = inp->constant.floating; break;
+ case STRING: u = strtoul(inp->constant.string, NULL, 10); break;
+ default: u = 0; break;
+ }
+ }
+ }
+
+ return u;
+}
+
+double mrp_lua_element_get_floating(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx)
+{
+ mrp_lua_element_input_t *inp;
+ double f = 0;
+
+ if (el && inpidx >= 0 && inpidx <= (int)el->ninput &&
+ (inp = el->inputs + inpidx))
+ {
+ if (inp->type == SELECT)
+ f = mrp_lua_select_get_floating(inp->select, colidx,rowidx);
+ else {
+ switch (inp->type) {
+ case NUMBER: f = inp->constant.floating; break;
+ case STRING: f = strtod(inp->constant.string, NULL); break;
+ default: f = 0; break;
+ }
+ }
+ }
+
+ return f;
+}
+
+
+static int element_create_from_lua(lua_State *L)
+{
+ mrp_lua_element_t *el;
+ int table;
+ size_t fldnamlen;
+ const char *fldnam;
+
+ MRP_LUA_ENTER;
+
+ el = (mrp_lua_element_t *)mrp_lua_create_object(L, ELEMENT_CLASS, NULL,0);
+ el->install = element_install;
+
+ table = lua_gettop(L);
+
+ lua_pushinteger(L, INPUT_IDX);
+ element_input_create_luatbl(L, table);
+ lua_rawset(L, table);
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ el->name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case INPUTS:
+ el->inputs = element_input_create_userdata(L, -1, &el->ninput,
+ &el->inpmask);
+ break;
+
+ case OUTPUTS:
+ el->outputs = element_output_check(L, -1, &el->noutput);
+ break;
+
+ case UPDATE:
+ el->update = mrp_funcbridge_create_luafunc(L, -1);
+ break;
+
+ default:
+ lua_pushvalue(L, -2);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, table);
+ break;
+ }
+
+ } /* MRP_LUA_FOREACH_FIELD */
+
+ if (!el->name)
+ luaL_error(L, "missing mandatory 'name' field");
+ if (!el->inputs || !el->ninput)
+ luaL_error(L, "missing or empty manadatory 'input' field");
+ if (!el->outputs || !el->noutput)
+ luaL_error(L, "missing or empty manadatory 'output' field");
+ if (!el->update)
+ luaL_error(L, "missing or invalid mandatory 'update' field");
+
+ mrp_lua_set_object_name(L, ELEMENT_CLASS, el->name);
+
+ mrp_debug("element '%s' created", el->name);
+
+ if (el->inpmask == INPUT_MASK(el->ninput))
+ element_install(L, el);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int element_getfield(lua_State *L)
+{
+ mrp_lua_element_t *el;
+ field_t fld;
+
+ MRP_LUA_ENTER;
+
+ el = element_check(L, 1);
+ fld = field_check(L, 2, NULL);
+ lua_pop(L, 1);
+
+ switch (fld) {
+ case NAME: lua_pushstring(L, el->name); break;
+ case INPUTS: lua_rawgeti(L, 1, INPUT_IDX); break;
+ case OUTPUTS: lua_pushnil(L); break;
+ case UPDATE: mrp_funcbridge_push(L, el->update); break;
+ default: lua_pushnil(L); break;
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int element_setfield(lua_State *L)
+{
+ mrp_lua_element_t *el;
+
+ MRP_LUA_ENTER;
+
+ el = element_check(L, 1);
+ luaL_error(L, "'%s' is read-only", el->name);
+
+ MRP_LUA_LEAVE(0);
+}
+
+static int element_tostring(lua_State *L)
+{
+ mrp_lua_element_t *el;
+
+ MRP_LUA_ENTER;
+
+ if ((el = element_check(L, 1)) && el->name)
+ lua_pushstring(L, el->name);
+ else
+ lua_pushstring(L, "<error>");
+
+ MRP_LUA_LEAVE(1);
+}
+
+static void element_destroy_from_lua(void *data)
+{
+ mrp_lua_element_t *el = (mrp_lua_element_t *)data;
+
+ MRP_LUA_ENTER;
+
+ if (el) {
+ mrp_free((void *)el->name);
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static mrp_lua_element_t *element_check(lua_State *L, int idx)
+{
+ return (mrp_lua_element_t *)mrp_lua_check_object(L, ELEMENT_CLASS, idx);
+}
+
+static int element_update_cb(mrp_scriptlet_t *script, mrp_context_tbl_t *ctbl)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_lua_element_t *el = (mrp_lua_element_t *)script->data;
+ mrp_funcbridge_value_t args[1] = { { .pointer = el } };
+ mrp_funcbridge_value_t ret;
+ char t;
+
+ MRP_UNUSED(ctbl);
+
+ mrp_debug("'%s'", el->name);
+
+ if (el->update) {
+ memset(&ret, 0, sizeof(ret));
+ if (!mrp_funcbridge_call_from_c(L, el->update, "o", args, &t, &ret)) {
+ mrp_log_error("failed to call element.lua.%s:update method (%s)",
+ el->name, ret.string ? ret.string : "NULL");
+ mrp_free((void *)ret.string);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static void element_install(lua_State *L, void *void_el)
+{
+ static mrp_interpreter_t element_updater = {
+ { NULL, NULL },
+ "element_updater",
+ NULL,
+ NULL,
+ NULL,
+ element_update_cb,
+ NULL
+ };
+
+ mrp_lua_element_t *el = (mrp_lua_element_t *)void_el;
+ mrp_lua_element_input_t *inp;
+ mrp_context_t *ctx;
+ size_t i;
+ char buf[1024], target[1024];
+ const char **depends, *d;
+ char *dep;
+ int ndepend;
+ char *p, *e;
+ size_t len;
+
+ MRP_UNUSED(L);
+
+ MRP_LUA_ENTER;
+
+ ctx = mrp_lua_get_murphy_context();
+
+ if (ctx == NULL || ctx->r == NULL) {
+ mrp_log_error("Invalid or incomplete murphy context");
+ return;
+ }
+
+ depends = alloca(el->ninput * sizeof(depends[0]));
+ ndepend = 0;
+
+ for (i = 0, e = (p = buf) + sizeof(buf); i < el->ninput && p < e; i++) {
+ inp = el->inputs + i;
+
+ if (inp->type == SELECT) {
+ d = mrp_lua_select_name(inp->select);
+ p += snprintf(p, e-p, " _select_%s", d);
+
+ len = strlen(d) + 7 + 1;
+ depends[ndepend++] = dep = alloca(len);
+ sprintf(dep, "_select_%s", d);
+ }
+ }
+
+ for (i = 0; i < el->noutput; i++) {
+ snprintf(target, sizeof(target), "_table_%s",
+ mrp_lua_table_name(el->outputs[i]));
+
+ printf("\%s:%s\n\tupdate(%s)\n\n", target, buf, el->name);
+
+
+ if (!mrp_resolver_add_prepared_target(ctx->r, target, depends, ndepend,
+ &element_updater, NULL, el)) {
+ mrp_log_error("Failed to install resolver target for element '%s'.",
+ el->name);
+ MRP_LUA_LEAVE_ERROR(L,
+ "Failed to install resolver target for "
+ "element '%s'.", el->name);
+ }
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+
+static int sink_create_from_lua(lua_State *L)
+{
+ mrp_lua_sink_t *sink;
+ int table;
+ size_t fldnamlen;
+ const char *fldnam;
+
+ MRP_LUA_ENTER;
+
+ sink = (mrp_lua_sink_t *)mrp_lua_create_object(L, SINK_CLASS, NULL,0);
+ sink->install = sink_install;
+
+ table = lua_gettop(L);
+
+ lua_pushinteger(L, INPUT_IDX);
+ element_input_create_luatbl(L, table);
+ lua_rawset(L, table);
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ sink->name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case INPUTS:
+ sink->inputs = element_input_create_userdata(L, -1, &sink->ninput,
+ &sink->inpmask);
+ break;
+
+ case OUTPUTS:
+ luaL_error(L, "sinks can't have outputs");
+ break;
+
+ case OBJECT:
+ sink->object = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case INTERFACE:
+ sink->interface = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case PROPERTY:
+ sink->property = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case TYPE:
+ sink->type = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case INITIATE:
+ sink->initiate = mrp_funcbridge_create_luafunc(L, -1);
+ break;
+
+ case UPDATE:
+ sink->update = mrp_funcbridge_create_luafunc(L, -1);
+ break;
+
+ default:
+ lua_pushvalue(L, -2);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, table);
+ break;
+ }
+
+ } /* MRP_LUA_FOREACH_FIELD */
+
+ if (!sink->name)
+ luaL_error(L, "missing mandatory 'name' field");
+ if (!sink->inputs || !sink->ninput)
+ luaL_error(L, "missing or empty manadatory 'input' field");
+ if (!sink->update)
+ luaL_error(L, "missing or invalid mandatory 'update' field");
+
+ mrp_lua_set_object_name(L, SINK_CLASS, sink->name);
+
+ mrp_debug("sink '%s' created", sink->name);
+
+ if (sink->inpmask == INPUT_MASK(sink->ninput))
+ sink_install(L, sink);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int sink_getfield(lua_State *L)
+{
+ mrp_lua_sink_t *sink;
+ field_t fld;
+
+ MRP_LUA_ENTER;
+
+ sink = sink_check(L, 1);
+ fld = field_check(L, 2, NULL);
+ lua_pop(L, 1);
+
+ switch (fld) {
+ case NAME: lua_pushstring(L, sink->name); break;
+ case INPUTS: lua_rawgeti(L, 1, INPUT_IDX); break;
+ case OBJECT: lua_pushstring(L, sink->object); break;
+ case INTERFACE: lua_pushstring(L, sink->interface); break;
+ case PROPERTY: lua_pushstring(L, sink->property); break;
+ case TYPE: lua_pushstring(L, sink->type); break;
+ case UPDATE: mrp_funcbridge_push(L, sink->update); break;
+ default: lua_pushnil(L); break;
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int sink_setfield(lua_State *L)
+{
+ mrp_lua_sink_t *sink;
+
+ MRP_LUA_ENTER;
+
+ sink = sink_check(L, 1);
+ luaL_error(L, "'%s' is read-only", sink->name);
+
+ MRP_LUA_LEAVE(0);
+}
+
+static int sink_tostring(lua_State *L)
+{
+ mrp_lua_sink_t *sink;
+
+ MRP_LUA_ENTER;
+
+ if ((sink = sink_check(L, 1)) && sink->name)
+ lua_pushstring(L, sink->name);
+ else
+ lua_pushstring(L, "<error>");
+
+ MRP_LUA_LEAVE(1);
+}
+
+static void sink_destroy_from_lua(void *data)
+{
+ mrp_lua_sink_t *sink = (mrp_lua_sink_t *)data;
+
+ MRP_LUA_ENTER;
+
+ if (sink) {
+ mrp_free((void *)sink->name);
+ mrp_free((void *)sink->object);
+ mrp_free((void *)sink->interface);
+ mrp_free((void *)sink->property);
+ mrp_free((void *)sink->type);
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static mrp_lua_sink_t *sink_check(lua_State *L, int idx)
+{
+ return (mrp_lua_sink_t *)mrp_lua_check_object(L, SINK_CLASS, idx);
+}
+
+static int sink_update_cb(mrp_scriptlet_t *script, mrp_context_tbl_t *ctbl)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_lua_sink_t *sink = (mrp_lua_sink_t *)script->data;
+ mrp_funcbridge_value_t args[1] = { { .pointer = sink } };
+ mrp_funcbridge_value_t ret;
+ char t;
+
+ MRP_UNUSED(ctbl);
+
+ mrp_debug("'%s'", sink->name);
+
+ if (sink->update) {
+ if (!mrp_funcbridge_call_from_c(L, sink->update, "o",args, &t,&ret)) {
+ mrp_log_error("failed to call sink.lua.%s:update method (%s)",
+ sink->name, ret.string);
+ mrp_free((void *)ret.string);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void sink_install(lua_State *L, void *void_sink)
+{
+ static mrp_interpreter_t sink_updater = {
+ { NULL, NULL },
+ "sink_updater",
+ NULL,
+ NULL,
+ NULL,
+ sink_update_cb,
+ NULL
+ };
+
+ mrp_lua_sink_t *sink = (mrp_lua_sink_t *)void_sink;
+ mrp_context_t *ctx;
+ mrp_funcbridge_value_t args[1] = { { .pointer = sink } };
+ mrp_funcbridge_value_t ret;
+ char t;
+ mrp_lua_element_input_t *inp;
+ size_t i;
+ char buf[1024], target[1024];
+ const char **depends, *d;
+ char *dep;
+ int ndepend;
+ char *p, *e;
+ size_t len;
+
+ MRP_LUA_ENTER;
+
+ ctx = mrp_lua_get_murphy_context();
+
+ if (ctx == NULL || ctx->r == NULL) {
+ mrp_log_error("Invalid or incomplete murphy context");
+ return;
+ }
+
+ if (sink->initiate) {
+ if (!mrp_funcbridge_call_from_c(L, sink->initiate, "o",args, &t,&ret)){
+ mrp_log_error("failed to call sink.lua.%s:initiate method (%s)",
+ sink->name, ret.string);
+ mrp_free((void *)ret.string);
+ return;
+ }
+ if (t != MRP_FUNCBRIDGE_BOOLEAN) {
+ mrp_log_error("sink.lua.%s:initiate returned '%c' type instead of "
+ "'b' (boolean)", sink->name, t);
+ return;
+ }
+ if (!ret.boolean) {
+ mrp_log_error("sink.lua.%s:initiate failed", sink->name);
+ return;
+ }
+ }
+
+ depends = alloca(sink->ninput * sizeof(depends[0]));
+ ndepend = 0;
+
+ for (i = 0, e = (p = buf) + sizeof(buf); i < sink->ninput && p < e; i++){
+ inp = sink->inputs + i;
+
+ if (inp->type == SELECT) {
+ d = mrp_lua_select_name(inp->select);
+ p += snprintf(p, e-p, " _select_%s", d);
+
+ len = strlen(d) + 7 + 1;
+ depends[ndepend++] = dep = alloca(len);
+ sprintf(dep, "_select_%s", d);
+ }
+ }
+
+ snprintf(target, sizeof(target), "_sink_%s", sink->name);
+
+ printf("\%s:%s\n\tupdate(%s)\n\n", target, buf, sink->name);
+
+
+ if (!mrp_resolver_add_prepared_target(ctx->r, target, depends, ndepend,
+ &sink_updater, NULL, sink))
+ {
+ mrp_log_error("Failed to install resolver target for element '%s'.",
+ sink->name);
+
+ MRP_LUA_LEAVE_ERROR(L, "Failed to install resolver target for "
+ "element '%s'.", sink->name);
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static void element_input_class_create(lua_State *L)
+{
+ /* create a metatable for input's */
+ luaL_newmetatable(L, ELEMENT_INPUT_CLASSID);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, element_input_overrides, 0);
+}
+
+static int element_input_create_luatbl(lua_State *L, int el)
+{
+ MRP_LUA_ENTER;
+
+ el = (el < 0) ? lua_gettop(L) + el + 1 : el;
+
+ luaL_checktype(L, el, LUA_TTABLE);
+
+ lua_createtable(L, 2, 0);
+
+ luaL_getmetatable(L, ELEMENT_INPUT_CLASSID);
+ lua_setmetatable(L, -2);
+
+ lua_pushinteger(L, ELEMENT_IDX);
+ lua_pushvalue(L, el);
+ lua_rawset(L, -3);
+
+ MRP_LUA_LEAVE(0);
+}
+
+static int element_input_getfield(lua_State *L)
+{
+ mrp_lua_element_t *el;
+ const char *inpnam;
+ mrp_lua_element_input_t *inp;
+ size_t i;
+
+ MRP_LUA_ENTER;
+
+ lua_rawgeti(L, 1, INPUT_IDX);
+ el = element_check(L, -1);
+ lua_pop(L, 1);
+
+ inpnam = luaL_checklstring(L, 2, NULL);
+
+ mrp_debug("reading %s.inputs.%s", el->name, inpnam);
+
+ for (i = 0; i < el->ninput; i++) {
+ inp = el->inputs + i;
+ if (!strcmp(inpnam, inp->name)) {
+ switch (inp->type) {
+ case NUMBER: lua_pushnumber(L, inp->constant.floating); break;
+ case STRING: lua_pushstring(L, inp->constant.string); break;
+ case SELECT: mrp_lua_push_select(L, inp->select, false); break;
+ default: lua_pushnil(L); break;
+ }
+ return 1;
+ }
+ }
+
+ lua_pushnil(L);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int element_input_setfield(lua_State *L)
+{
+ mrp_lua_element_t *el;
+ const char *inpnam;
+ mrp_lua_element_input_t *inp;
+ size_t i;
+
+ MRP_LUA_ENTER;
+
+ lua_rawgeti(L, 1, INPUT_IDX);
+ el = element_check(L, -1);
+ lua_pop(L, 1);
+
+ inpnam = luaL_checklstring(L, 2, NULL);
+
+ mrp_debug("writing %s.inputs.%s", el->name, inpnam);
+
+ for (i = 0; i < el->ninput; i++) {
+ inp = el->inputs + i;
+
+ if (!strcmp(inpnam, inp->name)) {
+ luaL_argcheck(L, !inp->type, 1, "input already assigned");
+
+ switch (lua_type(L, 3)) {
+ case LUA_TNUMBER:
+ inp->type = NUMBER;
+ inp->constant.floating = lua_tonumber(L, 3);
+ break;
+ case LUA_TSTRING:
+ inp->type = STRING;
+ inp->constant.string = lua_tolstring(L, 3, NULL);
+ break;
+ case LUA_TTABLE:
+ if ((inp->select = mrp_lua_to_select(L, 3))) {
+ inp->type = SELECT;
+ break;
+ }
+ /* intentional fall through */
+ default:
+ luaL_error(L, "invalid input type '%s' for %s",
+ lua_typename(L, lua_type(L, 3)), inpnam);
+ break;
+ } /* switch type */
+
+ if ((el->inpmask |= INPUT_BIT(i)) == INPUT_MASK(el->ninput))
+ el->install(L, el);
+
+ break;
+ }
+ } /* for inp */
+
+ MRP_LUA_LEAVE(0);
+}
+
+static mrp_lua_element_input_t *element_input_create_userdata(lua_State *L,
+ int idx,
+ size_t *ret_len,
+ mrp_lua_element_mask_t *ret_inpmask)
+{
+ mrp_lua_element_input_t arr[INPUT_MAX + 1], *i, *inp;
+ mrp_lua_element_mask_t inpmask;
+ const char *name;
+ size_t namlgh;
+ size_t len;
+
+ idx = (idx < 0) ? lua_gettop(L) + idx + 1 : idx;
+ len = 0;
+ inpmask = 0;
+
+ luaL_checktype(L, idx, LUA_TTABLE);
+
+ memset(arr, 0, sizeof(arr));
+
+ MRP_LUA_FOREACH_FIELD(L, idx, name, namlgh) {
+ if (len >= INPUT_MAX)
+ luaL_error(L, "too many inputs (max %d allowes)", INPUT_MAX);
+
+ i = arr + len++;
+
+ if (namlgh < 1) {
+ if (lua_type(L, -1) == LUA_TSTRING)
+ i->name = mrp_strdup(luaL_checkstring(L, -1));
+ else {
+ luaL_error(L, "invalid type '%s' for input name",
+ lua_typename(L, lua_type(L, -1)));
+ }
+ }
+ else {
+ switch (lua_type(L, -1)) {
+
+ case LUA_TNUMBER:
+ i->name = mrp_strdup(name);
+ i->type = NUMBER;
+ i->constant.floating = luaL_checknumber(L, -1);
+ break;
+
+ case LUA_TSTRING:
+ i->name = mrp_strdup(name);
+ i->type = STRING;
+ i->constant.string = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case LUA_TTABLE:
+ i->name = mrp_strdup(name);
+ i->type = SELECT;
+ i->select = mrp_lua_select_check(L, -1);
+ break;
+
+ default:
+ luaL_error(L, "invalid input type %s",
+ lua_typename(L, lua_type(L, -1)));
+ break;
+ }
+
+ inpmask |= INPUT_BIT(len - 1);
+ }
+ } /* MRP_LUA_FOREACH_FIELD */
+
+ if (!(inp = mrp_alloc(sizeof(mrp_lua_element_input_t) * (len + 1))))
+ luaL_error(L, "can't allocate memory");
+
+ memcpy(inp, arr, sizeof(mrp_lua_element_input_t) * len);
+ memset(inp + len, 0, sizeof(mrp_lua_element_input_t));
+
+ if (ret_len)
+ *ret_len = len;
+
+ if (ret_inpmask)
+ *ret_inpmask = inpmask;
+
+ return inp;
+}
+
+static mrp_lua_mdb_table_t **element_output_check(lua_State *L,
+ int idx,
+ size_t *ret_len)
+{
+ mrp_lua_mdb_table_t **arr;
+ size_t len, i;
+ size_t size;
+
+ luaL_checktype(L, idx, LUA_TTABLE);
+ len = luaL_getn(L, idx);
+ size = sizeof(mrp_lua_mdb_table_t *) * (len + 1);
+
+ if (!(arr = mrp_alloc(size))) {
+ luaL_error(L, "can't allocate %d byte long memory", size);
+ return NULL;
+ }
+
+ lua_pushvalue(L, idx);
+
+ for (i = 0; i < len; i++) {
+ lua_pushnumber(L, (int)(i+1));
+ lua_gettable(L, -2);
+
+ arr[i] = mrp_lua_table_check(L, -1);
+
+ lua_pop(L, 1);
+ }
+
+ arr[i] = NULL;
+
+ lua_pop(L, 1);
+
+ if (ret_len)
+ *ret_len = len;
+
+ return arr;
+}
+
+static field_t field_check(lua_State *L, int idx, const char **ret_fldnam)
+{
+ const char *fldnam;
+ size_t fldnamlen;
+ field_t fldtyp;
+
+ if (!(fldnam = lua_tolstring(L, idx, &fldnamlen)))
+ fldtyp = 0;
+ else
+ fldtyp = field_name_to_type(fldnam, fldnamlen);
+
+ if (ret_fldnam)
+ *ret_fldnam = fldnam;
+
+ return fldtyp;
+}
+
+static field_t field_name_to_type(const char *name, size_t len)
+{
+ switch (len) {
+
+ case 4:
+ if (!strcmp(name, "name"))
+ return NAME;
+ if (!strcmp(name, "type"))
+ return TYPE;
+ break;
+
+ case 6:
+ if (!strcmp(name, "inputs"))
+ return INPUTS;
+ if (!strcmp(name, "update"))
+ return UPDATE;
+ if (!strcmp(name, "object"))
+ return OBJECT;
+ break;
+
+ case 7:
+ if (!strcmp(name, "outputs"))
+ return OUTPUTS;
+ break;
+
+ case 8:
+ if (!strcmp(name, "property"))
+ return PROPERTY;
+ if (!strcmp(name, "initiate"))
+ return INITIATE;
+ break;
+
+ case 9:
+ if (!strcmp(name, "interface"))
+ return INTERFACE;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+const char *mrp_lua_sink_get_interface(mrp_lua_sink_t *s)
+{
+ return s ? s->interface : "";
+}
+
+const char *mrp_lua_sink_get_object(mrp_lua_sink_t *s)
+{
+ return s ? s->object : "";
+}
+
+const char *mrp_lua_sink_get_type(mrp_lua_sink_t *s)
+{
+ return s ? s->type : "";
+}
+
+const char *mrp_lua_sink_get_property(mrp_lua_sink_t *s)
+{
+ return s ? s->property : "";
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_ELEMENT_H__
+#define __MURPHY_LUA_ELEMENT_H__
+
+#include <lua.h>
+#include <murphy-db/mqi-types.h>
+
+#define MRP_LUA_ELEMENT_FIELDS \
+ const char *name; \
+ mrp_lua_element_mask_t inpmask; \
+ size_t ninput; \
+ mrp_lua_element_input_t *inputs; \
+ size_t noutput; \
+ mrp_lua_mdb_table_t **outputs; \
+ void (*install)(lua_State *, void *); \
+ mrp_funcbridge_t *update
+
+#define mrp_lua_get_sink_name(s) \
+ mrp_lua_get_element_name((mrp_lua_element_t *)(s))
+#define mrp_lua_get_sink_name(s) \
+ mrp_lua_get_element_name((mrp_lua_element_t *)(s))
+#define mrp_lua_sink_get_input_count(s) \
+ mrp_lua_element_get_input_count((mrp_lua_element_t *)(s))
+#define mrp_lua_sink_get_input_name(s,i) \
+ mrp_lua_element_get_input_name((mrp_lua_element_t *)(s),i)
+#define mrp_lua_sink_get_input_index(s,n) \
+ mrp_lua_element_get_input_index((mrp_lua_element_t *)(s),n);
+#define mrp_lua_sink_get_column_index(s,i,n) \
+ mrp_lua_element_get_column_index((mrp_lua_element_t *)(s),i,n)
+#define mrp_lua_sink_get_column_count(s,i) \
+ mrp_lua_element_get_column_count((mrp_lua_element_t *)(s),i)
+#define mrp_lua_sink_get_column_type(s,i,c) \
+ mrp_lua_element_get_column_type((mrp_lua_element_t *)(s),i,c)
+#define mrp_lua_sink_get_row_count(s,i) \
+ mrp_lua_element_get_row_count((mrp_lua_element_t *)(s),i)
+#define mrp_lua_sink_get_string(s,i,c,r,b,l) \
+ mrp_lua_element_get_string((mrp_lua_element_t *)(s),i,c,r,b,l)
+#define mrp_lua_sink_get_integer(s,i,c,r) \
+ mrp_lua_element_get_integer((mrp_lua_element_t *)(s),i,c,r)
+#define mrp_lua_sink_get_unsigned(s,i,c,r) \
+ mrp_lua_element_get_unsigned((mrp_lua_element_t *)(s),i,c,r)
+#define mrp_lua_sink_get_floating(s,i,c,r) \
+ mrp_lua_element_get_floating((mrp_lua_element_t *)(s),i,c,r)
+
+
+typedef struct mrp_lua_element_s mrp_lua_element_t;
+typedef struct mrp_lua_sink_s mrp_lua_sink_t;
+typedef struct mrp_lua_element_input_s mrp_lua_element_input_t;
+typedef uint32_t mrp_lua_element_mask_t;
+
+void mrp_lua_create_element_class(lua_State *L);
+
+const char *mrp_lua_get_element_name(mrp_lua_element_t *el);
+int mrp_lua_element_get_input_count(mrp_lua_element_t *el);
+const char *mrp_lua_element_get_input_name(mrp_lua_element_t *el, int inpidx);
+int mrp_lua_element_get_input_index(mrp_lua_element_t *el, const char *inpnam);
+int mrp_lua_element_get_column_index(mrp_lua_element_t *el, int inpidx,
+ const char *colnam);
+int mrp_lua_element_get_column_count(mrp_lua_element_t *el, int inpidx);
+
+mqi_data_type_t mrp_lua_element_get_column_type(mrp_lua_element_t *el,
+ int inpidx, int colidx);
+int mrp_lua_element_get_row_count(mrp_lua_element_t *el, int inpidx);
+const char *mrp_lua_element_get_string(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx,
+ char * buf, int len);
+int32_t mrp_lua_element_get_integer(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx);
+
+uint32_t mrp_lua_element_get_unsigned(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx);
+double mrp_lua_element_get_floating(mrp_lua_element_t *el, int inpidx,
+ int colidx, int rowidx);
+
+const char *mrp_lua_sink_get_interface(mrp_lua_sink_t *s);
+const char *mrp_lua_sink_get_object(mrp_lua_sink_t *s);
+const char *mrp_lua_sink_get_type(mrp_lua_sink_t *s);
+const char *mrp_lua_sink_get_property(mrp_lua_sink_t *s);
+
+
+#endif /* __MURPHY_LUA_ELEMENT_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+#include <murphy/common/debug.h>
+#include <murphy-db/mqi.h>
+#include <murphy-db/mql.h>
+
+#include <murphy/core/context.h>
+#include <murphy/core/scripting.h>
+#include <murphy/core/lua-decision/mdb.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/strarray.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+
+#define TABLE_CLASS MRP_LUA_CLASS(mdb, table)
+#define SELECT_CLASS MRP_LUA_CLASS(mdb, select)
+
+#define TABLE_ROW_CLASSID MRP_LUA_CLASSID_ROOT "table_row"
+#define SELECT_ROW_CLASSID MRP_LUA_CLASSID_ROOT "select_row"
+
+
+typedef enum field_e field_t;
+typedef struct row_s row_t;
+typedef union value_u value_t;
+typedef struct const_def_s const_def_t;
+
+
+
+enum field_e {
+ NAME = 1,
+ INDEX,
+ COLUMNS,
+ TABLE,
+ CONDITION,
+ STATEMENT,
+ SINGLEVAL,
+ CREATE
+};
+
+
+struct mrp_lua_mdb_table_s {
+ bool builtin;
+ mqi_handle_t handle;
+ const char *name;
+ mrp_lua_strarray_t *index;
+ size_t ncolumn;
+ mqi_column_def_t *columns;
+ size_t nrow;
+};
+
+
+struct mrp_lua_mdb_select_s {
+ const char *name;
+ const char *table_name;
+ mrp_lua_strarray_t *columns;
+ const char *condition;
+ struct {
+ const char *string;
+ mql_statement_t *precomp;
+ } statement;
+ mql_result_t *result;
+ size_t nrow;
+};
+
+struct row_s {
+ int index;
+ void *data;
+};
+
+union value_u {
+ char *string;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+};
+
+struct const_def_s {
+ const char *name;
+ mqi_data_type_t value;
+};
+
+
+
+static int table_create_from_lua(lua_State *);
+static int table_getfield(lua_State *);
+static int table_setfield(lua_State *);
+static int table_tostring(lua_State *);
+static int table_insert(lua_State *);
+static int table_replace(lua_State *);
+static int table_update(lua_State *);
+static int table_delete(lua_State *);
+static void table_destroy_from_lua(void *);
+
+static void table_row_class_create(lua_State *);
+/* static int table_row_create(lua_State *, int, void *, int); */
+static int table_row_getfield(lua_State *);
+static int table_row_setfield(lua_State *);
+static int table_row_getlength(lua_State *);
+static int table_row_getvalues(lua_State *, mrp_lua_mdb_table_t *, int, bool,
+ mqi_column_desc_t *, value_t *);
+static void table_row_resetvalues(mrp_lua_mdb_table_t *, mqi_column_desc_t *,
+ value_t *);
+static mrp_lua_mdb_table_t *table_row_check(lua_State *, int, int *);
+
+static int select_create_from_lua(lua_State *);
+static int select_getfield(lua_State *);
+static int select_setfield(lua_State *);
+static void select_destroy_from_lua(void *);
+static int select_update(lua_State *, int, mrp_lua_mdb_select_t *);
+static int select_update_from_lua(lua_State *);
+static int select_update_from_resolver(mrp_scriptlet_t *,mrp_context_tbl_t *);
+static void select_install(lua_State *, mrp_lua_mdb_select_t *);
+
+static void select_row_class_create(lua_State *);
+/* static int select_row_create(lua_State *, int, void *, int); */
+static int select_row_getfield(lua_State *);
+static int select_row_setfield(lua_State *);
+static int select_row_getlength(lua_State *);
+static mrp_lua_mdb_select_t *select_row_check(lua_State *, int, int *);
+
+static bool define_constants(lua_State *);
+
+static field_t field_check(lua_State *, int, const char **);
+static field_t field_name_to_type(const char *, size_t);
+
+static mqi_column_def_t *check_coldefs(lua_State *, int, size_t *);
+static int push_coldefs(lua_State *, mqi_column_def_t *, size_t);
+static void free_coldefs(mqi_column_def_t *);
+
+static int row_create(lua_State *, int, void *, int, const char *);
+static row_t *row_check(lua_State *, int, const char *);
+
+static void adjust_lua_table_size(lua_State *, int, void *, size_t, size_t,
+ const char *);
+static bool create_mdb_table(mrp_lua_mdb_table_t *);
+static mqi_cond_entry_t *condition_check(lua_State *,int,mrp_lua_mdb_table_t*);
+
+
+MRP_LUA_METHOD_LIST_TABLE (
+ table_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (table_create_from_lua)
+ MRP_LUA_METHOD (insert, table_insert )
+ MRP_LUA_METHOD (replace, table_replace )
+ MRP_LUA_METHOD (update, table_update )
+ MRP_LUA_METHOD (delete, table_delete )
+);
+
+#if 0
+MRP_LUA_METHOD_LIST_TABLE (
+ table_row_methods, /* methodlist name */
+);
+#endif
+
+MRP_LUA_METHOD_LIST_TABLE (
+ select_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (select_create_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ table_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (table_create_from_lua)
+ MRP_LUA_OVERRIDE_GETFIELD (table_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (table_setfield)
+ MRP_LUA_OVERRIDE_STRINGIFY (table_tostring)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ table_row_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (table_row_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (table_row_setfield)
+ MRP_LUA_OVERRIDE_GETLENGTH (table_row_getlength)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ select_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (select_create_from_lua)
+ MRP_LUA_OVERRIDE_GETFIELD (select_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (select_setfield)
+ MRP_LUA_METHOD (update, select_update_from_lua)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ select_row_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (select_row_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (select_row_setfield)
+ MRP_LUA_OVERRIDE_GETLENGTH (select_row_getlength)
+);
+
+MRP_LUA_CLASS_DEF (
+ mdb, /* class name */
+ table, /* constructor name */
+ mrp_lua_mdb_table_t, /* userdata type */
+ table_destroy_from_lua, /* userdata destructor */
+ table_methods, /* class methods */
+ table_overrides /* override methods */
+);
+
+MRP_LUA_CLASS_DEF (
+ mdb, /* class name */
+ select, /* constructor name */
+ mrp_lua_mdb_select_t, /* userdata type */
+ select_destroy_from_lua, /* userdata destructor */
+ select_methods, /* class methods */
+ select_overrides /* override methods */
+);
+
+void mrp_lua_create_mdb_class(lua_State *L)
+{
+ mrp_lua_create_object_class(L, TABLE_CLASS);
+ mrp_lua_create_object_class(L, SELECT_CLASS);
+
+ table_row_class_create(L);
+ select_row_class_create(L);
+
+ define_constants(L);
+
+ mrp_lua_findtable(L, MRP_LUA_GLOBALTABLE, "builtin.table", 20);
+}
+
+mrp_lua_mdb_table_t *mrp_lua_create_builtin_table(lua_State *L,
+ mqi_handle_t handle)
+{
+ mrp_lua_mdb_table_t *tbl = NULL;
+
+ MRP_UNUSED(L);
+ MRP_UNUSED(handle);
+
+ return tbl;
+}
+
+mrp_lua_mdb_table_t *mrp_lua_table_check(lua_State *L, int idx)
+{
+ return (mrp_lua_mdb_table_t *)mrp_lua_check_object(L, TABLE_CLASS, idx);
+}
+
+mrp_lua_mdb_table_t *mrp_lua_to_table(lua_State *L, int idx)
+{
+ return (mrp_lua_mdb_table_t *)mrp_lua_to_object(L, TABLE_CLASS, idx);
+}
+
+int mrp_lua_push_table(lua_State *L, mrp_lua_mdb_table_t *tbl)
+{
+ return mrp_lua_push_object(L, tbl);
+}
+
+const char *mrp_lua_table_name(mrp_lua_mdb_table_t *tbl)
+{
+ return (tbl && tbl->name) ? tbl->name : "<unknown>";
+}
+
+mrp_lua_mdb_select_t *mrp_lua_select_check(lua_State *L, int idx)
+{
+ return (mrp_lua_mdb_select_t *)mrp_lua_check_object(L, SELECT_CLASS, idx);
+}
+
+mrp_lua_mdb_select_t *mrp_lua_to_select(lua_State *L, int idx)
+{
+ return (mrp_lua_mdb_select_t *)mrp_lua_to_object(L, SELECT_CLASS, idx);
+}
+
+int mrp_lua_push_select(lua_State *L,mrp_lua_mdb_select_t *sel,bool singleval)
+{
+ mql_result_t *rslt;
+ const char *str;
+ lua_Number num;
+ char buf[1024];
+
+ if (!singleval)
+ mrp_lua_push_object(L, sel);
+ else {
+ if (!(rslt = sel->result) || sel->nrow < 1)
+ lua_pushnil(L);
+ else {
+ switch (mql_result_rows_get_row_column_type(rslt, 0)) {
+ case mqi_string:
+ str = mql_result_rows_get_string(rslt, 0, 0, buf,sizeof(buf));
+ lua_pushstring(L, str);
+ break;
+ case mqi_integer:
+ case mqi_unsignd:
+ case mqi_floating:
+ num = mql_result_rows_get_floating(rslt, 0, 0);
+ lua_pushnumber(L, num);
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+ }
+
+ return 1;
+}
+
+const char *mrp_lua_select_name(mrp_lua_mdb_select_t *sel)
+{
+ return (sel && sel->name) ? sel->name : "<unknown>";
+}
+
+int mrp_lua_select_get_column_index(mrp_lua_mdb_select_t *sel,
+ const char *colnam)
+{
+ mrp_lua_strarray_t *cols;
+ size_t colidx;
+
+ if (sel && colnam && (cols = sel->columns)) {
+ for (colidx = 0; colidx < cols->nstring; colidx++) {
+ if (!strcmp(colnam, cols->strings[colidx]))
+ return (int)colidx;
+ }
+ }
+
+ return -1;
+}
+
+int mrp_lua_select_get_column_count(mrp_lua_mdb_select_t *sel)
+{
+ return sel ? mql_result_rows_get_row_column_count(sel->result) : -1;
+}
+
+mqi_data_type_t mrp_lua_select_get_column_type(mrp_lua_mdb_select_t *sel,
+ int colidx)
+{
+ return sel ? mql_result_rows_get_row_column_type(sel->result, colidx) : -1;
+}
+
+int mrp_lua_select_get_row_count(mrp_lua_mdb_select_t *sel)
+{
+ return sel ? mql_result_rows_get_row_count(sel->result) : -1;
+}
+
+const char *mrp_lua_select_get_string(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx,
+ char * buf, int len)
+{
+ return sel ? mql_result_rows_get_string(sel->result, colidx,rowidx,
+ buf,len) : NULL;
+}
+
+int32_t mrp_lua_select_get_integer(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx)
+{
+ return sel ? mql_result_rows_get_integer(sel->result, colidx,rowidx) : 0;
+}
+
+uint32_t mrp_lua_select_get_unsigned(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx)
+{
+ return sel ? mql_result_rows_get_unsigned(sel->result, colidx,rowidx) : 0;
+}
+
+double mrp_lua_select_get_floating(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx)
+{
+ return sel ? mql_result_rows_get_floating(sel->result,colidx,rowidx) : 0.0;
+}
+
+
+static int table_create_from_lua(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+ size_t fldnamlen;
+ const char *fldnam;
+ mqi_column_def_t defs[MQI_COLUMN_MAX+1], *d;
+ int ndef;
+ size_t i;
+
+ MRP_LUA_ENTER;
+
+ if (!lua_istable(L, 2))
+ luaL_error(L, "expecting table as argument");
+
+ tbl = (mrp_lua_mdb_table_t *)mrp_lua_create_object(L, TABLE_CLASS, NULL,0);
+
+ tbl->builtin = true;
+ tbl->handle = MQI_HANDLE_INVALID;
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ tbl->name = mrp_strdup(luaL_checkstring(L, -1));
+ tbl->handle = mqi_get_table_handle((char *)tbl->name);
+ break;
+
+ case INDEX:
+ tbl->index = mrp_lua_check_strarray(L, -1);
+ break;
+
+ case COLUMNS:
+ tbl->columns = check_coldefs(L, -1, &tbl->ncolumn);
+ break;
+
+ case CREATE:
+ if (!lua_isboolean(L, -1)) {
+ luaL_error(L, "attempt to assign non-boolean "
+ "value to 'create' field");
+ }
+ tbl->builtin = !lua_toboolean(L, -1);
+ break;
+
+ default:
+ luaL_error(L, "unexpected field '%s'", fldnam);
+ break;
+ }
+
+ } /* MRP_LUA_FOREACH_FIELD */
+
+ if (!tbl->name)
+ luaL_error(L, "mandatory 'name' field is unspecified");
+
+ if (tbl->builtin) {
+ if (tbl->handle == MQI_HANDLE_INVALID)
+ luaL_error(L, "table '%s' do not exist", tbl->name);
+ if (tbl->columns && tbl->ncolumn > 0)
+ luaL_error(L, "can't specify columns for an existing table");
+ if ((ndef = mqi_describe(tbl->handle, defs, MQI_COLUMN_MAX)) < 0)
+ luaL_error(L, "can't get column definitions of '%s'", tbl->name);
+
+ tbl->ncolumn = ndef;
+ tbl->columns = mrp_allocz(sizeof(mqi_column_def_t) * (ndef + 1));
+
+ memcpy(tbl->columns, defs, sizeof(mqi_column_def_t) * ndef);
+ for (d = tbl->columns; d->name; d++)
+ d->name = mrp_strdup(d->name);
+ }
+ else {
+ if (tbl->handle != MQI_HANDLE_INVALID) {
+ luaL_error(L, "attempt to create an already existing table '%s'",
+ tbl->name);
+ }
+ if (tbl->columns && tbl->ncolumn > 0) {
+ if (!create_mdb_table(tbl))
+ luaL_error(L, "failed to create MDB table '%s'", tbl->name);
+ if (tbl->index) {
+ for (d = tbl->columns; d->name; d++) {
+ for (i = 0; i < tbl->index->nstring; i++) {
+ if (!strcmp(d->name, tbl->index->strings[i]))
+ d->flags |= MQI_COLUMN_KEY;
+ }
+ }
+ }
+ }
+ else {
+ luaL_error(L,"mandatory 'column' field is unspecified or invalid");
+ }
+ }
+
+ mrp_lua_set_object_name(L, TABLE_CLASS, tbl->name);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_getfield(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl = mrp_lua_table_check(L, 1);
+ const char *fldnam;
+ field_t fld;
+
+ MRP_LUA_ENTER;
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ mrp_debug("reading row %d in '%s'", (int)lua_tointeger(L,-1), tbl->name);
+ lua_rawget(L, 1);
+ }
+ else {
+ fld = field_check(L, 2, &fldnam);
+ lua_pop(L, 1);
+
+ mrp_debug("reading '%s' property of '%s'", fldnam, tbl->name);
+
+ if (!tbl)
+ lua_pushnil(L);
+ else {
+ switch (fld) {
+ case NAME: lua_pushstring(L, tbl->name); break;
+ case INDEX: mrp_lua_push_strarray(L, tbl->index); break;
+ case COLUMNS: push_coldefs(L, tbl->columns, tbl->ncolumn); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_setfield(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+ size_t rowidx;
+
+ MRP_LUA_ENTER;
+
+ tbl = mrp_lua_table_check(L, 1);
+
+ if (lua_type(L, 2) != LUA_TNUMBER)
+ luaL_error(L, "'%s' is read-only", tbl->name);
+ else {
+ rowidx = lua_tointeger(L, 2);
+
+ if (rowidx-- < 1)
+ luaL_error(L, "invalid row index %u", rowidx);
+ if (rowidx > tbl->nrow)
+ luaL_error(L, "row index '%u' is out of sequence", rowidx);
+
+ if (rowidx == tbl->nrow) {
+ adjust_lua_table_size(L, 1, tbl, tbl->nrow, tbl->nrow+1,
+ TABLE_ROW_CLASSID);
+ tbl->nrow++;
+ }
+ else {
+ lua_pushvalue(L, 2);
+ lua_rawget(L, 1);
+ luaL_checktype(L, -1, LUA_TTABLE);
+ }
+
+ mrp_debug("setting row %zu in table '%s'\n", rowidx+1, tbl->name);
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+
+static int table_tostring(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+
+ MRP_LUA_ENTER;
+
+ tbl = mrp_lua_table_check(L, 1);
+
+ if (tbl && tbl->name)
+ lua_pushstring(L, tbl->name);
+ else
+ lua_pushstring(L, "<error>");
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_insert(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+ mqi_column_desc_t desc[MQI_COLUMN_MAX+1];
+ value_t values[MQI_COLUMN_MAX];
+ void *data[2];
+ mqi_handle_t th;
+ int inserted;
+ int sts;
+
+ MRP_LUA_ENTER;
+
+ inserted = -1;
+
+ tbl = mrp_lua_table_check(L, 1);
+ sts = table_row_getvalues(L, tbl, 2, true, desc, values);
+
+ data[0] = values;
+ data[1] = NULL;
+
+ if (!sts)
+ luaL_error(L, "insert failed: some columns do not have value");
+ else {
+ th = mqi_begin_transaction();
+
+ if ((inserted = MQI_INSERT_INTO(tbl->handle, desc, data)) == 1)
+ mqi_commit_transaction(th);
+ else {
+ mqi_rollback_transaction(th);
+ table_row_resetvalues(tbl, desc, values);
+ luaL_error(L, "insert failed: %s", strerror(errno));
+ }
+
+ table_row_resetvalues(tbl, desc, values);
+ }
+
+ lua_pushinteger(L, inserted);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_replace(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+ mqi_column_desc_t desc[MQI_COLUMN_MAX+1];
+ value_t values[MQI_COLUMN_MAX];
+ void *data[2];
+ mqi_handle_t th;
+ int inserted;
+ int sts;
+
+ MRP_LUA_ENTER;
+
+ inserted = -1;
+
+ tbl = mrp_lua_table_check(L, 1);
+ sts = table_row_getvalues(L, tbl, 2, true, desc, values);
+
+ data[0] = values;
+ data[1] = NULL;
+
+ if (!sts)
+ luaL_error(L, "replace failed: some columns do not have value");
+ else {
+ th = mqi_begin_transaction();
+
+ if ((inserted = MQI_REPLACE(tbl->handle, desc, data)) >= 0)
+ mqi_commit_transaction(th);
+ else {
+ mqi_rollback_transaction(th);
+ table_row_resetvalues(tbl, desc, values);
+ luaL_error(L, "replace failed: %s", strerror(errno));
+ }
+
+ table_row_resetvalues(tbl, desc, values);
+ }
+
+ lua_pushinteger(L, inserted);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_update(lua_State *L)
+{
+ int narg;
+ mrp_lua_mdb_table_t *tbl;
+ mqi_column_desc_t desc[MQI_COLUMN_MAX+1];
+ value_t values[MQI_COLUMN_MAX];
+ mqi_cond_entry_t *cond;
+ mqi_handle_t th;
+ int updated;
+ int sts;
+
+ MRP_LUA_ENTER;
+
+ narg = lua_gettop(L);
+ tbl = mrp_lua_table_check(L, 1);
+ sts = table_row_getvalues(L, tbl, 2, false, desc, values);
+ cond = NULL;
+
+ if (!sts) {
+ luaL_error(L, "update failed: no values");
+ updated = FALSE; /* not reached, avoid uninitialized usage warning */
+ }
+ else if (narg > 2 && !(cond = condition_check(L, 3, tbl))) {
+ luaL_error(L, "update failed: invalid condition");
+ updated = FALSE; /* not reached, avoid uninitialized usage warning */
+ }
+ else {
+ th = mqi_begin_transaction();
+
+ if ((updated = MQI_UPDATE(tbl->handle, desc, &values, cond)) >= 0)
+ mqi_commit_transaction(th);
+ else {
+ mqi_rollback_transaction(th);
+ table_row_resetvalues(tbl, desc, values);
+ luaL_error(L, "update failed: %s", strerror(errno));
+ }
+
+ table_row_resetvalues(tbl, desc, values);
+ }
+
+ lua_pushinteger(L, updated);
+
+ MRP_LUA_LEAVE(1);
+}
+
+
+static int table_delete(lua_State *L)
+{
+ int narg;
+ mrp_lua_mdb_table_t *tbl;
+ mqi_cond_entry_t *cond;
+ mqi_handle_t th;
+ int deleted;
+
+ MRP_LUA_ENTER;
+
+ narg = lua_gettop(L);
+ tbl = mrp_lua_table_check(L, 1);
+ cond = NULL;
+
+ if (narg > 1 && !(cond = condition_check(L, 2, tbl))) {
+ luaL_error(L, "delete failed: invalid condition");
+ deleted = FALSE; /* not reached, avoid uninitialized usage warning */
+ }
+ else {
+ th = mqi_begin_transaction();
+
+ if ((deleted = MQI_DELETE(tbl->handle, cond)) >= 0)
+ mqi_commit_transaction(th);
+ else {
+ mqi_rollback_transaction(th);
+ luaL_error(L, "delete failed: %s", strerror(errno));
+ }
+ }
+
+ lua_pushinteger(L, deleted);
+
+ MRP_LUA_LEAVE(1);
+}
+
+
+static void table_destroy_from_lua(void *data)
+{
+ mrp_lua_mdb_table_t *tbl = (mrp_lua_mdb_table_t *)data;
+
+ MRP_LUA_ENTER;
+
+ if (tbl) {
+ mrp_free((void *)tbl->name);
+ mrp_lua_free_strarray(tbl->index);
+ free_coldefs(tbl->columns);
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+
+static void table_row_class_create(lua_State *L)
+{
+ /* create a metatable for row's */
+ luaL_newmetatable(L, TABLE_ROW_CLASSID);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, table_row_overrides, 0);
+}
+
+#if 0
+static int table_row_create(lua_State *L, int tbl, void *data, int rowidx)
+{
+ int n;
+
+ MRP_LUA_ENTER;
+
+ n = row_create(L, tbl, data, rowidx, TABLE_ROW_CLASSID);
+
+ MRP_LUA_LEAVE(n);
+}
+#endif
+
+static int table_row_getfield(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+ int rowidx;
+
+ MRP_LUA_ENTER;
+
+ tbl = table_row_check(L, 1, &rowidx);
+
+ mrp_debug("reading field in row %d of '%s' table\n", rowidx+1, tbl->name);
+
+ lua_pushnil(L);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_row_setfield(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+ int rowidx;
+
+ MRP_LUA_ENTER;
+
+ tbl = table_row_check(L, 1, &rowidx);
+
+ mrp_debug("writing field in row %d of '%s' table\n", rowidx+1, tbl->name);
+
+ MRP_LUA_LEAVE(0);
+}
+
+static int table_row_getlength(lua_State *L)
+{
+ mrp_lua_mdb_table_t *tbl;
+
+ MRP_LUA_ENTER;
+
+ tbl = table_row_check(L, 1, NULL);
+ lua_pushinteger(L, tbl->ncolumn);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int table_row_getvalues(lua_State *L, mrp_lua_mdb_table_t *tbl,
+ int idx, bool all_fields,
+ mqi_column_desc_t *desc, value_t *values)
+{
+ int sts = 0;
+ mqi_column_def_t *c;
+ mqi_column_desc_t *d;
+ value_t *v;
+ size_t i;
+ int nfield;
+
+ idx = (idx < 0) ? lua_gettop(L) + idx + 1 : idx;
+
+ MRP_LUA_ENTER;
+
+ if (desc && values) {
+ for (i = 0, nfield = 0, sts = 1; sts && i < tbl->ncolumn; i++) {
+ c = tbl->columns + i;
+ d = desc + nfield;
+ v = values + nfield;
+
+ d->cindex = i;
+ d->offset = sizeof(value_t) * nfield;
+
+ lua_pushstring(L, c->name);
+ lua_gettable(L, idx);
+
+ if (lua_isnil(L, -1)) {
+ if (all_fields)
+ sts = 0;
+ }
+ else {
+ switch (c->type) {
+ case mqi_string:
+ v->string = mrp_strdup(luaL_checklstring(L, -1, NULL));
+ break;
+ case mqi_integer:
+ v->integer = luaL_checkinteger(L, -1);
+ break;
+ case mqi_unsignd:
+ if ((v->integer = luaL_checkinteger(L, -1)) < 0)
+ sts = 0;
+ break;
+ case mqi_floating:
+ v->floating = luaL_checknumber(L, -1);
+ break;
+ default:
+ sts = 0;
+ break;
+ }
+
+ nfield++;
+ }
+
+ lua_pop(L, 1);
+ } /* for */
+
+ d = desc + nfield;
+ d->cindex = -1;
+ d->offset = -1;
+
+ if (!nfield)
+ sts = 0;
+ if (!sts)
+ table_row_resetvalues(tbl, desc, values);
+ }
+
+ MRP_LUA_LEAVE(sts);
+}
+
+static void table_row_resetvalues(mrp_lua_mdb_table_t *tbl,
+ mqi_column_desc_t *desc,
+ value_t *values)
+{
+ int cidx;
+ mqi_column_desc_t *d;
+
+ for (d = desc; (cidx = d->cindex) >= 0; d++) {
+ if (tbl->columns[cidx].type == mqi_string)
+ mrp_free(*(void **)((void *)values + d->offset));
+ }
+
+ memset(values, 0, sizeof(value_t) * tbl->ncolumn);
+}
+
+
+static mrp_lua_mdb_table_t *table_row_check(lua_State *L,
+ int idx,
+ int *ret_rowidx)
+{
+ row_t *row = row_check(L, idx, TABLE_ROW_CLASSID);
+
+ if (ret_rowidx)
+ *ret_rowidx = row->index;
+
+ return (mrp_lua_mdb_table_t *)row->data;
+}
+
+
+static int select_create_from_lua(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel;
+ size_t fldnamlen;
+ const char *fldnam;
+ const char *condition;
+ char cols[1024];
+ char qry[2048];
+
+ MRP_LUA_ENTER;
+
+ if (!lua_istable(L, 2))
+ luaL_error(L, "expecting table as argument");
+
+ sel = (mrp_lua_mdb_select_t *)mrp_lua_create_object(L,SELECT_CLASS,NULL,0);
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ sel->name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case TABLE:
+ sel->table_name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case COLUMNS:
+ sel->columns = mrp_lua_check_strarray(L, -1);
+ break;
+
+ case CONDITION:
+ condition = luaL_checkstring(L, -1);
+
+ if (strchr(condition, '%'))
+ luaL_error(L, "non-static condition '%s'", condition);
+ else
+ sel->condition = mrp_strdup(condition);
+ break;
+
+ default:
+ luaL_error(L, "unexpected field '%s'", fldnam);
+ break;
+ }
+ } /* MRP_LUA_FOREACH_FIELD */
+
+ if (!sel->name)
+ luaL_error(L, "mandatory 'name' field is missing");
+ if (!sel->table_name)
+ luaL_error(L, "mandatory 'table' field is missing");
+ if (!sel->columns || !sel->columns->nstring)
+ luaL_error(L, "mandatory 'column' field is missing or invalid");
+
+ mrp_lua_print_strarray(sel->columns, cols, sizeof(cols));
+
+ if (!sel->condition) {
+ snprintf(qry, sizeof(qry), "SELECT %s FROM %s",
+ cols, sel->table_name);
+ }
+ else {
+ snprintf(qry, sizeof(qry), "SELECT %s FROM %s WHERE %s",
+ cols, sel->table_name, sel->condition);
+ }
+
+ sel->statement.string = mrp_strdup(qry);
+
+ mrp_lua_set_object_name(L, SELECT_CLASS, sel->name);
+
+ mrp_debug("select '%s' created", sel->name);
+
+ select_install(L, sel);
+
+ select_update(L, -1, sel);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int select_getfield(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel = mrp_lua_select_check(L, 1);
+ field_t fld;
+ const char *fldnam;
+
+ MRP_LUA_ENTER;
+
+ if (!sel)
+ lua_pushnil(L);
+ else {
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ mrp_debug("reading row %d in '%s'", (int)lua_tointeger(L,-1),
+ sel->name);
+ lua_rawget(L, 1);
+ }
+ else {
+ fld = field_check(L, 2, &fldnam);
+ lua_pop(L, 1);
+
+ mrp_debug("reading property %s in '%s'", fldnam, sel->name);
+
+ if (fld) {
+ switch (fld) {
+ case NAME: lua_pushstring(L, sel->name); break;
+ case TABLE: lua_pushstring(L, sel->table_name); break;
+ case COLUMNS: mrp_lua_push_strarray(L, sel->columns); break;
+ case CONDITION: lua_pushstring(L, sel->condition); break;
+ case STATEMENT: lua_pushstring(L,sel->statement.string); break;
+ case SINGLEVAL: mrp_lua_push_select(L, sel, true); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+ else {
+ if (!fldnam || !luaL_getmetafield(L, 1, fldnam))
+ lua_pushnil(L);
+ }
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int select_setfield(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel;
+
+ MRP_LUA_ENTER;
+
+ sel = mrp_lua_select_check(L, 1);
+
+ luaL_error(L, "'%s' is read-only", sel->name);
+
+ MRP_LUA_LEAVE(0);
+}
+
+
+static void select_destroy_from_lua(void *data)
+{
+ mrp_lua_mdb_select_t *sel = (mrp_lua_mdb_select_t *)data;
+
+ MRP_LUA_ENTER;
+
+ if (sel) {
+ mrp_lua_free_strarray(sel->columns);
+ mrp_free((void *)sel->name);
+ mrp_free((void *)sel->table_name);
+ mrp_free((void *)sel->condition);
+ mrp_free((void *)sel->statement.string);
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static int select_update(lua_State *L, int tbl, mrp_lua_mdb_select_t *sel)
+{
+ mql_statement_t *statement;
+ mql_result_t *result;
+ int nrow;
+
+ MRP_LUA_ENTER;
+
+ if (!sel->statement.precomp)
+ sel->statement.precomp = mql_precompile(sel->statement.string);
+
+ if (!(statement = sel->statement.precomp))
+ nrow = 0;
+ else {
+ mql_result_free(sel->result);
+ sel->result = NULL;
+
+ result = mql_exec_statement(mql_result_rows, statement);
+ if (!mql_result_is_success(result)) {
+ nrow = -mql_result_error_get_code(result);
+ }
+ else {
+ sel->result = result;
+ nrow = mql_result_rows_get_row_count(result);
+ }
+ }
+
+ mrp_debug("\"%s\" resulted %d rows", sel->statement.string, nrow);
+
+ if (nrow >= 0) {
+ adjust_lua_table_size(L, tbl,sel, sel->nrow, nrow, SELECT_ROW_CLASSID);
+ sel->nrow = nrow;
+ }
+
+ MRP_LUA_LEAVE(nrow);
+}
+
+static int select_update_from_lua(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel;
+ int nrow;
+
+ MRP_LUA_ENTER;
+
+ sel = mrp_lua_select_check(L, 1);
+
+ mrp_debug("update request for select '%s'\n", sel->name);
+
+ nrow = select_update(L, 1, sel);
+
+ lua_pushinteger(L, nrow < 0 ? 0 : nrow);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int select_update_from_resolver(mrp_scriptlet_t *script,
+ mrp_context_tbl_t *ctbl)
+{
+ mrp_lua_mdb_select_t *sel = (mrp_lua_mdb_select_t *)script->data;
+ lua_State *L = mrp_lua_get_lua_state();
+ int nrow;
+
+ MRP_LUA_ENTER;
+
+ MRP_UNUSED(ctbl);
+
+ if (!sel || !L)
+ return -EINVAL;
+
+ mrp_debug("update request for select '%s'", sel->name);
+
+ mrp_lua_push_object(L, sel);
+
+ nrow = select_update(L, -1, sel);
+
+ lua_pop(L, 1);
+
+ MRP_LUA_LEAVE(nrow >= 0);
+}
+
+
+static void select_install(lua_State *L, mrp_lua_mdb_select_t *sel)
+{
+ static mrp_interpreter_t select_updater = {
+ { NULL, NULL },
+ "select_updater",
+ NULL,
+ NULL,
+ NULL,
+ select_update_from_resolver,
+ NULL
+ };
+
+ mrp_context_t *ctx;
+ char target[1024], table[1024];
+ const char *depends;
+
+ MRP_LUA_ENTER;
+
+ MRP_UNUSED(L);
+
+ ctx = mrp_lua_get_murphy_context();
+
+ if (ctx == NULL || ctx->r == NULL) {
+ printf("Invalid or incomplete murphy context.\n");
+ return;
+ }
+
+ snprintf(target, sizeof(target), "_select_%s", sel->name);
+ snprintf(table , sizeof(table) , "$%s" , sel->table_name);
+
+ depends = table;
+
+ printf("\n%s: %s\n\tupdate(%s)\n", target, depends, sel->name);
+
+
+
+ if (!mrp_resolver_add_prepared_target(ctx->r, target, &depends, 1,
+ &select_updater, NULL, sel))
+ {
+ mrp_log_error("Failed to install resolver target for element '%s'.",
+ sel->name);
+ MRP_LUA_LEAVE_ERROR(L,
+ "Failed to install resolver target for "
+ "element '%s'.", sel->name);
+ }
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static void select_row_class_create(lua_State *L)
+{
+ /* create a metatable for row's */
+ luaL_newmetatable(L, SELECT_ROW_CLASSID);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, select_row_overrides, 0);
+}
+
+#if 0
+static int select_row_create(lua_State *L, int tbl, void *data, int rowidx)
+{
+ int n;
+
+ MRP_LUA_ENTER;
+
+ n = row_create(L, tbl, data, rowidx, SELECT_ROW_CLASSID);
+
+ MRP_LUA_LEAVE(n);
+}
+#endif
+
+static int select_row_getfield(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel;
+ mrp_lua_strarray_t *cols;
+ mql_result_t *rslt;
+ const char *fldnam;
+ int rowidx;
+ int colidx;
+ const char *string;
+ lua_Number number;
+ char buf[1024];
+
+ MRP_LUA_ENTER;
+
+ sel = select_row_check(L, 1, &rowidx);
+ cols = sel->columns;
+ rslt = sel->result;
+
+ mrp_debug("reading field in row %d of '%s' selection\n",
+ rowidx+1, sel ? sel->name : "<unknwon>");
+
+ if (!sel || !rslt || (size_t)rowidx >= sel->nrow)
+ lua_pushnil(L); /* we should never get here actually */
+
+
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ fldnam = lua_tostring(L, 2);
+ for (colidx = 0; colidx < (int)cols->nstring; colidx++) {
+ if (!strcmp(fldnam, cols->strings[colidx]))
+ break;
+ }
+ goto get_data;
+
+ case LUA_TNUMBER:
+ colidx = lua_tointeger(L, 2) - 1;
+ /* intentional fall through */
+
+ get_data:
+ if (colidx < 0 || colidx >= (int)cols->nstring)
+ goto no_data;
+
+ switch (mql_result_rows_get_row_column_type(rslt, colidx)) {
+ case mqi_string:
+ string = mql_result_rows_get_string(rslt, colidx, rowidx,
+ buf, sizeof(buf));
+ lua_pushstring(L, string);
+ break;
+ case mqi_integer:
+ case mqi_unsignd:
+ case mqi_floating:
+ number = mql_result_rows_get_floating(rslt, colidx, rowidx);
+ lua_pushnumber(L, number);
+ break;
+ default:
+ goto no_data;
+ }
+ break;
+
+ default:
+ no_data:
+ lua_pushnil(L);
+ break;
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int select_row_setfield(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel;
+ int rowidx;
+
+ MRP_LUA_ENTER;
+
+ sel = select_row_check(L, 1, &rowidx);
+
+ luaL_error(L, "attempt to write row %u of read-only selection '%s'",
+ rowidx+1, sel->name);
+
+ MRP_LUA_LEAVE(0);
+}
+
+static int select_row_getlength(lua_State *L)
+{
+ mrp_lua_mdb_select_t *sel;
+
+ MRP_LUA_ENTER;
+
+ sel = select_row_check(L, 1, NULL);
+ lua_pushinteger(L, sel->columns->nstring);
+
+ MRP_LUA_LEAVE(1);
+}
+
+
+static mrp_lua_mdb_select_t *select_row_check(lua_State *L,
+ int idx,
+ int *ret_rowidx)
+{
+ row_t *row = row_check(L, idx, SELECT_ROW_CLASSID);
+
+ if (ret_rowidx)
+ *ret_rowidx = row->index;
+
+ return (mrp_lua_mdb_select_t *)row->data;
+}
+
+static bool define_constants(lua_State *L)
+{
+ static const_def_t const_defs[] = {
+ { "string" , mqi_string },
+ { "integer" , mqi_integer },
+ { "unsigned" , mqi_unsignd },
+ { "floating" , mqi_floating },
+ { NULL , mqi_unknown }
+ };
+
+ const_def_t *cd;
+ bool success = false;
+
+ lua_getglobal(L, "mdb");
+
+ if (lua_istable(L, -1)) {
+ for (cd = const_defs; cd->name; cd++) {
+ lua_pushinteger(L, cd->value);
+ lua_setfield(L, -2, cd->name);
+ }
+
+ lua_pop(L, 1);
+
+ success = true;
+ }
+
+ return success;
+}
+
+static field_t field_check(lua_State *L, int idx, const char **ret_fldnam)
+{
+ const char *fldnam;
+ size_t fldnamlen;
+ field_t fldtyp;
+
+ if (!(fldnam = lua_tolstring(L, idx, &fldnamlen)))
+ fldtyp = 0;
+ else
+ fldtyp = field_name_to_type(fldnam, fldnamlen);
+
+ if (ret_fldnam)
+ *ret_fldnam = fldnam;
+
+ return fldtyp;
+}
+
+static field_t field_name_to_type(const char *name, size_t len)
+{
+ switch (len) {
+
+ case 4:
+ if (!strcmp(name, "name"))
+ return NAME;
+ break;
+
+ case 5:
+ if (!strcmp(name, "index"))
+ return INDEX;
+ if (!strcmp(name, "table"))
+ return TABLE;
+ break;
+
+ case 6:
+ if (!strcmp(name, "create"))
+ return CREATE;
+ break;
+
+ case 7:
+ if (!strcmp(name, "columns"))
+ return COLUMNS;
+ break;
+
+ case 9:
+ if (!strcmp(name, "statement"))
+ return STATEMENT;
+ if (!strcmp(name, "condition"))
+ return CONDITION;
+ break;
+
+ case 12:
+ if (!strcmp(name, "single_value"))
+ return SINGLEVAL;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+static mqi_column_def_t *check_coldefs(lua_State *L, int t, size_t *ret_len)
+{
+ size_t tlen, dlen;
+ size_t size;
+ mqi_column_def_t *coldefs, *cd;
+ size_t i,j;
+
+ t = (t < 0) ? lua_gettop(L) + t + 1 : t;
+
+ luaL_checktype(L, t, LUA_TTABLE);
+ tlen = lua_objlen(L, t);
+ size = sizeof(mqi_column_def_t) * (tlen + 1);
+
+ if (!(coldefs = mrp_allocz(size)))
+ luaL_error(L, "can't allocate %d byte long memory", size);
+ else {
+ for (i = 0; i < tlen; i++) {
+ cd = coldefs + i;
+
+ lua_pushinteger(L, (int)(i+1));
+ lua_gettable(L, t);
+
+ if (!lua_istable(L, -1))
+ goto error;
+
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ dlen = lua_objlen(L, -1);
+
+ for (j = 0; j < dlen; j++) {
+ lua_pushnumber(L, (int)(j+1));
+ lua_gettable(L, -2);
+
+ switch (j) {
+ case 0: cd->name = mrp_strdup(lua_tostring(L, -1)); break;
+ case 1: cd->type = lua_tointeger(L, -1); break;
+ case 2: cd->length = lua_tointeger(L, -1); break;
+ default: cd->type = mqi_error; break;
+ }
+
+ lua_pop(L, 1);
+ }
+
+ lua_pop(L, 1);
+
+ if ( cd->name == NULL ||
+ field_name_to_type(cd->name, strlen(cd->name)) ||
+ (cd->type != mqi_string &&
+ cd->type != mqi_integer &&
+ cd->type != mqi_unsignd &&
+ cd->type != mqi_floating ))
+ goto error;
+ }
+
+ if (ret_len)
+ *ret_len = tlen;
+ }
+
+ return coldefs;
+
+ error:
+ free_coldefs(coldefs);
+ luaL_argerror(L, i+1, "malformed column definition");
+ if (ret_len)
+ *ret_len = 0;
+ return NULL;
+}
+
+static int push_coldefs(lua_State *L, mqi_column_def_t *coldefs, size_t hint)
+{
+ mqi_column_def_t *cd;
+ int i;
+
+ if (!coldefs)
+ lua_pushnil(L);
+ else {
+ lua_createtable(L, hint, 0);
+
+ for (cd = coldefs, i = 1; cd->name; cd++, i++) {
+ lua_pushinteger(L, i);
+
+ lua_createtable(L, cd->length ? 3 : 2, 0);
+
+ lua_pushinteger(L, 1);
+ lua_pushstring(L, cd->name);
+ lua_settable(L, -3);
+
+ lua_pushinteger(L, 2);
+ lua_pushinteger(L, cd->type);
+ lua_settable(L, -3);
+
+ if (cd->length) {
+ lua_pushinteger(L, 3);
+ lua_pushinteger(L, cd->length);
+ lua_settable(L, -3);
+ }
+
+ lua_settable(L, -3);
+ }
+ }
+
+ return 1;
+}
+
+static void free_coldefs(mqi_column_def_t *coldefs)
+{
+ mqi_column_def_t *cd;
+
+ if (coldefs) {
+ for (cd = coldefs; cd->name; cd++)
+ mrp_free((void *)cd->name);
+ mrp_free(coldefs);
+ }
+}
+
+static int row_create(lua_State *L, int tbl, void *data, int rowidx,
+ const char *class_id)
+{
+ row_t *row;
+
+ tbl = (tbl < 0) ? lua_gettop(L) + tbl + 1 : tbl;
+
+ luaL_checktype(L, tbl, LUA_TTABLE);
+
+ row = lua_newuserdata(L, sizeof(row_t));
+
+ row->data = data;
+ row->index = rowidx;
+
+ luaL_getmetatable(L, class_id);
+ lua_setmetatable(L, -2);
+
+ return 1;
+}
+
+static row_t *row_check(lua_State *L, int idx, const char *class_id)
+{
+ row_t *row;
+
+ idx = (idx < 0) ? lua_gettop(L) + idx + 1 : idx;
+
+ row = (row_t *)luaL_checkudata(L, idx, class_id);
+
+ return row;
+}
+
+
+static void adjust_lua_table_size(lua_State *L,
+ int tbl,
+ void *data,
+ size_t old_size,
+ size_t new_size,
+ const char *class_id)
+{
+ size_t rowidx;
+
+ tbl = (tbl < 0) ? lua_gettop(L) + tbl + 1 : tbl;
+
+ luaL_checktype(L, tbl, LUA_TTABLE);
+
+ if (old_size < new_size) {
+ for (rowidx = old_size; rowidx < new_size; rowidx++) {
+ lua_pushinteger(L, (int)(rowidx+1));
+ row_create(L, tbl, data, rowidx, class_id);
+ lua_rawset(L, tbl);
+ }
+ return;
+ }
+
+ if (old_size > new_size) {
+ for (rowidx = new_size; rowidx < old_size; rowidx++) {
+ lua_pushinteger(L, (int)(rowidx+1));
+ lua_pushnil(L);
+ lua_rawset(L, tbl);
+ }
+ return;
+ }
+}
+
+static bool create_mdb_table(mrp_lua_mdb_table_t *tbl)
+{
+ char **index;
+
+ if (!tbl->columns || !tbl->ncolumn)
+ tbl->handle = MQI_HANDLE_INVALID;
+ else {
+ if (!tbl->index || !tbl->index->nstring)
+ index = NULL;
+ else
+ index = (char **)tbl->index->strings;
+
+ tbl->handle = mqi_create_table((char *)tbl->name, MQI_TEMPORARY,
+ index, tbl->columns);
+
+ if (tbl->handle == MQI_HANDLE_INVALID)
+ mrp_debug("failed to create table '%s'", tbl->name);
+ else
+ mrp_debug("table '%s' has been sucessfully created", tbl->name);
+ }
+
+ return (tbl->handle != MQI_HANDLE_INVALID);
+}
+
+static mqi_cond_entry_t *condition_check(lua_State *L,
+ int idx,
+ mrp_lua_mdb_table_t *tbl)
+{
+#define ALIGN(x) ((((x) + (sizeof(void*)-1)) / sizeof(void*)) * sizeof(void*))
+
+ mqi_column_def_t *col;
+ mqi_variable_t *var;
+ mqi_cond_entry_t *cond, *conds;
+ size_t ncond;
+ const char *fldnam;
+ size_t fldnamlen;
+ size_t total_size, cond_size, pool_size;
+ int i;
+ size_t sl, pl;
+ char *pool;
+ const char *s;
+
+ idx = (idx < 0) ? lua_gettop(L) + idx + 1 : idx;
+
+ luaL_checktype(L, idx, LUA_TTABLE);
+
+ cond_size = ALIGN(sizeof(mqi_cond_entry_t) * (MQI_COND_MAX));
+ pool_size = 16384;
+ total_size = cond_size + pool_size;
+
+ conds = mrp_alloc(total_size);
+ pool = (char *)conds + cond_size;
+
+ pl = 0;
+ ncond = 0;
+
+ MRP_LUA_FOREACH_FIELD(L, idx, fldnam, fldnamlen) {
+ if ((i = mqi_get_column_index(tbl->handle, (char *)fldnam)) < 0)
+ luaL_error(L, "invalid field name '%s' in condition", fldnam);
+
+ MRP_ASSERT(i < (int)tbl->ncolumn, "internal error: column index is "
+ "out of range");
+
+ col = tbl->columns + i;
+
+ if (ncond + 4 >= MQI_COND_MAX ||
+ pl + (col->length + sizeof(void *)) > pool_size)
+ goto too_complex;
+
+ if (ncond > 0) {
+ cond = conds + ncond++;
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_and;
+ }
+
+ cond = conds + ncond++;
+ cond->type = mqi_column;
+ cond->u.column = i;
+
+ cond = conds + ncond++;
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_eq;
+
+ cond = conds + ncond++;
+ cond->type = mqi_variable;
+ var = &cond->u.variable;
+
+ var->type = col->type;
+ var->flags = col->flags;
+ var->v.generic = pool + pl;
+
+ switch (col->type) {
+ case mqi_string:
+ if (!(s = luaL_checklstring(L,-1,&sl)) || (int)sl >= col->length) {
+ luaL_error(L, "expect max %u character long string for '%s'",
+ col->length, col->name);
+ }
+ *var->v.varchar = pool + (pl += sizeof(void *));
+ memcpy(pool + pl, s, sl+1);
+ break;
+ case mqi_integer:
+ *var->v.integer = luaL_checkinteger(L, -1);
+ break;
+ case mqi_unsignd:
+ if ((*var->v.integer = luaL_checkinteger(L, -1)) < 0) {
+ luaL_error(L, "attempt to compare '%s' to negative value",
+ col->name);
+ }
+ break;
+ case mqi_floating:
+ *var->v.floating = luaL_checknumber(L, -1);
+ break;
+ default:
+ luaL_error(L, "unsupported type '%s' in condition (column '%s')",
+ mqi_data_type_str(col->type), col->name);
+ break;
+ }
+
+ pl = ALIGN(pl + col->length);
+ }
+
+ cond = conds + ncond++;
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_end;
+
+
+ return conds;
+
+ too_complex:
+ luaL_error(L, "condition is too complex");
+ return NULL;
+
+#undef ALIGN
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_MDB_H__
+#define __MURPHY_LUA_MDB_H__
+
+#include <murphy-db/mqi.h>
+
+typedef struct mrp_lua_mdb_table_s mrp_lua_mdb_table_t;
+typedef struct mrp_lua_mdb_select_s mrp_lua_mdb_select_t;
+typedef struct mrp_lua_mdb_dependency_s mrp_lua_mdb_dependency_t;
+
+void mrp_lua_create_mdb_class(lua_State *L);
+mrp_lua_mdb_table_t *mrp_lua_create_builtin_table(lua_State *L,
+ mqi_handle_t handle);
+mrp_lua_mdb_table_t *mrp_lua_table_check(lua_State *L, int idx);
+mrp_lua_mdb_table_t *mrp_lua_to_table(lua_State *L, int idx);
+int mrp_lua_push_table(lua_State *L, mrp_lua_mdb_table_t *tbl);
+const char *mrp_lua_table_name(mrp_lua_mdb_table_t *tbl);
+
+mrp_lua_mdb_select_t *mrp_lua_select_check(lua_State *L, int idx);
+mrp_lua_mdb_select_t *mrp_lua_to_select(lua_State *L, int idx);
+int mrp_lua_push_select(lua_State *L,mrp_lua_mdb_select_t *sel,bool singleval);
+const char * mrp_lua_select_name(mrp_lua_mdb_select_t *sel);
+int mrp_lua_select_get_column_index(mrp_lua_mdb_select_t *sel,
+ const char *colnam);
+int mrp_lua_select_get_column_count(mrp_lua_mdb_select_t *sel);
+mqi_data_type_t mrp_lua_select_get_column_type(mrp_lua_mdb_select_t *sel,
+ int colidx);
+int mrp_lua_select_get_row_count(mrp_lua_mdb_select_t *sel);
+const char *mrp_lua_select_get_string(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx,
+ char * buf, int len);
+int32_t mrp_lua_select_get_integer(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx);
+
+uint32_t mrp_lua_select_get_unsigned(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx);
+double mrp_lua_select_get_floating(mrp_lua_mdb_select_t *sel,
+ int colidx, int rowidx);
+
+int mrp_lua_dependency_add(lua_State *L, const char *name);
+
+
+#endif /* __MURPHY_LUA_MDB_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-lua-decison
+Description: Murphy policy framework, LUA support library for building decision networks
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-lua-decision -lmurphy-lua-utils @LUA_LIBS@
+Cflags: -I${includedir}
--- /dev/null
+AM_CPPFLAGS = -I$(top_builddir)/src/murphy-db/include -I$(top_builddir)
+AM_CFLAGS = $(WARNING_CFLAGS) $(AM_CPPFLAGS)
+
+
+noinst_PROGRAMS = decision-test
+
+# lua decision network test
+decision_test_SOURCES = decision-test.c
+decision_test_CFLAGS = $(AM_CFLAGS) $(LUA_CFLAGS)
+decision_test_LDADD = ../../../libmurphy-lua-utils.la \
+ ../../../libmurphy-lua-decision.la \
+ ../../../libmurphy-resolver.la \
+ ../../../libmurphy-core.la \
+ ../../../libmurphy-common.la \
+ ../../../murphy-db/mql/libmql.la \
+ ../../../murphy-db/mqi/libmqi.la \
+ ../../../murphy-db/mdb/libmdb.la \
+ $(LUA_LIBS)
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-utils/strarray.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-decision/mdb.h>
+#include <murphy/core/lua-decision/element.h>
+
+
+#define VOLUME_CLASS MRP_LUA_CLASS(volume, limit)
+
+
+typedef enum {
+ DEVICE = 1,
+ STREAM
+} volume_type_t;
+
+typedef enum {
+ TYPE = 1,
+ NAME,
+ DEVICES,
+ STREAMS,
+ LIMIT,
+ UPDATE,
+} volume_field_t;
+
+typedef struct volume_s {
+ volume_type_t type;
+ const char *name;
+ mrp_lua_strarray_t *nodes;
+ double limit;
+ mrp_funcbridge_t *update;
+ void *user_data;
+} volume_t;
+
+static int volume_create(lua_State *);
+static int volume_getfield(lua_State *);
+static int volume_setfield(lua_State *);
+static void volume_destroy(void *);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ volume_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR (volume_create)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ volume_overrides,
+ MRP_LUA_OVERRIDE_CALL (volume_create)
+ MRP_LUA_OVERRIDE_GETFIELD (volume_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (volume_setfield)
+);
+
+
+MRP_LUA_CLASS_DEF (
+ volume, /* class name */
+ limit, /* constructor name */
+ volume_t, /* userdata type */
+ volume_destroy, /* userdata destructor */
+ volume_methods,
+ volume_overrides
+);
+
+
+static size_t nvol;
+static volume_t *vols[5];
+
+static int volume_create(lua_State *L)
+{
+ int table;
+ size_t fldnamlen;
+ const char *fldnam;
+ volume_t *vol;
+
+ vol = (volume_t *)mrp_lua_create_object(L, VOLUME_CLASS, NULL,0);
+
+ table = lua_gettop(L);
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (fldnamlen) {
+ case 7:
+ if (!strcmp(fldnam, "devices")) {
+ if (vol->nodes) {
+ return luaL_error(L, "streams and devices are "
+ "mutually exclusive");
+ }
+ vol->type = DEVICE;
+ vol->nodes = mrp_lua_check_strarray(L, -1);
+ break;
+ }
+ if (!strcmp(fldnam, "streams")) {
+ if (vol->nodes) {
+ return luaL_error(L, "streams and devices are "
+ "mutually exclusive");
+ }
+ vol->type = STREAM;
+ vol->nodes = mrp_lua_check_strarray(L, -1);
+ break;
+ }
+ goto not_userdata;
+
+ case 6:
+ if (!strcmp(fldnam, "update")) {
+ vol->update = mrp_funcbridge_create_luafunc(L, -1);
+ break;
+ }
+ goto not_userdata;
+
+ case 5:
+ if (!strcmp(fldnam, "limit")) {
+ vol->limit = luaL_checknumber(L, -1);
+ break;
+ }
+ goto not_userdata;
+
+ case 4:
+ if (!strcmp(fldnam, "type")) {
+ return luaL_error(L, "type field is readonly");
+ }
+ if (!strcmp(fldnam, "name")) {
+ vol->name = luaL_checklstring(L, -1, NULL);
+ break;
+ }
+ goto not_userdata;
+
+ default:
+ not_userdata:
+ lua_pushvalue(L, -2);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, table);
+ break;
+ }
+ } /* MRP_LUA_FOREACH_FIELD */
+
+ if (!vol->type || !vol->nodes)
+ return luaL_error(L, "Either streams or devices must be present");
+ if (!vol->name)
+ return luaL_error(L, "name is not present");
+
+ mrp_lua_set_object_name(L, VOLUME_CLASS, vol->name);
+
+ vols[nvol++] = vol;
+
+ printf("volume %p\n", vol);
+
+ return 1;
+}
+
+static volume_t *checkvolume(lua_State *L)
+{
+ return (volume_t *)mrp_lua_check_object(L, VOLUME_CLASS, 1);
+}
+
+static volume_field_t checkfield(lua_State *L)
+{
+ size_t len;
+ const char *name;
+
+ name = luaL_checklstring(L, 2, &len);
+
+ switch (len) {
+ case 4:
+ if (!strcmp(name, "type"))
+ return TYPE;
+ if (!strcmp(name, "name"))
+ return NAME;
+ break;
+ case 5:
+ if (!strcmp(name, "limit"))
+ return LIMIT;
+ break;
+ case 6:
+ if (!strcmp(name, "update"))
+ return UPDATE;
+ break;
+ case 7:
+ if (!strcmp(name, "streams"))
+ return STREAMS;
+ if (!strcmp(name, "devices"))
+ return DEVICES;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void volume_destroy(void *data)
+{
+ volume_t *vol = (volume_t *)data;
+ size_t i;
+
+ printf("*** volume destroyed\n");
+
+ mrp_lua_free_strarray(vol->nodes);
+
+ for (i = 0; i < nvol; i++) {
+ if (vols[i] == vol) {
+ while ((i+1) < sizeof(vols)/sizeof(vols[0]) && vols[i+1]) {
+ vols[i] = vols[i+1];
+ i++;
+ }
+ vols[i] = NULL;
+ break;
+ }
+ }
+}
+
+static const char *volumetype2str(volume_type_t type)
+{
+ switch (type) {
+ case DEVICE: return "device";
+ case STREAM: return "stream";
+ default: return "<unknown>";
+ }
+}
+
+
+static int volume_getfield(lua_State *L)
+{
+ volume_t *vol = checkvolume(L);
+ volume_field_t fld = checkfield(L);
+ char buf[256];
+
+ printf("index %d for %s volume (node %s) \n",
+ fld, volumetype2str(vol->type),
+ mrp_lua_print_strarray(vol->nodes, buf, sizeof(buf)));
+
+ switch (fld) {
+ case TYPE:
+ lua_pushstring(L, volumetype2str(vol->type));
+ break;
+ case STREAMS:
+ if (vol->type == STREAM)
+ mrp_lua_push_strarray(L, vol->nodes);
+ else
+ lua_pushnil(L);
+ break;
+ case DEVICES:
+ if (vol->type == DEVICE)
+ mrp_lua_push_strarray(L, vol->nodes);
+ else
+ lua_pushnil(L);
+ break;
+ case LIMIT:
+ lua_pushnumber(L, vol->limit);
+ break;
+ case UPDATE:
+ mrp_funcbridge_push(L, vol->update);
+ break;
+ default:
+ lua_pushvalue(L, 2);
+ lua_rawget(L, 1);
+ break;
+ }
+
+ return 1;
+}
+
+static int volume_setfield(lua_State *L)
+{
+ volume_t *vol = checkvolume(L);
+ volume_field_t fld = checkfield(L);
+ char buf[256];
+
+ printf("new index %d for %s volume (node %s) \n",
+ fld, volumetype2str(vol->type),
+ mrp_lua_print_strarray(vol->nodes, buf, sizeof(buf)));
+
+ switch (fld) {
+ case STREAMS:
+ if (vol->type != STREAM) {
+ return luaL_error(L, "attempt to set sterams for device "
+ "volume limit");
+ }
+ goto set_nodes;
+ case DEVICES:
+ if (vol->type != STREAM) {
+ return luaL_error(L, "attempt to set sterams for device "
+ "volume limit");
+ }
+ goto set_nodes;
+ set_nodes:
+ mrp_lua_free_strarray(vol->nodes);
+ vol->nodes = mrp_lua_check_strarray(L, 3);
+ break;
+ case LIMIT:
+ vol->limit = luaL_checknumber(L, 3);
+ break;
+ case UPDATE:
+ vol->update = mrp_funcbridge_create_luafunc(L, 3);
+ default:
+ lua_rawset(L, 1);
+ break;
+ }
+
+ return 0;
+}
+
+
+static void volume_openlib(lua_State *L)
+{
+
+ mrp_lua_create_object_class(L, VOLUME_CLASS);
+}
+
+
+bool my_update_func(lua_State *L, void *data,
+ const char *signature, mrp_funcbridge_value_t *args,
+ char *ret_type, mrp_funcbridge_value_t *ret_val)
+{
+ MRP_UNUSED(L);
+
+ printf("**** %s(%p) signature='%s' arg1=%p arg2='%s'\n",
+ __FUNCTION__, data, signature,
+ signature[0] == 'o' ? args[0].pointer : NULL,
+ signature[1] == 's' ? args[1].string : "<undefined>");
+
+ *ret_type = MRP_FUNCBRIDGE_FLOATING;
+ ret_val->floating = 3.1415;
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ mrp_funcbridge_value_t args[] = {
+ {.pointer = NULL },
+ {.string = "Hello world, here I am"}
+ };
+
+ const char *pnam = basename(argv[0]);
+ lua_State *L;
+ char buf[512];
+ int error;
+ volume_t *v;
+ mrp_funcbridge_t *fb;
+ mrp_funcbridge_value_t ret;
+ char t;
+
+ if (argc > 2) {
+ printf("Usage: %s [file]\n", pnam);
+ exit(1);
+ }
+
+ if (!(L = luaL_newstate())) {
+ printf("failed to initialize Lua\n");
+ exit(1);
+ }
+
+ printf("Lua initialized\n");
+
+ luaL_openlibs(L);
+ mrp_create_funcbridge_class(L);
+ mrp_lua_create_mdb_class(L);
+ mrp_lua_create_element_class(L);
+ volume_openlib(L);
+
+ mrp_funcbridge_create_cfunc(L, "my_update_func", "os",
+ my_update_func, (void *)0x1234);
+
+ if (argc == 2) {
+ error = luaL_loadfile(L, argv[1]) ||
+ lua_pcall(L, 0, 0, 0);
+ if (error) {
+ printf("%s\n", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+
+ if ((v = args[0].pointer = vols[0]) && (fb = v->update)) {
+ char value[32];
+ if (!mrp_funcbridge_call_from_c(L, fb, "os", args, &t, &ret))
+ printf("*** call failed: %s\n", ret.string);
+ else {
+ switch (t) {
+ case MRP_FUNCBRIDGE_NO_DATA:
+ snprintf(value, sizeof(value), "<no data>");
+ break;
+ case MRP_FUNCBRIDGE_STRING:
+ snprintf(value, sizeof(value), "%s", ret.string);
+ break;
+ case MRP_FUNCBRIDGE_INTEGER:
+ snprintf(value, sizeof(value), "%d", ret.integer);
+ break;
+ case MRP_FUNCBRIDGE_FLOATING:
+ snprintf(value, sizeof(value), "%lf", ret.floating);
+ break;
+ default:
+ snprintf(value, sizeof(value), "<unsupported>");
+ break;
+ }
+ printf("*** return value %s\n", value);
+ }
+ }
+ }
+ else {
+ printf("%s> ", pnam);
+ fflush(stdout);
+
+ while (fgets(buf, sizeof(buf), stdin)) {
+ error = luaL_loadbuffer(L, buf, strlen(buf), "line") ||
+ lua_pcall(L, 0, 0, 0);
+ if (error) {
+ printf("%s\n", lua_tostring(L, -1));
+ lua_pop(L, 1);
+ }
+
+ printf("%s> ", pnam);
+ fflush(stdout);
+ }
+ }
+
+ lua_close(L);
+
+ return 0;
+}
--- /dev/null
+--[[
+for n in pairs(_G) do
+ print(n)
+end
+--]]
+
+print_table = function (tbl)
+ print("-------- mdb start -------");
+ for kn, n in pairs(tbl) do
+ if type(n) ~= "table" then
+ if type(n) == "function" then
+ print(kn.."=<function>")
+ else
+ print(kn.."="..n)
+ end
+ else
+ print("'"..kn.."' table ")
+ for km, m in pairs(n) do
+ if type(n) == "function" then
+ print(" "..km.."=<function>")
+ elseif type(n) == "table" then
+ print(" '"..km.."' table")
+ else
+ print(" "..km.."="..m)
+ end
+ end
+ end
+ end
+ print("--------- mdb end --------");
+end
+
+print_table(mdb)
+
+rows = {{key="a",value="A"},{key="b",value="B"},{key="c",value="C"}}
+
+print("rows[2].value="..rows[2].value)
+
+print("mdb.string="..mdb.string)
+
+mdb.table {
+ name = "amb",
+ index = {"key"},
+ columns = {{"key", mdb.string, 16}, {"value", mdb.floating}}
+}
+
+print("mdb.amb="..tostring(mdb.table.amb))
+
+index = "{"
+
+for _, k in ipairs(mdb.table.amb.index) do
+ index = index.." "..k
+end
+
+index = index.." }"
+
+
+coldefs = "{"
+
+for _, cd in ipairs(mdb.table.amb.columns) do
+ local name, type, length
+
+ for i, v in ipairs(cd) do
+ if i == 1 then name = v end
+ if i == 2 then type = v end
+ if i == 3 then length = v end
+ end
+
+ coldefs = coldefs.." {"..name..","..type
+
+ if length then
+ coldefs = coldefs..","..length
+ end
+
+ coldefs = coldefs.."}"
+end
+
+coldefs = coldefs.." }"
+
+print("mdb.table.amb.name="..mdb.table.amb.name)
+print("mdb.table.amb.index="..index)
+print("mdb.table.amb.columns="..coldefs)
+
+mdb.table.amb[1] = { key = "foo", value = 3.1415 }
+
+mdb.select {
+ name = "speed",
+ table = "amb",
+ columns = {"value"},
+ condition = "key = 'speed'"
+}
+
+--[[
+print("mdb.select.speed.statement="..mdb.select.speed.statement)
+
+q = mdb.select.speed[0]
+
+mdb.select.speed:update()
+
+print_table(mdb)
+
+--]]
+
+element.lua {
+ name = "speed2volume",
+ inputs = { "bar", foo = mdb.select.speed, param = 5 },
+ outputs = { mdb.table { name = "speedvol",
+ index = {"zone", "device"},
+ columns = {{"zone", mdb.string, 16},
+ {"device", mdb.string, 16},
+ {"value", mdb.floating}}
+ }
+ },
+ update = function(self)
+ print("*** element "..self.name.." update function called")
+ end
+}
+
+-- print_table(element)
+
+element.lua.speed2volume.inputs.bar = mdb.select {name = "rpm",
+ table = "amb",
+ columns = {"value"},
+ condition = "key = 'rpm'"}
+
+element.lua.speed2volume:update()
+print("speed2volume.inputs.param="..element.lua.speed2volume.inputs.param)
+-- print("speed2volume.inputs.foo[0].value="..element.lua.speed2volume.inputs.foo[0].value)
+
+volume.limit {
+ name = "speed_adjust",
+ devices = {"speaker", "headphone", "headset"},
+ limit = -0.5,
+ extra = 2468,
+ update = builtin.method.my_update_func
+
+ --[[
+ update = function(self, arg)
+ print("*** lua update function arg="..arg.." extra="..self.extra)
+ return 987.0
+ end
+ --]]
+}
+
+
+foo = volume.limit.speed_adjust
+
+for k,v in pairs(foo) do
+ print(k..": "..type(v))
+end
+
+a = pairs(foo)
+print("a "..type(a))
+
+print("limit "..foo.limit)
+
+foo.limit = -31.2
+foo.yoyo = "a"
+
+print("limit "..foo.limit)
+print("type "..foo.type)
+print("yoyo "..foo.yoyo)
+print("extra "..foo.extra)
+print("volume.limit.speed_adjust "..type(volume.limit.speed_adjust))
+print("builtin.method.my_update_func "..type(builtin.method.my_update_func))
+
+-- builtin.my_update_func()
+print("update returned "..foo:update("Hello world"))
+-- volume.speed_adjust=nil
+-- foo=nil
+collectgarbage()
+print("done");
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C ../.. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C ../.. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <lauxlib.h>
+
+#include "murphy/common/log.h"
+#include "murphy/core/lua-utils/error.h"
+
+
+int mrp_lua_set_error(lua_State *L, char *errbuf, size_t bufsize, ...)
+{
+ va_list ap;
+ char buf[bufsize ? bufsize : 256];
+ char *err;
+ size_t size;
+ char *fmt;
+
+ err = errbuf ? errbuf : buf;
+ size = errbuf ? bufsize : sizeof(buf);
+
+ va_start(ap, bufsize);
+ fmt = va_arg(ap, char *);
+ vsnprintf(err, size, fmt, ap);
+ va_end(ap);
+
+ if (errbuf != NULL)
+ return -1;
+
+ if (L != NULL)
+ luaL_error(L, "%s", errbuf);
+
+ mrp_log_error("Lua error in non-throwable context, '%s'", err);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_ERROR_H__
+#define __MURPHY_LUA_ERROR_H__
+
+#include <stdlib.h>
+#include <setjmp.h>
+#include <lua.h>
+
+#include "murphy/common/debug.h"
+#include "murphy/common/mm.h"
+#include "murphy/common/list.h"
+
+/*
+ * basic error handling
+ */
+
+/** Macro to declare an error buffer for mrp_lua_{error, throw}. */
+#define MRP_LUA_ERRBUF(...) \
+ size_t _ela[] = { 256, __VA_ARGS__ }; \
+ size_t _errl = MRP_ARRAY_SIZE(_ela) == 1 ? _ela[0] : _ela[1]; \
+ char _errb[_errl]
+
+/** Macro to mark arguments to be used by mrp_lua_{error, throw}. */
+#define MRP_LUA_ERRUSE(_ebuf, _esize) \
+ size_t _errl = _esize; \
+ char *_errb = _ebuf
+
+/** Macro to pass on error buffer argument to a function call. */
+#define MRP_LUA_ERRPASS _errb, _errl
+
+/** Macro to omit error buffer from a function call. */
+#define MRP_LUA_ERRNONE NULL, 0
+
+/** Active error buffer. */
+#define MRP_LUA_ERR _errb
+
+/** Macro to pass up, throw, or print an error. */
+#define mrp_lua_error(_retval, _L, ...) \
+ mrp_lua_set_error(_L, _errb, _errl, __VA_ARGS__), \
+ _retval
+
+/** Macro to throw an error/exception. */
+#define mrp_lua_throw(_L, ...) \
+ mrp_lua_set_error(_L, _errb, _errl, __VA_ARGS__)
+
+/** The low-level error formatting/throwing/printing routine. */
+int mrp_lua_set_error(lua_State *L, char *errbuf, size_t size, ...);
+
+
+/*
+ * chained error-path cleanup
+ */
+
+
+/** Error trap callback type. */
+typedef int (*mrp_lua_trapcb_t)(lua_State *L, void *data);
+
+/** An error trap callback buffer. */
+typedef struct {
+ mrp_list_hook_t hook; /* hook to trap-list */
+ mrp_lua_trapcb_t cb; /* trap callback */
+ void *data; /* trap callback data */
+ const char *name; /* stringified data */
+} mrp_lua_trap_t;
+
+/** Lua error buffer with a chain of trap handlers. */
+typedef struct {
+ mrp_list_hook_t traps; /* traps to call during cleanup */
+ lua_State *L; /* lua state buffer */
+ char buf[1024]; /* error message buffer */
+ char *err; /* buffer write pointer */
+ size_t len; /* remaining buffer length */
+ int error; /* error code */
+ jmp_buf jmp; /* saved jump location */
+} mrp_lua_errbuf_t;
+
+
+#define MRP_LUA_CATCH(_err, _L) \
+ mrp_lua_errbuf_t _err##_buf = { \
+ .traps = { .prev = &_err.traps, .next = &_err.traps }, \
+ .buf = { '\0' }, \
+ .len = 0, \
+ .error = 0, \
+ }, _err = &_err##_buf; \
+ if (setjmp(&(_err)->loc) != 0) { \
+ mrp_log_error("%s", (_err)->buf); \
+ mrp_lua_trigger_traps(_err, NULL, _L, -1); \
+ }
+
+#define MRP_LUA_THROW(_err, _ret, _L, _fmt, ...) do { \
+ ssize_t _n; \
+ \
+ _n = snprintf((_err)->err, (_err)->len, _fmt, __VA_ARGS__); \
+ if (_n >= (_err)->len) \
+ (_err)->len = 0; \
+ else \
+ (_err)->len -= (size_t)n; \
+ \
+ longjmp(&(_err)->jmp, _ret); \
+ } while (0)
+
+
+/** Macro to declare an error buffer/error-path cleanup chain. */
+#define MRP_LUA_TRAPCHAIN(_err) \
+ mrp_lua_errbuf_t _err = { \
+ .traps = { .prev = &_err.traps, .next = &_err.traps }, \
+ .buf = { '\0' }, \
+ .len = 0, \
+ .error = 0, \
+ }
+
+/** Macro to push a new item to the cleanup chain. */
+#define mrp_lua_push_trap(_err, _cb, _data) do { \
+ mrp_lua_trap_t *_trap = mrp_allocz(sizeof(*_trap)); \
+ \
+ mrp_clear(_trap); \
+ mrp_list_init(&_trap->hook); \
+ _trap->cb = _cb; \
+ _trap->data = _data; \
+ _trap->name = #_data; \
+ mrp_list_append(&(_err)->traps, &_trap->hook); \
+ } while (0)
+
+/** Macro to run trap handlers then execute (usually return) back up. */
+#define mrp_lua_trigger_traps(_err, _guardptr, _L, _retval) do { \
+ void *_guard = (void *)_guardptr; \
+ mrp_list_hook_t *_p, *_n; \
+ mrp_lua_trap_t *_t; \
+ \
+ if (_guard == NULL) \
+ _guard = (void *)_err; \
+ \
+ mrp_list_foreach(&(_err)->traps, _p, _n) { \
+ if ((void *)_p > _guard) \
+ break; \
+ \
+ _t = mrp_list_entry(_p, typeof(*_t), hook); \
+ \
+ mrp_list_delete(_p); \
+ mrp_debug_at(__FILE__, __LINE__, "mrp_lua_run_traps", \
+ "Running trap '%s'...", _t->name); \
+ if (_t->cb(_L, _t->data) < 0) \
+ mrp_log_error("Uh-oh... fasten your seatbelts and prepare " \
+ "for crash. Trap handler reported failure."); \
+ mrp_free(_p); \
+ } \
+ \
+ return _retval; \
+ } while (0)
+
+/** Macro to purge trap handlers up to a given guard. */
+#define mrp_lua_cancel_traps(_err, _guardptr) do { \
+ void *_guard = (void *)_guardptr; \
+ mrp_list_hook_t *_p, *_n; \
+ mrp_lua_trap_t *_t; \
+ \
+ if (_guard == NULL) \
+ _guard = (void *)_err; \
+ \
+ mrp_list_foreach_back(&(_err)->traps, _p, _n) { \
+ if ((void *)_p > _guard) \
+ break; \
+ \
+ _t = mrp_list_entry(_p, typeof(*_t), hook); \
+ \
+ mrp_list_delete(_p); \
+ mrp_debug_at(__FILE__, __LINE__, "mrp_lua_cancel_traps", \
+ "Cancelling trap '%s'...", _t->name); \
+ mrp_free(_p); \
+ } \
+ } while (0)
+
+
+/*
+ * debugging
+ */
+
+/** Workhorse macro to produce stack dumps */
+#define __mrp_lua_stackdump(_fl, _ln, _fn, _L, _fmt, _args...) do { \
+ int _i, _depth, _t; \
+ \
+ if (_fmt && *_fmt) \
+ mrp_debug_at(_fl, _ln, _fn, _fmt, ## _args); \
+ \
+ if ((_depth = lua_gettop(_L)) > 0) \
+ mrp_debug_at(_fl, _ln, _fn, " Lua stack (depth: %d)", _depth); \
+ else \
+ mrp_debug_at(_fl, _ln, _fn, " Lua stack: empty"); \
+ \
+ for (_i = -1; _i > -1 - _depth; _i--) { \
+ switch ((_t = lua_type(_L, _i))) { \
+ case LUA_TSTRING: \
+ mrp_debug_at(_fl, _ln, _fn, " [#%d:'%s']", _i, \
+ lua_tostring(_L, _i)); \
+ break; \
+ case LUA_TNUMBER: \
+ if (((int)lua_tointeger(_L,_i) == (double)lua_tonumber(_L,_i))) \
+ mrp_debug_at(_fl, _ln, _fn, " [#%d: %d]", _i, \
+ (int)lua_tointeger(_L, _i)); \
+ else \
+ mrp_debug_at(_fl, _ln, _fn, " [#%d: %f]", _i, \
+ (double)lua_tonumber(_L, _i)); \
+ break; \
+ case LUA_TTABLE: \
+ mrp_debug_at(_fl, _ln, _fn, " [#%d: {%p}]", _i, \
+ lua_topointer(_L, _i)); \
+ break; \
+ default: \
+ mrp_debug_at(_fl, _ln, _fn, " [#%d: %s]", _i, \
+ lua_typename(_L, _t)); \
+ } \
+ } \
+} while (0)
+
+
+/** Macro to produce a simple debug dump of the Lua stack. */
+#define mrp_lua_stackdump(L, fmt, args...) \
+ __mrp_lua_stackdump(__FILE__, __LINE__, __FUNCTION__, L, fmt, ## args)
+
+
+#endif /* __MURPHY_LUA_ERROR_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+
+#include <murphy/core/lua-utils/error.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/lua-utils.h>
+
+#define FUNCBRIDGE_METATABLE "LuaBook.funcbridge"
+#define FUNCBRIDGE_USERDATA_METATABLE "LuaBook.funcbridge.userdata"
+
+#define FUNCARRAY_METATABLE "LuaBook.funcarray"
+#define FUNCARRAY_USERDATA_METATABLE "LuaBook.funcarray.userdata"
+
+static mrp_funcbridge_t *create_funcbridge(lua_State *, int, int);
+static mrp_funcbridge_t *check_funcbridge(lua_State *, int);
+static int call_funcbridge_from_lua(lua_State *);
+static int get_funcbridge_field(lua_State *);
+static int set_funcbridge_field(lua_State *);
+static int funcbridge_destructor(lua_State *);
+
+static int call_funcarray_from_lua(lua_State *);
+static int get_funcarray_field(lua_State *);
+static int set_funcarray_field(lua_State *);
+static mrp_funcarray_t *to_funcarray(lua_State *, int);
+static int funcarray_destructor(lua_State *);
+
+static int make_lua_call(lua_State *, mrp_funcbridge_t *, int);
+
+
+void mrp_create_funcbridge_class(lua_State *L)
+{
+ static const struct luaL_reg class_methods [] = {
+ { NULL, NULL }
+ };
+
+ static const struct luaL_reg override_methods [] = {
+ { "__call" , call_funcbridge_from_lua },
+ { "__index" , get_funcbridge_field },
+ { "__newindex", set_funcbridge_field },
+ { NULL , NULL }
+ };
+
+ luaL_newmetatable(L, FUNCBRIDGE_USERDATA_METATABLE);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ lua_pushcfunction(L, funcbridge_destructor);
+ lua_setfield(L, -2, "__gc");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, FUNCBRIDGE_METATABLE);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, override_methods, 0);
+ lua_pop(L, 1);
+
+ luaL_openlib(L, "builtin.method", class_methods, 0);
+ lua_pushvalue(L, -2);
+ lua_setmetatable(L, -2);
+ lua_pop(L, 1);
+}
+
+
+void mrp_create_funcarray_class(lua_State *L)
+{
+ static const struct luaL_reg override_methods [] = {
+ { "__call" , call_funcarray_from_lua },
+ { "__index" , get_funcarray_field },
+ { "__newindex", set_funcarray_field },
+ { NULL , NULL }
+ };
+
+ luaL_newmetatable(L, FUNCARRAY_USERDATA_METATABLE);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ lua_pushcfunction(L, funcarray_destructor);
+ lua_setfield(L, -2, "__gc");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, FUNCARRAY_METATABLE);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, override_methods, 0);
+ lua_pop(L, 1);
+}
+
+
+int parse_signature(const char *signature, char **sigp, mrp_lua_type_t **typep)
+{
+ char type[256];
+ char *sigs;
+ mrp_lua_type_t *types, t;
+ int ntype;
+ int len;
+ size_t l;
+
+ *sigp = sigs = NULL;
+ *typep = types = NULL;
+ ntype = 0;
+
+ if (signature == NULL)
+ return 0;
+
+ if ((len = strlen(signature)) == 0)
+ return 0;
+
+ if ((sigs = mrp_allocz(len + 1)) == NULL)
+ return -1;
+
+ if ((types = mrp_allocz_array(typeof(*types), len + 1)) == NULL) {
+ mrp_free(sigs);
+ return -1;
+ }
+
+ *sigp = sigs;
+ *typep = types;
+
+ while (*signature) {
+ switch (*signature) {
+ case MRP_FUNCBRIDGE_STRING:
+ case MRP_FUNCBRIDGE_INTEGER:
+ case MRP_FUNCBRIDGE_FLOATING:
+ case MRP_FUNCBRIDGE_BOOLEAN:
+ case MRP_FUNCBRIDGE_POINTER:
+ case MRP_FUNCBRIDGE_OBJECT:
+ *sigs++ = *signature++;
+ break;
+
+ case MRP_FUNCBRIDGE_ARRAY:
+ *sigs++ = *signature;
+
+ if (!signature[1] || signature[2] != MRP_FUNCBRIDGE_ARRAY_END) {
+ mrp_log_error("Invalid array reference in signature at '%s'.",
+ signature);
+ errno = EINVAL;
+ goto fail;
+ }
+
+ switch (signature[1]) {
+ case MRP_FUNCBRIDGE_STRING:
+ types[ntype++] = MRP_LUA_STRING_ARRAY;
+ break;
+ case MRP_FUNCBRIDGE_INTEGER:
+ types[ntype++] = MRP_LUA_INTEGER_ARRAY;
+ break;
+ case MRP_FUNCBRIDGE_FLOATING:
+ types[ntype++] = MRP_LUA_DOUBLE_ARRAY;
+ break;
+ case MRP_FUNCBRIDGE_BOOLEAN:
+ types[ntype++] = MRP_LUA_BOOLEAN_ARRAY;
+ break;
+ case MRP_FUNCBRIDGE_ANY:
+ types[ntype++] = MRP_LUA_ANY;
+ break;
+ default:
+ mrp_log_error("Invalid array type in signature at '%s'.",
+ signature);
+ errno = EINVAL;
+ goto fail;
+ }
+
+ signature += 3;
+ break;
+
+ case MRP_FUNCBRIDGE_MRPLUATYPE:
+ *sigs++ = *signature;
+
+ if (signature[1] != '(' || !signature[2]) {
+ mrp_log_error("Invalid object signagure at '%s'.", signature);
+ errno = EINVAL;
+ goto fail;
+ }
+
+ signature += 2;
+
+ l = 0;
+ while (*signature && *signature != ')' && l < sizeof(type) - 1) {
+ type[l++] = *signature++;
+ }
+
+ if (*signature != ')' || l >= sizeof(type) - 1) {
+ errno = E2BIG;
+ goto fail;
+ }
+ type[l] = '\0';
+
+ if ((t = mrp_lua_class_type(type)) == MRP_LUA_NONE) {
+ if (type[0] == MRP_FUNCBRIDGE_ANY && type[1] == '\0')
+ t = MRP_LUA_ANY;
+ }
+
+ if (t == MRP_LUA_NONE) {
+ mrp_log_error("Function bridge signature references "
+ "unknown type '%s'.", type);
+ errno = EINVAL;
+ goto fail;
+ }
+
+ types[ntype++] = t;
+ signature++;
+ break;
+
+ default:
+ mrp_log_error("Invalid type in signature at '%s'.", signature);
+ errno = EINVAL;
+ goto fail;
+ }
+ }
+
+ types[ntype++] = MRP_LUA_NONE;
+ *sigs = '\0';
+
+ mrp_realloc(types, sizeof(*types) * ntype);
+
+ return 0;
+
+ fail:
+ mrp_free(*sigp);
+ mrp_free(*typep);
+
+ *sigp = NULL;
+ *typep = NULL;
+
+ return -1;
+}
+
+
+
+mrp_funcbridge_t *mrp_funcbridge_create_cfunc(lua_State *L, const char *name,
+ const char *signature,
+ mrp_funcbridge_cfunc_t func,
+ void *data)
+{
+ int builtin, top;
+ mrp_funcbridge_t *fb;
+
+ top = lua_gettop(L);
+
+ if (mrp_lua_findtable(L, MRP_LUA_GLOBALTABLE, "builtin.method", 20))
+ fb = NULL;
+ else {
+ builtin = lua_gettop(L);
+
+ fb = create_funcbridge(L, 0, 1);
+
+ fb->type = MRP_C_FUNCTION;
+ if (parse_signature(signature, &fb->c.signature, &fb->c.sigtypes) < 0) {
+ mrp_log_error("Failed to parse signature '%s'.", signature);
+ fb->c.signature = mrp_strdup(signature);
+ }
+ else
+ mrp_debug("signature '%s' parsed into '%s'", signature,
+ fb->c.signature);
+
+ fb->c.func = func;
+ fb->c.data = data;
+
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+
+ lua_rawset(L, builtin);
+
+ lua_settop(L, top);
+ }
+
+ return fb;
+}
+
+
+mrp_funcbridge_t *mrp_funcbridge_create_luafunc(lua_State *L, int f)
+{
+ mrp_funcbridge_t *fb;
+
+ if (f < 0 && f > LUA_REGISTRYINDEX)
+ f = lua_gettop(L) + f + 1;
+
+ switch (lua_type(L, f)) {
+
+ case LUA_TTABLE:
+ fb = check_funcbridge(L, f);
+ break;
+
+ case LUA_TFUNCTION:
+ fb = create_funcbridge(L, 1, 1);
+ lua_pushvalue(L, f);
+ lua_rawseti(L, -2, 1);
+ lua_pop(L, 1);
+ fb->type = MRP_LUA_FUNCTION;
+ break;
+
+ default:
+ luaL_argcheck(L, false, f < 0 ? lua_gettop(L) + f + 1 : f,
+ "'builtin.method.xxx' or lua function expected");
+ fb = NULL;
+ break;
+ }
+
+ return fb;
+}
+
+
+mrp_funcbridge_t *mrp_funcbridge_ref(lua_State *L, mrp_funcbridge_t *fb)
+{
+ if (fb->dead)
+ return NULL;
+
+ MRP_UNUSED(L);
+
+ fb->refcnt++;
+
+ return fb;
+}
+
+void mrp_funcbridge_unref(lua_State *L, mrp_funcbridge_t *fb)
+{
+ if (fb == NULL)
+ return;
+
+ if (fb->refcnt > 1)
+ fb->refcnt--;
+ else {
+ free((void *)fb->c.signature);
+ fb->c.signature = NULL;
+
+ if (fb->luatbl) {
+ luaL_unref(L, LUA_REGISTRYINDEX, fb->luatbl);
+ fb->luatbl = 0;
+ }
+ }
+}
+
+bool mrp_funcbridge_call_from_c(lua_State *L,
+ mrp_funcbridge_t *fb,
+ const char *signature,
+ mrp_funcbridge_value_t *args,
+ char *ret_type,
+ mrp_funcbridge_value_t *ret_value)
+{
+ char t;
+ int i;
+ int sp;
+ mrp_funcbridge_value_t *a;
+ int sts;
+ bool success;
+
+ if (!fb)
+ success = false;
+ else {
+ mrp_lua_checkstack(L, -1);
+
+ switch (fb->type) {
+
+ case MRP_C_FUNCTION:
+ if (!strcmp(signature, fb->c.signature))
+ success = fb->c.func(L, fb->c.data, signature, args, ret_type,
+ ret_value);
+ else {
+ char errmsg[256];
+
+ snprintf(errmsg, sizeof(errmsg),
+ "mismatching signature @ C invocation ('%s' != '%s')",
+ signature, fb->c.signature);
+
+ *ret_type = MRP_FUNCBRIDGE_STRING;
+ ret_value->string = mrp_strdup(errmsg);
+ success = false;
+ }
+ break;
+
+ case MRP_LUA_FUNCTION:
+ sp = lua_gettop(L);
+ mrp_funcbridge_push(L, fb);
+ lua_rawgeti(L, -1, 1);
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ for (i = 0; (t = signature[i]); i++) {
+ a = args + i;
+ switch (t) {
+ case MRP_FUNCBRIDGE_STRING:
+ lua_pushstring(L, a->string);
+ break;
+ case MRP_FUNCBRIDGE_INTEGER:
+ lua_pushinteger(L, a->integer);
+ break;
+ case MRP_FUNCBRIDGE_FLOATING:
+ lua_pushnumber(L, a->floating);
+ break;
+ case MRP_FUNCBRIDGE_BOOLEAN:
+ lua_pushboolean(L, a->boolean);
+ break;
+ case MRP_FUNCBRIDGE_OBJECT:
+ mrp_lua_push_object(L, a->pointer);
+ break;
+ case MRP_FUNCBRIDGE_MRPLUATYPE:
+ mrp_lua_push_object(L, a->pointer);
+ break;
+ default:
+ success = false;
+ goto done;
+ }
+
+ if (!(i % 20) && i)
+ mrp_lua_checkstack(L, -1);
+ }
+
+ sts = lua_pcall(L, i, 1, 0);
+
+ MRP_ASSERT(!sts || (sts && lua_type(L, -1) == LUA_TSTRING),
+ "lua pcall did not return error string when failed");
+
+ switch (lua_type(L, -1)) {
+ case LUA_TSTRING:
+ *ret_type = MRP_FUNCBRIDGE_STRING;
+ ret_value->string = mrp_strdup(lua_tolstring(L, -1, NULL));
+ break;
+ case LUA_TNUMBER:
+ *ret_type = MRP_FUNCBRIDGE_FLOATING;
+ ret_value->floating = lua_tonumber(L, -1);
+ break;
+ case LUA_TBOOLEAN:
+ *ret_type = MRP_FUNCBRIDGE_BOOLEAN;
+ ret_value->boolean = lua_toboolean(L, -1);
+ break;
+ case LUA_TTABLE:
+ {
+ int size = 4; /* array size (initial) */
+ int item_size = 0; /* array item size (calculated) */
+ void *items = NULL;
+ int allowed_type = LUA_TNIL;
+ bool first = true;
+ int j = 0;
+
+ *ret_type = MRP_FUNCBRIDGE_ARRAY;
+
+ /* push NIL to stack as the first key */
+ lua_pushnil(L);
+
+ while (lua_next(L, -2)) {
+ if (first == true) {
+ first = false;
+ allowed_type = lua_type(L, -1);
+ switch (allowed_type) {
+ case LUA_TNUMBER:
+ ret_value->array.type = MRP_FUNCBRIDGE_FLOATING;
+ item_size = sizeof(lua_Number);
+ break;
+ case LUA_TBOOLEAN:
+ ret_value->array.type = MRP_FUNCBRIDGE_BOOLEAN;
+ item_size = sizeof(int);
+ break;
+ case LUA_TSTRING:
+ ret_value->array.type = MRP_FUNCBRIDGE_STRING;
+ item_size = sizeof(char *);
+ break;
+ default:
+ goto error;
+ }
+ items = mrp_allocz(size * item_size);
+ if (!items) {
+ goto error;
+ }
+ }
+ else {
+ /* check that all members of the table are of the same
+ * type */
+ if (lua_type(L, -1) != allowed_type) {
+ goto error;
+ }
+ }
+
+ if (size == j+1) {
+ /* check size */
+ size *= 2;
+ items = mrp_realloc(items, size * item_size);
+ if (!items) {
+ goto error;
+ }
+ }
+
+ switch (allowed_type) {
+ case LUA_TNUMBER:
+ {
+ lua_Number *arr = (lua_Number *) items;
+ arr[j] = lua_tonumber(L, -1);
+ break;
+ }
+ case LUA_TBOOLEAN:
+ {
+ int *arr = (int *) items;
+ arr[j] = lua_toboolean(L, -1);
+ break;
+ }
+ case LUA_TSTRING:
+ {
+ char **arr = (char **) items;
+ char *value = mrp_strdup(lua_tostring(L, -1));
+
+ if (!value) {
+ goto error;
+ }
+
+ arr[j] = value;
+ break;
+ }
+ default:
+ /* impossible */
+ break;
+ }
+
+ j++;
+
+ /* remove the value, keep key */
+ lua_pop(L, 1);
+ }
+
+ ret_value->array.nitem = j;
+ ret_value->array.items = items;
+
+ break;
+ error:
+ if (allowed_type == LUA_TSTRING) {
+ /* we need to free possibly allocated strings */
+ int k;
+ char **arr = items;
+
+ if (items) {
+ for (k = 0; k < j; k++) {
+ mrp_free(arr[k]);
+ }
+ }
+ }
+
+ mrp_free(items);
+
+ *ret_type = MRP_FUNCBRIDGE_NO_DATA;
+ memset(ret_value, 0, sizeof(*ret_value));
+ sts = 1; /* success is later calculated from !sts */
+ mrp_log_error("funcbridge: error reading array from Lua");
+ break;
+ }
+
+ default:
+ *ret_type = MRP_FUNCBRIDGE_NO_DATA;
+ memset(ret_value, 0, sizeof(*ret_value));
+ break;
+ }
+ success = !sts;
+ done:
+ lua_settop(L, sp);
+ break;
+
+ default:
+ success = false;
+ break;
+ }
+ }
+
+ return success;
+}
+
+
+int mrp_funcbridge_push(lua_State *L, mrp_funcbridge_t *fb)
+{
+ if (!fb)
+ lua_pushnil(L);
+ else
+ lua_rawgeti(L, LUA_REGISTRYINDEX, fb->luatbl);
+ return 1;
+}
+
+
+mrp_funcarray_t *mrp_funcarray_create(lua_State *L)
+{
+ int table;
+ mrp_funcarray_t *fa;
+
+ lua_createtable(L, 0, 1);
+ table = lua_gettop(L);
+
+ luaL_getmetatable(L, FUNCARRAY_METATABLE);
+ lua_setmetatable(L, table);
+
+ lua_pushliteral(L, "userdata");
+
+ fa = (mrp_funcarray_t *)lua_newuserdata(L,sizeof(mrp_funcarray_t));
+ memset(fa, 0, sizeof(mrp_funcarray_t));
+
+ luaL_getmetatable(L, FUNCARRAY_USERDATA_METATABLE);
+ lua_setmetatable(L, -2);
+
+ lua_rawset(L, table);
+
+ return fa;
+}
+
+
+
+bool mrp_funcarray_call_from_c(lua_State *L,
+ mrp_funcarray_t *fa,
+ const char *signature,
+ mrp_funcbridge_value_t *args)
+{
+ size_t i;
+ bool success, ok;
+ char rtyp;
+ mrp_funcbridge_value_t rval;
+
+ if (!fa || (fa->nfunc > 0 && !fa->funcs))
+ success = false;
+ else {
+ success = true;
+
+ for (i = 0; i < fa->nfunc; i++) {
+ ok = mrp_funcbridge_call_from_c(L, fa->funcs[i], signature, args,
+ &rtyp, &rval);
+ if (!ok || rtyp != MRP_FUNCBRIDGE_BOOLEAN || !rval.boolean)
+ success = false;
+ }
+ }
+
+ return success;
+}
+
+mrp_funcarray_t *mrp_funcarray_check(lua_State *L, int t)
+{
+ mrp_funcarray_t *fa;
+ size_t i, len;
+
+ if (t < 0 && t > LUA_REGISTRYINDEX)
+ t = lua_gettop(L) + t + 1;
+
+ switch (lua_type(L, t)) {
+
+ case LUA_TFUNCTION:
+ fa = mrp_funcarray_create(L);
+
+ fa->funcs = calloc(1, sizeof(mrp_funcbridge_t *));
+ if (!fa->funcs)
+ return NULL;
+
+ fa->nfunc = 1;
+ fa->funcs[0] = mrp_funcbridge_create_luafunc(L, t);
+
+ break;
+
+ case LUA_TTABLE:
+ if (!(fa = to_funcarray(L, t))) {
+ fa = mrp_funcarray_create(L);
+
+ len = luaL_getn(L, t);
+
+ fa->funcs = calloc(len, sizeof(mrp_funcbridge_t *));
+ fa->nfunc = len;
+
+ for (i = 0; i < len; i++) {
+ lua_pushnumber(L, (int)(i + 1));
+ lua_gettable(L, t);
+
+ fa->funcs[i] = mrp_funcbridge_create_luafunc(L, -1);
+
+ lua_pop(L, 1);
+ }
+
+ lua_replace(L, t);
+ }
+ break;
+
+ default:
+ luaL_typerror(L, t, "function array");
+ fa = NULL;
+ break;
+ }
+
+ return fa;
+}
+
+
+static mrp_funcbridge_t *create_funcbridge(lua_State *L, int narr, int nrec)
+{
+ int table;
+ mrp_funcbridge_t *fb;
+
+ lua_createtable(L, narr, nrec);
+ table = lua_gettop(L);
+
+ luaL_getmetatable(L, FUNCBRIDGE_METATABLE);
+ lua_setmetatable(L, table);
+
+ lua_pushliteral(L, "userdata");
+
+ fb = (mrp_funcbridge_t *)lua_newuserdata(L,sizeof(mrp_funcbridge_t));
+ memset(fb, 0, sizeof(mrp_funcbridge_t));
+
+ luaL_getmetatable(L, FUNCBRIDGE_USERDATA_METATABLE);
+ lua_setmetatable(L, -2);
+
+ lua_rawset(L, table);
+
+ lua_pushvalue(L, -1);
+ fb->luatbl = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ fb->refcnt = 1;
+
+ return fb;
+}
+
+
+static mrp_funcbridge_t *check_funcbridge(lua_State *L, int t)
+{
+ mrp_funcbridge_t *fb;
+
+ luaL_checktype(L, t, LUA_TTABLE);
+
+ lua_pushvalue(L, t);
+ lua_pushliteral(L, "userdata");
+ lua_rawget(L, -2);
+
+ fb = luaL_checkudata(L, -1, FUNCBRIDGE_USERDATA_METATABLE);
+ luaL_argcheck(L, fb != NULL, 1, "'function bridge' expected");
+
+ lua_pop(L, 2);
+
+ return fb;
+}
+
+static int call_funcbridge_from_lua(lua_State *L)
+{
+ mrp_funcbridge_t *fb = check_funcbridge(L, 1);
+
+ return make_lua_call(L, fb, 1);
+}
+
+static int get_funcbridge_field(lua_State *L)
+{
+ lua_pushnil(L);
+ return 1;
+}
+
+static int set_funcbridge_field(lua_State *L)
+{
+ return luaL_error(L, "attempt to write a readonly object");
+}
+
+static int funcbridge_destructor(lua_State *L)
+{
+ mrp_funcbridge_t *fb;
+
+ fb = luaL_checkudata(L, -1, FUNCBRIDGE_USERDATA_METATABLE);
+
+ if (!fb->dead) {
+ fb->dead = true;
+ mrp_funcbridge_unref(L, fb);
+ }
+
+ return 0;
+}
+
+static int call_funcarray_from_lua(lua_State *L)
+{
+ mrp_funcarray_t *fa;
+ int narg, top;
+ size_t i;
+ int j;
+ int success;
+
+ top = lua_gettop(L);
+ narg = top - 1;
+
+ if (!(fa = to_funcarray(L, 1)))
+ luaL_typerror(L, 1, "function array");
+
+ if (fa->nfunc > 0 && !fa->funcs)
+ luaL_error(L, "attempt to call a corruptfunction array");
+
+ success = true;
+
+ for (i = 0; i < fa->nfunc; i++) {
+ mrp_funcbridge_push(L, fa->funcs[i]);
+
+ for (j = 0; j < narg; j++)
+ lua_pushvalue(L, j+2);
+
+ make_lua_call(L, fa->funcs[i], top+1);
+
+ if (!lua_isboolean(L, -1) || !lua_toboolean(L, -1))
+ success = false;
+
+ lua_settop(L, top);
+ }
+
+ lua_pushboolean(L, success);
+ lua_replace(L, 1);
+
+ lua_settop(L, 1);
+
+ return 1;
+}
+
+static int get_funcarray_field(lua_State *L)
+{
+ lua_pushnil(L);
+ return 1;
+}
+
+static int set_funcarray_field(lua_State *L)
+{
+ luaL_error(L, "attempt to change a function array");
+ return 0;
+}
+
+static mrp_funcarray_t *to_funcarray(lua_State *L, int t)
+{
+ mrp_funcarray_t *fa = NULL;
+
+ t = (t < 0) ? lua_gettop(L) + t + 1 : t;
+
+ if (t < 0 && t > LUA_REGISTRYINDEX)
+ t = lua_gettop(L) + t + 1;
+
+ if (lua_istable(L, t)) {
+ lua_pushstring(L, "userdata");
+ lua_rawget(L, t);
+
+ if (!lua_isnil(L, -1))
+ fa = luaL_checkudata(L, -1, FUNCARRAY_USERDATA_METATABLE);
+
+ lua_pop(L, 1);
+ }
+
+ return fa;
+}
+
+static int funcarray_destructor(lua_State *L)
+{
+ mrp_funcarray_t *fa;
+ size_t i;
+
+ fa = luaL_checkudata(L, -1, FUNCARRAY_USERDATA_METATABLE);
+
+ if (fa->funcs) {
+ for (i = 0; i < fa->nfunc; i++)
+ mrp_funcbridge_unref(L, fa->funcs[i]);
+
+ free(fa->funcs);
+ }
+
+ memset(fa, 0, sizeof(mrp_funcarray_t));
+
+ return 0;
+}
+
+
+static inline int funcbridge_type(mrp_lua_type_t type)
+{
+#define MAP(_t, _fbt) case MRP_LUA_##_t: return MRP_FUNCBRIDGE_##_fbt;
+ switch (type) {
+ MAP(STRING , STRING);
+ MAP(INTEGER, INTEGER);
+ MAP(DOUBLE , FLOATING);
+ MAP(BOOLEAN, BOOLEAN);
+ MAP(OBJECT , OBJECT);
+ MAP(STRING_ARRAY , ARRAY);
+ MAP(INTEGER_ARRAY, ARRAY);
+ MAP(DOUBLE_ARRAY , ARRAY);
+ MAP(BOOLEAN_ARRAY, ARRAY);
+ default:
+ return MRP_FUNCBRIDGE_UNSUPPORTED;
+ }
+}
+
+
+static inline int funcbridge_elemtype(mrp_lua_type_t type)
+{
+ switch (type) {
+ MAP(STRING_ARRAY , STRING);
+ MAP(INTEGER_ARRAY, INTEGER);
+ MAP(DOUBLE_ARRAY , FLOATING);
+ MAP(BOOLEAN_ARRAY, BOOLEAN);
+ default:
+ return MRP_FUNCBRIDGE_UNSUPPORTED;
+ }
+}
+
+
+static int autobridge_patch(lua_State *L, void *object, int npop, int *refs)
+{
+ int i;
+
+ /* remove nref elements from the bottom, save references to them */
+ for (i = 1; i <= npop; i++) {
+ lua_pushvalue(L, 1);
+ lua_remove(L, 1);
+ }
+
+ for (i = -npop; i <= -1; i++) {
+ refs[npop+i] = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+
+ if (object && mrp_lua_check_object(L, NULL, 1) != object) {
+ mrp_log_error("wrong stack detected before calling autobridge");
+#if 0
+ /*
+ * Hmm... on a second though, let's not count on that only
+ * the self/object argument is missing for the autobridge
+ * method call. Who knows what else is wrong with the stack...
+ */
+ if (object != NULL) {
+ mrp_lua_push_object(L, object);
+ lua_insert(L, 1);
+ }
+#endif
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void autobridge_restore(lua_State *L, void *object, int npop, int *refs)
+{
+ int i;
+
+ MRP_UNUSED(object);
+
+ lua_settop(L, 0);
+
+ for (i = 1; i <= npop; i++) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, refs[i-1]);
+ luaL_unref(L, LUA_REGISTRYINDEX, refs[i-1]);
+ lua_insert(L, 1);
+ }
+}
+
+
+static int make_lua_call(lua_State *L, mrp_funcbridge_t *fb, int f)
+{
+#define ARG_MAX 256
+#define ARRAY_MAX 256
+
+ int ret;
+ int i, n, m, b, e, tidx;
+ const char *s;
+ char t;
+ mrp_funcbridge_value_t args[ARG_MAX];
+ mrp_funcbridge_value_t *a, r;
+ mrp_lua_type_t type;
+ int elem;
+ size_t tlen;
+ int status;
+ int refs[3] = { LUA_NOREF, LUA_NOREF, LUA_NOREF };
+
+ e = lua_gettop(L);
+ f = (f < 0) ? e + f + 1 : f;
+ b = f + 1 + (fb->autobridge ? 1 : 0);
+ n = e - b + 1;
+
+ mrp_debug("fn:%d, beg:%d, end:%d, num:%d", f, b, e, n);
+
+ switch (fb->type) {
+
+ case MRP_C_FUNCTION:
+ m = strlen(fb->c.signature);
+
+ if (n >= ARG_MAX - 1 || n > m)
+ return luaL_error(L, "too many arguments (%d > %d)", n, m);
+ if (n < m)
+ return luaL_error(L, "too few arguments (%d < %d)", n, m);
+
+ tidx = 0;
+ for (i = b, s = fb->c.signature, a= args; i <= e; i++, s++, a++){
+ switch (*s) {
+ case MRP_FUNCBRIDGE_STRING:
+ a->string = luaL_checklstring(L, i, NULL);
+ break;
+ case MRP_FUNCBRIDGE_INTEGER:
+ a->integer = luaL_checkinteger(L, i);
+ break;
+ case MRP_FUNCBRIDGE_FLOATING:
+ a->floating = luaL_checknumber(L, i);
+ break;
+ case MRP_FUNCBRIDGE_OBJECT:
+ a->pointer = mrp_lua_check_object(L, NULL, i);
+ break;
+ case MRP_FUNCBRIDGE_BOOLEAN:
+ a->boolean = lua_toboolean(L, i);
+ break;
+ case MRP_FUNCBRIDGE_ARRAY:
+ if (fb->c.sigtypes == NULL ||
+ (type = fb->c.sigtypes[tidx++]) == MRP_LUA_NONE) {
+ invalid_array_type:
+ return luaL_error(L, "type info missing for array or "
+ "array argument %d", (i - b + 1));
+ }
+
+ switch (type) {
+ case MRP_LUA_STRING_ARRAY:
+ tlen = sizeof(char *);
+ elem = MRP_FUNCBRIDGE_STRING;
+ break;
+ case MRP_LUA_INTEGER_ARRAY:
+ tlen = sizeof(int32_t);
+ elem = MRP_FUNCBRIDGE_INTEGER;
+ break;
+ case MRP_LUA_DOUBLE_ARRAY:
+ tlen = sizeof(double );
+ elem = MRP_FUNCBRIDGE_DOUBLE;
+ break;
+ case MRP_LUA_BOOLEAN_ARRAY:
+ tlen = sizeof(bool );
+ elem = MRP_FUNCBRIDGE_BOOLEAN;
+ break;
+ case MRP_LUA_ANY:
+ tlen = sizeof(double);
+ elem = MRP_FUNCBRIDGE_ANY;
+ break;
+ default: goto invalid_array_type;
+ }
+
+ a->array.items = alloca(tlen * ARRAY_MAX);
+ a->array.nitem = ARRAY_MAX;
+
+ if (mrp_lua_object_collect_array(L, i, &a->array.items,
+ &a->array.nitem, &type,
+ false, NULL, 0) < 0) {
+ return luaL_error(L, "failed to collect array");
+ }
+
+ if (elem == MRP_FUNCBRIDGE_ANY) {
+ elem = funcbridge_elemtype(type);
+ if (elem == MRP_FUNCBRIDGE_UNSUPPORTED)
+ goto invalid_array_type;
+ }
+ a->array.type = elem;
+ break;
+
+ default:
+ return luaL_error(L, "argument %d has unsupported type '%c'",
+ (i - b) + 1, i);
+ }
+ }
+ memset(a, 0, sizeof(*a));
+
+ if (fb->autobridge && fb->usestack) {
+ mrp_debug("patching stack for autobridge %p", fb->c.func);
+
+ if (autobridge_patch(L, fb->c.data, 1, refs) < 0) {
+ autobridge_restore(L, fb->c.data, 1, refs);
+ return luaL_error(L, "incorrect stack to call autobridge %p",
+ fb->c.func);
+ }
+ }
+
+ status = fb->c.func(L, fb->c.data, fb->c.signature, args, &t, &r);
+
+ if (fb->autobridge && fb->usestack) {
+ mrp_debug("restoring stack after autobridge call");
+
+ autobridge_restore(L, fb->c.data, 1, refs);
+ }
+
+ if (!status)
+ return luaL_error(L, "c function invocation failed");
+
+ switch (t) {
+ case MRP_FUNCBRIDGE_NO_DATA:
+ ret = 0;
+ break;
+ case MRP_FUNCBRIDGE_STRING:
+ ret = 1;
+ lua_pushstring(L, r.string);
+ break;
+ case MRP_FUNCBRIDGE_INTEGER:
+ ret = 1;
+ lua_pushinteger(L, r.integer);
+ break;
+ case MRP_FUNCBRIDGE_FLOATING:
+ ret = 1;
+ lua_pushnumber(L, r.floating);
+ break;
+ default:
+ ret = 0;
+ lua_pushnil(L);
+ }
+ break;
+
+ case MRP_LUA_FUNCTION:
+ lua_rawgeti(L, f, 1);
+ luaL_checktype(L, -1, LUA_TFUNCTION);
+ lua_replace(L, f);
+ lua_pcall(L, n, 1, 0);
+ ret = 1;
+ break;
+
+ default:
+ return luaL_error(L, "internal error");
+ }
+
+ return ret;
+
+#undef ARG_MAX
+#undef ARRAY_MAX
+}
+
+
+int mrp_call_funcbridge(lua_State *L, mrp_funcbridge_t *fb, int f)
+{
+ return make_lua_call(L, fb, f);
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_FUNCBRIDGE_H__
+#define __MURPHY_LUA_FUNCBRIDGE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef union mrp_funcbridge_value_u mrp_funcbridge_value_t;
+typedef enum mrp_funcbridge_type_e mrp_funcbridge_type_t;
+typedef struct mrp_funcbridge_s mrp_funcbridge_t;
+typedef struct mrp_funcarray_s mrp_funcarray_t;
+
+typedef bool (*mrp_funcbridge_cfunc_t)(lua_State *, void *,
+ const char *, mrp_funcbridge_value_t *,
+ char *, mrp_funcbridge_value_t *);
+
+#include "murphy/core/lua-utils/object.h"
+
+#define MRP_FUNCBRIDGE_NO_DATA 0
+#define MRP_FUNCBRIDGE_UNSUPPORTED '?'
+#define MRP_FUNCBRIDGE_STRING 's'
+#define MRP_FUNCBRIDGE_INTEGER 'd'
+#define MRP_FUNCBRIDGE_FLOATING 'f'
+#define MRP_FUNCBRIDGE_DOUBLE MRP_FUNCBRIDGE_FLOATING
+#define MRP_FUNCBRIDGE_BOOLEAN 'b'
+#define MRP_FUNCBRIDGE_POINTER 'p'
+#define MRP_FUNCBRIDGE_OBJECT 'o'
+#define MRP_FUNCBRIDGE_ARRAY '['
+#define MRP_FUNCBRIDGE_ARRAY_END ']'
+#define MRP_FUNCBRIDGE_MRPLUATYPE 'O'
+#define MRP_FUNCBRIDGE_ANY '*'
+enum mrp_funcbridge_type_e {
+ MRP_C_FUNCTION = 1,
+ MRP_LUA_FUNCTION
+};
+
+
+union mrp_funcbridge_value_u {
+ const char *string;
+ int32_t integer;
+ double floating;
+ bool boolean;
+ void *pointer;
+ struct {
+ void *items;
+ size_t nitem;
+ char type;
+ } array;
+};
+
+struct mrp_funcbridge_s {
+ mrp_funcbridge_type_t type;
+ struct {
+ char *signature;
+ mrp_lua_type_t *sigtypes;
+ mrp_funcbridge_cfunc_t func;
+ void *data;
+ } c;
+ int luatbl;
+ int refcnt;
+ int dead : 1;
+ int autobridge : 1; /* autobridged member */
+ int usestack : 1; /* also uses the Lua stack */
+};
+
+struct mrp_funcarray_s {
+ size_t nfunc;
+ mrp_funcbridge_t **funcs;
+ int luatbl;
+};
+
+
+void mrp_create_funcbridge_class(lua_State *);
+void mrp_create_funcarray_class(lua_State *);
+
+
+mrp_funcbridge_t *mrp_funcbridge_create_cfunc(lua_State *, const char *,
+ const char *,
+ mrp_funcbridge_cfunc_t, void *);
+mrp_funcbridge_t *mrp_funcbridge_create_luafunc(lua_State *, int);
+mrp_funcbridge_t *mrp_funcbridge_ref(lua_State *L, mrp_funcbridge_t *);
+void mrp_funcbridge_unref(lua_State *L, mrp_funcbridge_t *);
+bool mrp_funcbridge_call_from_c(lua_State *, mrp_funcbridge_t *,
+ const char *,
+ mrp_funcbridge_value_t *,
+ char *,
+ mrp_funcbridge_value_t *);
+int mrp_call_funcbridge(lua_State *L, mrp_funcbridge_t *fb, int f);
+mrp_funcbridge_t *mrp_funcbridge_check(lua_State *, int);
+int mrp_funcbridge_push(lua_State *, mrp_funcbridge_t *);
+
+mrp_funcarray_t *mrp_funcarray_create(lua_State *);
+bool mrp_funcarray_call_from_c(lua_State *, mrp_funcarray_t *,
+ const char *,
+ mrp_funcbridge_value_t *);
+mrp_funcarray_t *mrp_funcarray_check(lua_State *, int);
+
+
+
+#endif /* __MURPHY_LUA_FUNCBRIDGE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/file-utils.h>
+
+#include <murphy/core/lua-utils/include.h>
+
+
+/*
+ * tracking data for include-once files
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to list of files included */
+ dev_t dev; /* device id */
+ ino_t ino; /* file id */
+} file_t;
+
+
+static inline int once_included(mrp_list_hook_t *files, dev_t dev, ino_t ino)
+{
+ mrp_list_hook_t *p, *n;
+ file_t *f;
+
+ if (files == NULL)
+ return FALSE;
+
+ mrp_list_foreach(files, p, n) {
+ f = mrp_list_entry(p, typeof(*f), hook);
+
+ if (f->dev == dev && f->ino == ino)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int save_included(mrp_list_hook_t *files, const char *path, dev_t dev,
+ ino_t ino)
+{
+ file_t *f;
+
+ MRP_UNUSED(path);
+
+ if (files == NULL)
+ return -1;
+
+ if ((f = mrp_allocz(sizeof(*f))) == NULL)
+ return -1;
+
+ mrp_list_init(&f->hook);
+ f->dev = dev;
+ f->ino = ino;
+ mrp_list_append(files, &f->hook);
+
+ return 0;
+}
+
+
+int mrp_lua_include_file(lua_State *L, const char *file, const char **dirs,
+ mrp_list_hook_t *files)
+{
+ struct stat st;
+ char path[PATH_MAX];
+
+ if (mrp_find_file(file, dirs, R_OK, path, sizeof(path)) < 0)
+ return -1;
+
+ if (files != NULL) {
+ if (stat(path, &st) < 0)
+ return -1;
+
+ if (once_included(files, st.st_dev, st.st_ino))
+ return 0;
+ }
+
+ mrp_debug("file '%s' resolved to '%s' for inclusion", file, path);
+
+ if (!luaL_loadfile(L, path) && !lua_pcall(L, 0, 0, 0)) {
+ if (files != NULL)
+ save_included(files, path, st.st_dev, st.st_ino);
+
+ return 0;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_INCLUDE_H__
+#define __MURPHY_LUA_INCLUDE_H__
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/list.h>
+
+/** Include (read and evaluate) the given Lua file. */
+int mrp_lua_include_file(lua_State *L, const char *file, const char **dirs,
+ mrp_list_hook_t *files);
+
+/** Include the given Lua file, search the given directories if necessary. */
+#define mrp_lua_include(_L, _file, _dirs) \
+ mrp_lua_include_file(_L, _file, _dirs, NULL)
+
+/** Include the given Lua file at most once. */
+#define mrp_lua_include_once(_L, _file, _dirs, _included) \
+ mrp_lua_include_file(_L, _file, _dirs, _included)
+
+#endif /* __MURPHY_LUA_INCLUDE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <string.h>
+
+#include "murphy/common/debug.h"
+#include "murphy/common/log.h"
+#include "murphy/core/lua-utils/lua-utils.h"
+
+
+void mrp_lua_setglobal(lua_State *L, const char *name)
+{
+#if LUA_VERSION_NUM >= 502
+ lua_setglobal(L, name);
+#else
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_insert(L, -2);
+ lua_setfield(L, -2, name);
+ lua_pop(L, 1);
+#endif
+}
+
+
+void mrp_lua_setglobal_idx(lua_State *L, int idx)
+{
+ if (lua_isstring(L, idx)) {
+ lua_pushvalue(L, idx);
+ mrp_lua_setglobal(L, lua_tostring(L, -1));
+ }
+}
+
+
+void mrp_lua_getglobal(lua_State *L, const char *name)
+{
+#if LUA_VERSION_NUM >= 502
+ lua_getglobal(L, name);
+#else
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+ lua_getfield(L, -1, name);
+ lua_remove(L, -2);
+#endif
+}
+
+
+void mrp_lua_getglobal_idx(lua_State *L, int idx)
+{
+ if (lua_isstring(L, idx)) {
+ lua_pushvalue(L, idx);
+ mrp_lua_getglobal(L, lua_tostring(L, -1));
+ lua_remove(L, -2);
+ }
+}
+
+
+const char *mrp_lua_findtable(lua_State *L, int t, const char *field, int size)
+{
+ const char *p, *n;
+ size_t l;
+
+ if (t != MRP_LUA_GLOBALTABLE) { /* t == 0 indicates a global */
+ if (!lua_istable(L, t))
+ return field;
+ else
+ lua_pushvalue(L, t);
+ }
+
+ for (p = field; p != NULL; p = n && *n ? n + 1: NULL) {
+ if ((n = strchr(p, '.')) != NULL)
+ l = n - p;
+ else
+ l = strlen(p);
+
+ lua_pushlstring(L, p, l);
+
+ if (!(p == field && t == MRP_LUA_GLOBALTABLE))
+ lua_rawget(L, -2);
+ else
+ mrp_lua_getglobal_idx(L, -1);
+
+ switch (lua_type(L, -1)) {
+ case LUA_TTABLE:
+ lua_remove(L, -2);
+ break;
+
+ case LUA_TNIL:
+ lua_pop(L, 1);
+ lua_createtable(L, 0, n && *n ? 1 : size);
+ lua_pushlstring(L, p, l);
+ lua_pushvalue(L, -2);
+ lua_settable(L, -4);
+ lua_remove(L, -2);
+ break;
+
+ default:
+ lua_pop(L, 2);
+ return p;
+ }
+ }
+
+ lua_remove(L, -2);
+ return NULL;
+}
+
+
+void mrp_lua_checkstack(lua_State *L, int extra)
+{
+ /*
+ * Notes:
+ *
+ * We have a systematic bug throughout our codebase. We never ever
+ * grow the Lua stack according to our needs. We simply rely on the
+ * available space to be enough. When we occasionally do run out of
+ * stack space, this causes severe memory corruption.
+ *
+ * This is relatively easy to trigger with Lua 5.1.x but much harder
+ * with Lua 5.2.x (I could not reproduce this with 5.2.x at all).
+ *
+ * This function is merely a desperate kludgish attemp to try and
+ * hide the damage caused by the bug. In a couple of commonly used
+ * functions we call this to make sure there's plenty of space in the
+ * stack and hope that it will be enough also for those who do not
+ * ensure this themselves.
+ *
+ * XXX TODO: Eventually we'll need to fix this properly.
+ */
+
+ lua_checkstack(L, extra > 0 ? extra : 40);
+}
+
+
+const char *mrp_lua_callstack(lua_State *L, char *buf, size_t size, int depth)
+{
+ int top;
+ int i, b, e;
+ const char *w;
+ char *p;
+ int n, l;
+
+ if (depth <= 0)
+ depth = 16;
+
+ top = lua_gettop(L);
+
+ p = buf;
+ l = (int)size;
+
+ *p = '\0';
+
+ b = e = -1;
+ for (i = 0; i < depth; i++) {
+ luaL_where(L, i);
+
+ if (lua_isnil(L, -1))
+ break;
+
+ w = lua_tostring(L, -1);
+
+ if (!(w && *w)) {
+ if (b < 0)
+ b = e = i;
+ else
+ e = i;
+
+ continue;
+ }
+
+ if (b >= 0 && e == i - 1) {
+ if (b != e)
+ n = snprintf(p, l, "\n [#%d-%d] ?", b, e);
+ else
+ n = snprintf(p, l, "\n [#%d] ?", b);
+ b = e = -1;
+
+ p += n;
+ l -= n;
+
+ if (l <= 0)
+ goto out;
+ }
+
+ n = snprintf(p, l, "\n [#%d] @%s", i, w);
+
+ p += n;
+ l -= n;
+
+ if (l <= 0)
+ goto out;
+ }
+
+ out:
+ lua_settop(L, top);
+
+ return buf;
+}
+
+
+void mrp_lua_calltrace(lua_State *L, int depth, bool debug)
+{
+ char buf[1024];
+
+ if (debug)
+ mrp_debug("\n%s", mrp_lua_callstack(L, buf, sizeof(buf), depth));
+ else
+ mrp_log_info("%s", mrp_lua_callstack(L, buf, sizeof(buf), depth));
+}
--- /dev/null
+/*
+ * Copyright (c) 2012-2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_UTILS_H__
+#define __MURPHY_LUA_UTILS_H__
+
+#include <stdbool.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#if LUA_VERSION_NUM >= 502
+/* this backward-compatibility macro is no longer pre-defined in Lua 5.2 */
+# define luaL_reg luaL_Reg
+
+/* the undocumented luaL_getn is no longer available in 5.2 */
+# define luaL_getn luaL_len
+
+/* this has been removed from Lua 5.2 */
+static inline int luaL_typerror (lua_State *L, int arg, const char *type) {
+ return luaL_argerror(L, arg, lua_pushfstring(L, "%s expected, got %s",
+ type, luaL_typename(L, arg)));
+}
+
+# ifndef lua_objlen
+# define lua_objlen(L, i) lua_rawlen(L, (i))
+# endif
+#endif
+
+/** Convert the given stack index to an absolute one. */
+static inline int mrp_lua_absidx(lua_State *L, int idx) {
+ return (idx >= 0 ? idx : (1 + lua_gettop(L) + idx));
+}
+
+/** Convert the given stack index to a relative one. */
+static inline int mrp_lua_relidx(lua_State *L, int idx) {
+ return (idx <= 0 ? idx : -(1 + lua_gettop(L) - idx));
+}
+
+/** Set @name to the value at the top, pops the stack. */
+void mrp_lua_setglobal(lua_State *L, const char *name);
+
+/** Set the value at the top to the name at @idx. */
+void mrp_lua_setglobal_idx(lua_State *L, int idx);
+
+/** Get the value of the global variable @name. Push nil if not found. */
+void mrp_lua_getglobal(lua_State *L, const char *name);
+
+/** Get the value of the name at @idx. Push nil if not found. */
+void mrp_lua_getglobal_idx(lua_State *L, int idx);
+
+/** Traverse table @t to find/create member @field. */
+#define MRP_LUA_GLOBALTABLE 0 /* use as t for globals */
+const char *mrp_lua_findtable(lua_State *L, int t, const char *field, int size);
+
+/** Make sure there's space for at least extra values in the stack. */
+void mrp_lua_checkstack(lua_State *L, int extra);
+
+/** Produce a Lua call stack trace of the given depth. */
+const char *mrp_lua_callstack(lua_State *L, char *buf, size_t size, int depth);
+
+/** Print a Lua call stack trace of the given depth. */
+void mrp_lua_calltrace(lua_State *L, int depth, bool debug);
+
+#endif /* __MURPHY_LUA_UTILS_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-lua-utils
+Description: Murphy policy framework, LUA support library
+Requires:
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-lua-utils @LUA_LIBS@
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/env.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/refcnt.h>
+
+#include <murphy/core/lua-bindings/murphy.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/error.h>
+
+
+#undef __MURPHY_MANGLE_CLASS_SELF__ /* extra self-mangling if defined */
+#define CHECK true /* do type/self-checking */
+#define NOCHECK (!CHECK) /* omit type/self-checking */
+
+/**
+ * Metadata we use to administer objects allocated via us.
+ */
+typedef struct {
+ void *selfish; /* verification pointer(ish) to us */
+ mrp_lua_classdef_t *def; /* class definition for this object */
+ struct {
+ int self; /* self reference for static objects */
+ int ext; /* object extensions */
+ int priv; /* private references */
+ } refs;
+ mrp_refcnt_t refcnt; /* object reference count */
+ int dead : 1; /* being cleaned up */
+ int initializing : 1; /* being initialized */
+ mrp_list_hook_t hook[0]; /* to object list if we're tracking */
+} userdata_t;
+
+
+static bool valid_id(const char *);
+static int userdata_destructor(lua_State *);
+
+static void object_create_reftbl(userdata_t *u, lua_State *L);
+static void object_delete_reftbl(userdata_t *u, lua_State *L);
+static void object_create_exttbl(userdata_t *u, lua_State *L);
+static void object_delete_exttbl(userdata_t *u, lua_State *L);
+static int override_setfield(lua_State *L);
+static int override_getfield(lua_State *L);
+static int override_tostring(lua_State *L);
+static int object_setup_bridges(userdata_t *u, lua_State *L);
+
+static void invalid_destructor(void *data);
+static inline int is_native(userdata_t *u, const char *name);
+
+/**
+ * A static non-NULL class definition we return for failed lookups.
+ */
+static mrp_lua_classdef_t invalid_classdef = {
+ .class_name = "<invalid class>",
+ .class_id = "<invalid class-id>",
+ .constructor = "<invalid constructor>",
+ .destructor = invalid_destructor,
+ .type_name = "<invalid class type>",
+ .type_id = MRP_LUA_NONE,
+ .userdata_id = "<invalid userdata>",
+ .userdata_size = 0,
+ .methods = NULL,
+ .overrides = NULL,
+ .members = NULL,
+ .nmember = 0,
+ .natives = NULL,
+ .nnative = 0,
+ .notify = NULL,
+ .flags = 0,
+}, *invalid_class = &invalid_classdef;
+
+
+/**
+ * object infra configurable settings
+ */
+static struct {
+ bool track; /* track objects per classdef */
+ bool set; /* whether already set */
+ bool busy; /* whether taken into use */
+} cfg;
+
+
+/**
+ * indirect table to look up classdefs by type_ids
+ */
+static mrp_lua_classdef_t **classdefs;
+static int nclassdef;
+
+/**
+ * Macros to convert between userdata and user-visible data addresses.
+ */
+
+#define USERDATA_SIZE \
+ (cfg.track?MRP_OFFSET(userdata_t, hook[1]):MRP_OFFSET(userdata_t, hook[0]))
+
+#define USER_TO_DATA(u) user_to_data(u)
+#define DATA_TO_USER(d) data_to_user(d)
+
+static inline void *user_to_data(userdata_t *u)
+{
+ if (u != NULL) {
+ if (cfg.track)
+ return &((userdata_t *)(u))->hook[1];
+ else
+ return &((userdata_t *)(u))->hook[0];
+ }
+ else
+ return NULL;
+}
+
+static inline userdata_t *data_to_user(void *d)
+{
+ if (d != NULL)
+ return ((userdata_t *)(((void *)d) - USERDATA_SIZE));
+ else
+ return NULL;
+}
+
+
+/** Check our configuration from the environment. */
+static void check_config(void)
+{
+ char *config = getenv(MRP_LUA_CONFIG_ENVVAR);
+
+ if (mrp_env_config_bool(config, "track", false))
+ mrp_lua_track_objects(true);
+}
+
+
+/** Encode our self(ish) pointer. */
+static inline void userdata_setself(userdata_t *u)
+{
+#ifdef __MURPHY_MANGLE_CLASS_SELF__
+ void *data = USER_TO_DATA(u);
+
+ u->selfish = (void *)(((ptrdiff_t)u) ^ ((ptrdiff_t)data));
+#else
+ u->selfish = u;
+#endif
+}
+
+/** Decode our self(ish) pointer, return NULL if the most basic check fails. */
+static inline void *userdata_getself(userdata_t *u)
+{
+#ifdef __MURPHY_MANGLE_CLASS_SELF__
+ void *data = USER_TO_DATA(u);
+ void *self = u ? (void *)(((ptrdiff_t)u->selfish) ^ ((ptrdiff_t)data)):NULL;
+#else
+ void *self = u ? u->selfish : NULL;
+#endif
+
+ if (u == self)
+ return self;
+ else
+ return NULL;
+}
+
+/** Check if the give pointer appears to point to a valid userdata. */
+static inline bool valid_userdata(userdata_t *u)
+{
+ return (userdata_getself(u) == u);
+}
+
+/** Obtain userdata for a data pointer, optionally checking basic validity. */
+static inline userdata_t *userdata_get(void *data, bool check)
+{
+ userdata_t *u;
+
+ if (data != NULL) {
+ u = DATA_TO_USER(data);
+
+ if (!check || userdata_getself(u) == u)
+ return u;
+ }
+
+ return NULL;
+}
+
+/** Obtain data for a userdata pointer, optionally checking for validity. */
+static inline void *object_get(userdata_t *u, bool check)
+{
+ if (u != NULL) {
+ if (!check || (userdata_getself(u) == u))
+ return USER_TO_DATA(u);
+ }
+
+ return NULL;
+}
+
+
+/** Create and register a new object class definition. */
+int mrp_lua_create_object_class(lua_State *L, mrp_lua_classdef_t *def)
+{
+ static bool chkconfig = true;
+
+ mrp_debug("registering Lua object class '%s'", def->class_name);
+
+ if (def->constructor == NULL) {
+ mrp_log_error("Classes with NULL constructor not allowed.");
+ mrp_log_error("Please define a constructor for class %s (type %s).",
+ def->class_name, def->type_name);
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (chkconfig) {
+ check_config();
+ chkconfig = false;
+ }
+
+ /* make a metatatable for userdata, ie for 'c' part of object instances*/
+ luaL_newmetatable(L, def->userdata_id);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ lua_pushcfunction(L, userdata_destructor);
+ lua_setfield(L, -2, "__gc");
+
+ lua_pushcfunction(L, override_tostring);
+ lua_setfield(L, -2, "__tostring");
+
+ lua_pop(L, 1);
+
+ /* define pre-declared members */
+ {
+ mrp_lua_class_member_t *members = def->members;
+ int nmember = def->nmember;
+ char **natives = def->natives;
+ int nnative = def->nnative;
+ mrp_lua_class_notify_t notify = def->notify;
+ int flags = def->flags;
+
+ def->members = NULL;
+ def->nmember = 0;
+ def->natives = NULL;
+ def->nnative = 0;
+ def->notify = NULL;
+ def->flags = 0;
+ def->brmeta = LUA_NOREF;
+
+ if (mrp_lua_declare_members(def, flags, members, nmember,
+ natives, nnative, notify) != 0) {
+ luaL_error(L, "failed to create object class '%s'",
+ def->class_name);
+ }
+ }
+
+ mrp_list_init(&def->objects);
+
+ /* make the class table */
+ luaL_openlib(L, def->constructor, def->methods, 0);
+
+ /* make a metatable for class, ie. for LUA part of object instances */
+ luaL_newmetatable(L, def->class_id);
+
+ if (mrp_reallocz(classdefs, nclassdef, nclassdef + 1) != NULL) {
+ def->type_id = MRP_LUA_OBJECT + nclassdef;
+ classdefs[nclassdef++] = def;
+ }
+ else {
+ mrp_log_error("Failed to store class %s in lookup table.",
+ def->class_name);
+ def->type_id = MRP_LUA_NONE;
+ }
+
+ /* XXX TODO we could/should do better identification */
+ def->type_meta = lua_topointer(L, -1);
+
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+
+ luaL_openlib(L, NULL, def->overrides, 0);
+ lua_setmetatable(L, -2);
+
+ lua_pop(L, 1);
+
+ return 0;
+}
+
+
+/** Traverse a dott global name and push the table it resolves to, or nil. */
+void mrp_lua_get_class_table(lua_State *L, mrp_lua_classdef_t *def)
+{
+#if 0
+ const char *p;
+ char *q;
+ char tag[256];
+
+ lua_pushvalue(L, LUA_GLOBALSINDEX);
+
+ for (p = def->constructor, q = tag; *p; p++) {
+ if ((*q++ = *p) == '.') {
+ q[-1] = '\0';
+ lua_getfield(L, -1, tag);
+ if (lua_type(L, -1) != LUA_TTABLE) {
+ lua_pop(L, 2);
+ lua_pushnil(L);
+ return;
+ }
+ lua_remove(L, -2);
+ q = tag;
+ }
+ } /* for */
+
+ *q = '\0';
+
+ lua_getfield(L, -1, tag);
+ lua_remove(L, -2);
+#else
+ const char *p;
+ char *q;
+ char tag[256];
+ int idx;
+
+ for (p = def->constructor, q = tag, idx = 0; *p; p++) {
+ if ((*q++ = *p) == '.') {
+ q[-1] = '\0';
+ if (idx++ == 0) {
+ lua_pushnil(L);
+ mrp_lua_getglobal(L, tag);
+ }
+ else
+ lua_getfield(L, -1, tag);
+ if (lua_type(L, -1) != LUA_TTABLE) {
+ lua_pop(L, 2);
+ lua_pushnil(L);
+ return;
+ }
+ lua_remove(L, -2);
+ q = tag;
+ }
+ } /* for */
+
+ *q = '\0';
+
+ if (idx++ == 0) {
+ lua_pushnil(L);
+ mrp_lua_getglobal(L, tag);
+ }
+ else
+ lua_getfield(L, -1, tag);
+
+ lua_remove(L, -2);
+#endif
+}
+
+
+static void invalid_destructor(void *data)
+{
+ MRP_UNUSED(data);
+ mrp_log_error("<invalid-destructor> called");
+}
+
+
+static mrp_lua_classdef_t *class_by_type(int type_id)
+{
+ int idx = type_id - MRP_LUA_OBJECT;
+
+ if (0 <= idx && idx < nclassdef)
+ return classdefs[idx];
+ else
+ return invalid_class;
+}
+
+
+static mrp_lua_classdef_t *class_by_type_name(const char *type_name)
+{
+ mrp_lua_classdef_t *def;
+ int i;
+
+ for (i = 0; i < nclassdef; i++) {
+ def = classdefs[i];
+
+ if (def->type_name[0] != type_name[0])
+ continue;
+
+ if (!strcmp(def->type_name + 1, type_name + 1))
+ return def;
+ }
+
+ return invalid_class;
+}
+
+
+static mrp_lua_classdef_t *class_by_class_name(const char *class_name)
+{
+ mrp_lua_classdef_t *def;
+ int i;
+
+ for (i = 0; i < nclassdef; i++) {
+ def = classdefs[i];
+
+ if (def->class_name[0] != class_name[0])
+ continue;
+
+ if (!strcmp(def->class_name + 1, class_name + 1))
+ return def;
+ }
+
+ return invalid_class;
+}
+
+
+static mrp_lua_classdef_t *class_by_class_id(const char *class_id)
+{
+ mrp_lua_classdef_t *def;
+ int i;
+
+ for (i = 0; i < nclassdef; i++) {
+ def = classdefs[i];
+
+ if (def->class_id[0] != class_id[0])
+ continue;
+
+ if (!strcmp(def->class_id + 1, class_id + 1))
+ return def;
+ }
+
+ return invalid_class;
+}
+
+
+static mrp_lua_classdef_t *class_by_userdata_id(const char *userdata_id)
+{
+ mrp_lua_classdef_t *def;
+ int i;
+
+ for (i = 0; i < nclassdef; i++) {
+ def = classdefs[i];
+
+ if (def->userdata_id[0] != userdata_id[0])
+ continue;
+
+ if (!strcmp(def->userdata_id + 1, userdata_id + 1))
+ return def;
+ }
+
+ return invalid_class;
+}
+
+/** Get the type_id for the given class name. */
+mrp_lua_type_t mrp_lua_class_name_type(const char *class_name)
+{
+ return class_by_class_name(class_name)->type_id;
+}
+
+/** Get the type_id for the given class id. */
+mrp_lua_type_t mrp_lua_class_id_type(const char *class_id)
+{
+ return class_by_class_id(class_id)->type_id;
+}
+
+/** Get the type_id for the given class type name. */
+mrp_lua_type_t mrp_lua_class_type(const char *type_name)
+{
+ return class_by_type_name(type_name)->type_id;
+}
+
+/** Dump the given oject instance for debugging. */
+static const char *__instance(userdata_t **uptr, const char *fmt)
+{
+ static char buf[16][256];
+ static int idx = 0;
+
+ userdata_t *u = uptr ? *uptr : NULL;
+ char *p = buf[idx++];
+ char *r = p;
+ const char *s;
+ int l, n;
+
+ /*
+ * Notes: The currently implemeted format specifiers are:
+ * 'D': dynamic flag, avaluates to 'S' or 'D'
+ * 't': object type name
+ * 'i': object instancem indirect userdata pointer
+ * 'u': object userdata_t
+ * 'd': object user-visible data
+ * 'S': USERDATA_SIZE
+ * 'R': reference count
+ */
+
+ if (!fmt || !*fmt || (fmt[0] == '*' && fmt[1] == '\0'))
+ fmt = "<%D:%t>%i:(%u+%S)>";
+
+ l = (int)sizeof(buf[0]);
+ s = fmt;
+ while (*s && l > 0) {
+ if (*s != '%') {
+ *p++ = *s++;
+ l--;
+ continue;
+ }
+
+ if (!u) {
+ *p++ = '?';
+ s++;
+ l--;
+ continue;
+ }
+
+ s++;
+
+#define P(fmt, arg) n = snprintf(p, l, fmt, arg);
+ switch (*s) {
+ case 'D': P("%s", u->def->flags & MRP_LUA_CLASS_DYNAMIC?"D":"S"); break;
+ case 't': P("%s", u->def->type_name); break;
+ case 'i': P("%p", uptr); break;
+ case 'u': P("%p", u); break;
+ case 'd': P("%p", USER_TO_DATA(u)); break;
+ case 'S': P("%d", (int)USERDATA_SIZE); break;
+ case 'R': P("%d", (int)u->refcnt); break;
+ default : P("%s", "?"); break;
+ }
+#undef P
+
+ p += n;
+ l -= n;
+
+ if (*s)
+ s++;
+ }
+
+ if (l <= 0)
+ buf[idx-1][sizeof(buf[0])-1] = '\0';
+
+ if (idx >= (int)MRP_ARRAY_SIZE(buf))
+ idx = 0;
+
+ return r;
+}
+
+/** Dump the given object for debugging. */
+static const char *__object(userdata_t *u, const char *fmt)
+{
+ static char buf[16][256];
+ static int idx = 0;
+
+ char *p = buf[idx++];
+ char *r = p;
+ const char *s;
+ int l, n;
+
+ /*
+ * Notes: The currently implemeted format specifiers are:
+ * 'D': dynamic flag, avaluates to 'S' or 'D'
+ * 't': object type name
+ * 'u': object userdata_t
+ * 'd': object user-visible data
+ * 'S': USERDATA_SIZE
+ * 'R': reference count
+ */
+
+ if (!fmt || !*fmt || (fmt[0] == '*' && fmt[1] == '\0'))
+ fmt = "<%D:%t>%i:(%u+%S)>";
+
+ l = (int)sizeof(buf[0]);
+ s = fmt;
+ while (*s && l > 0) {
+ if (*s != '%') {
+ *p++ = *s++;
+ l--;
+ continue;
+ }
+
+ if (!u) {
+ *p++ = '?';
+ s++;
+ l--;
+ continue;
+ }
+
+ s++;
+
+#define P(fmt, arg) n = snprintf(p, l, fmt, arg);
+ switch (*s) {
+ case 'D': P("%s", u->def->flags & MRP_LUA_CLASS_DYNAMIC?"D":"S"); break;
+ case 't': P("%s", u->def->type_name); break;
+ case 'i': P("%s", "?"); break;
+ case 'u': P("%p", u); break;
+ case 'd': P("%p", USER_TO_DATA(u)); break;
+ case 'S': P("%d", (int)USERDATA_SIZE); break;
+ case 'R': P("%d", (int)u->refcnt); break;
+ default : P("%s", "?"); break;
+ }
+#undef P
+
+ p += n;
+ l -= n;
+
+ if (*s)
+ s++;
+ }
+
+ if (l <= 0)
+ buf[idx-1][sizeof(buf[0])-1] = '\0';
+
+ if (idx >= (int)MRP_ARRAY_SIZE(buf))
+ idx = 0;
+
+ return r;
+}
+
+
+/** Create a new object, optionally assign it to a class table name or index. */
+void *mrp_lua_create_object(lua_State *L, mrp_lua_classdef_t *def,
+ const char *name, int idx)
+{
+ int class = 0;
+ size_t size;
+ userdata_t **userdatap, *userdata;
+ int dynamic;
+
+ MRP_UNUSED(class_by_userdata_id);
+
+ mrp_lua_checkstack(L, -1);
+
+ if (name || idx) {
+ if (name && !valid_id(name))
+ return NULL;
+
+ mrp_lua_get_class_table(L, def);
+ luaL_checktype(L, -1, LUA_TTABLE);
+ class = lua_gettop(L);
+ }
+
+ lua_createtable(L, 1, 1);
+
+ luaL_openlib(L, NULL, def->methods, 0);
+
+ luaL_getmetatable(L, def->class_id);
+ lua_setmetatable(L, -2);
+
+ lua_pushliteral(L, "userdata");
+
+ size = USERDATA_SIZE + def->userdata_size;
+ userdata = (userdata_t *)mrp_allocz(size);
+
+ if (userdata == NULL) {
+ mrp_log_error("Failed to allocate object of type %s <%s>.",
+ def->class_name, def->type_name);
+ return NULL;
+ }
+
+ userdatap = (userdata_t **)lua_newuserdata(L, sizeof(userdata));
+ *userdatap = userdata;
+ mrp_refcnt_init(&userdata->refcnt);
+
+ if (cfg.track)
+ mrp_list_init(&userdata->hook[0]);
+
+ userdata->refs.priv = LUA_NOREF;
+ userdata->refs.ext = LUA_NOREF;
+
+ luaL_getmetatable(L, def->userdata_id);
+ lua_setmetatable(L, -2);
+
+ lua_rawset(L, -3); /* userdata["userdata"]=lib<def->methods> */
+
+ userdata_setself(userdata);
+ userdata->def = def;
+
+ if (!(dynamic = def->flags & MRP_LUA_CLASS_DYNAMIC)) {
+ lua_pushvalue(L, -1); /* userdata->refs.self = lib<def->methods> */
+ userdata->refs.self = luaL_ref(L, LUA_REGISTRYINDEX);
+ }
+ else
+ userdata->refs.self = LUA_NOREF;
+
+ if (name) {
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, class);
+ }
+
+ if (idx) {
+ lua_pushvalue(L, -1);
+ lua_rawseti(L, class, idx);
+ }
+
+ if (class)
+ lua_remove(L, class);
+
+ object_create_reftbl(userdata, L);
+ if (def->flags & MRP_LUA_CLASS_EXTENSIBLE)
+ object_create_exttbl(userdata, L);
+
+ if (object_setup_bridges(userdata, L) < 0) {
+ luaL_error(L, "Failed to set up bridged methods.");
+ return NULL; /* not reached */
+ }
+
+ if (cfg.track)
+ mrp_list_append(&def->objects, &userdata->hook[0]);
+
+ def->nactive++;
+ def->ncreated++;
+
+ mrp_debug("created %s", __instance(userdatap, "*"));
+
+ return USER_TO_DATA(userdata);
+}
+
+
+/** Set the name of the object @-1 to the given name in the class table. */
+void mrp_lua_set_object_name(lua_State *L, mrp_lua_classdef_t *def,
+ const char *name)
+{
+ if (valid_id(name)) {
+ mrp_lua_get_class_table(L, def);
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ lua_pushstring(L, name);
+ lua_pushvalue(L, -3);
+
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+}
+
+/** Assign the object @-1 to the given index in the class table. */
+void mrp_lua_set_object_index(lua_State *L, mrp_lua_classdef_t *def, int idx)
+{
+ mrp_lua_get_class_table(L, def);
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ lua_pushvalue(L, -2);
+
+ lua_rawseti(L, -2, idx);
+
+ lua_pop(L, 1);
+}
+
+/** Trigger (potential) destruction of the given object. */
+void mrp_lua_destroy_object(lua_State *L, const char *name, int idx, void *data)
+{
+ userdata_t *userdata = userdata_get(data, CHECK);
+ mrp_lua_classdef_t *def;
+
+ if (userdata) {
+ if (userdata->dead)
+ return;
+
+ userdata->dead = true;
+ def = userdata->def;
+
+ if (!(def->flags & MRP_LUA_CLASS_DYNAMIC)) {
+ mrp_debug("destroying %s (name: '%s', idx: %d)",
+ __object(userdata, "*"), name ? name : "", idx);
+
+ def->nactive--;
+ def->ndead++;
+
+ object_delete_reftbl(userdata, L);
+ object_delete_exttbl(userdata, L);
+
+ if (userdata->refs.self != LUA_NOREF) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, userdata->refs.self);
+ lua_pushstring(L, "userdata");
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, -1);
+
+ luaL_unref(L, LUA_REGISTRYINDEX, userdata->refs.self);
+ userdata->refs.self = LUA_NOREF;
+ }
+ }
+ else {
+ mrp_log_error("ERROR: %s should be called for static object",
+ __FUNCTION__);
+ mrp_log_error("ERROR: but was called for %s",
+ __object(userdata, "*"));
+ }
+
+ if (name || idx) {
+ mrp_lua_get_class_table(L, def);
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ if (name) {
+ lua_pushstring(L, name);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ }
+
+ if (idx) {
+ lua_pushnil(L);
+ lua_rawseti(L, -2, idx);
+ }
+
+ lua_pop(L, 1);
+ }
+ }
+}
+
+
+/** Find the object corresponding to the given name in the class table. */
+int mrp_lua_find_object(lua_State *L, mrp_lua_classdef_t *def, const char *name)
+{
+ if (!name)
+ lua_pushnil(L);
+ else {
+ mrp_lua_get_class_table(L, def);
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+ lua_pushstring(L, name);
+ lua_rawget(L, -2);
+
+ lua_remove(L, -2);
+ }
+
+ return 1;
+}
+
+/** Check if the object @idx is ours and optionally of the given type. */
+void *mrp_lua_check_object(lua_State *L, mrp_lua_classdef_t *def, int idx)
+{
+ userdata_t *userdata, **userdatap;
+ char errmsg[256];
+
+ luaL_checktype(L, idx, LUA_TTABLE);
+
+ lua_pushvalue(L, idx);
+ lua_pushliteral(L, "userdata");
+ lua_rawget(L, -2);
+
+ if (!def) {
+ userdatap = (userdata_t **)lua_touserdata(L, -1);
+
+ if (!userdatap) {
+ luaL_argerror(L, idx, "couldn't find expected userdata");
+ userdata = NULL;
+ }
+ else
+ userdata = *userdatap;
+ }
+ else {
+ userdatap = (userdata_t **)luaL_checkudata(L, -1, def->userdata_id);
+
+ if (!userdatap || def != (userdata = *userdatap)->def) {
+ snprintf(errmsg, sizeof(errmsg), "'%s' expected", def->class_name);
+ luaL_argerror(L, idx, errmsg);
+ userdata = NULL;
+ }
+ else
+ userdata = *userdatap;
+ }
+
+ if (userdata_getself(userdata) != userdata) {
+ luaL_error(L, "invalid userdata");
+ userdata = NULL;
+ }
+
+ lua_pop(L, 2);
+
+ return userdata ? USER_TO_DATA(userdata) : NULL;
+}
+
+/** Check if the object @idx is of the given virtual type. */
+int mrp_lua_object_of_type(lua_State *L, int idx, mrp_lua_type_t type)
+{
+ mrp_lua_type_t ltype = (mrp_lua_type_t)lua_type(L, idx);
+ mrp_lua_classdef_t *def;
+ int match;
+
+ switch (type) {
+ case MRP_LUA_NULL:
+ case MRP_LUA_BOOLEAN:
+ case MRP_LUA_STRING:
+ case MRP_LUA_DOUBLE:
+ case MRP_LUA_FUNC:
+ return (type == ltype);
+
+ case MRP_LUA_INTEGER:
+ return ((int)lua_tointeger(L, idx) == (double)lua_tonumber(L, idx));
+
+ case MRP_LUA_LFUNC:
+ return (ltype == LUA_TFUNCTION && !lua_iscfunction(L, idx));
+ case MRP_LUA_CFUNC:
+ return (ltype == LUA_TFUNCTION && lua_iscfunction(L, idx));
+ case MRP_LUA_BFUNC:
+ /* XXX TODO */ mrp_log_error("Can't handle funcbridge yet.");
+ return false;
+
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_STRING_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ return (ltype == LUA_TTABLE); /* XXX could do be better */
+
+ case MRP_LUA_NONE:
+ return false;
+ case MRP_LUA_ANY:
+ return true;
+
+ case MRP_LUA_OBJECT:
+ return (ltype == LUA_TTABLE); /* XXX could do much be better */
+
+ default:
+ if (type > MRP_LUA_MAX)
+ return false;
+
+ if ((def = class_by_type(type)) == invalid_class)
+ return false;
+
+ if (lua_getmetatable(L, idx)) {
+ /* XXX TODO we could/should do better identification */
+ match = (lua_topointer(L, idx) == def->type_meta);
+ lua_pop(L, 1);
+ }
+ else
+ match = false;
+
+ return match;
+ }
+
+ return false;
+}
+
+/** Check if the given data is of the given virtual type. */
+int mrp_lua_pointer_of_type(void *data, mrp_lua_type_t type)
+{
+ userdata_t *u;
+
+ if (type < MRP_LUA_OBJECT) {
+ mrp_log_error("Can't do pointer-based type-equality for "
+ "non-object types.");
+ return 0;
+ }
+
+ /*
+ * We consider NULL to be a valid instance. Might need to be changed.
+ */
+
+ if ((u = userdata_get(data, CHECK)) != NULL)
+ return type == u->def->type_id;
+ else
+ return true;
+}
+
+/** Obtain the user-visible data for the object @idx. */
+void *mrp_lua_to_object(lua_State *L, mrp_lua_classdef_t *def, int idx)
+{
+ userdata_t *userdata, **userdatap;
+ int top = lua_gettop(L);
+
+ idx = (idx < 0) ? lua_gettop(L) + idx + 1 : idx;
+
+ if (!lua_istable(L, idx))
+ return NULL;
+
+ lua_pushliteral(L, "userdata");
+ lua_rawget(L, idx);
+
+ userdatap = (userdata_t **)lua_touserdata(L, -1);
+
+ if (!userdatap || !lua_getmetatable(L, -1)) {
+ lua_settop(L, top);
+ return NULL;
+ }
+
+ userdata = *userdatap;
+
+ lua_getfield(L, LUA_REGISTRYINDEX, def->userdata_id);
+
+ if (!lua_rawequal(L, -1, -2) || userdata != userdata_getself(userdata))
+ userdata = NULL;
+
+ lua_settop(L, top);
+
+ return userdata ? USER_TO_DATA(userdata) : NULL;
+}
+
+
+/** Push the given data on the stack. */
+int mrp_lua_push_object(lua_State *L, void *data)
+{
+ userdata_t *userdata = userdata_get(data, CHECK);
+ userdata_t **userdatap;
+ mrp_lua_classdef_t *def = userdata ? userdata->def : NULL;
+
+ /*
+ * Notes:
+ *
+ * This is essentially mrp_lua_create_object with a few differences:
+ * 1) No need for name or idx handling.
+ * 2) No need for adding global reference, already done during
+ * initial object creation if necessary.
+ * 3) Instead of creating a Lua userdata pointer and a new userdata,
+ * we only create a Lua userdata pointer, make it point to the
+ * existing userdata and increate the userdata reference count.
+ *
+ * userdata_destructor has been similarly modified to decrese the
+ * reference count of userdata and destroy the object only when the
+ * last reference is dropped.
+ */
+
+ mrp_lua_checkstack(L, -1);
+
+ if (!userdata || !def || userdata->dead) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ if (!(def->flags & MRP_LUA_CLASS_DYNAMIC)) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, userdata->refs.self);
+
+ mrp_debug("pushed %s", __object(userdata, "*"));
+ }
+ else {
+ lua_createtable(L, 1, 1);
+
+ luaL_openlib(L, NULL, def->methods, 0);
+
+ luaL_getmetatable(L, def->class_id);
+ lua_setmetatable(L, -2);
+
+ lua_pushliteral(L, "userdata");
+
+ userdatap = (userdata_t **)lua_newuserdata(L, sizeof(userdata));
+ *userdatap = userdata;
+ mrp_ref_obj(userdata, refcnt);
+
+ luaL_getmetatable(L, def->userdata_id);
+ lua_setmetatable(L, -2);
+
+ lua_rawset(L, -3);
+
+ mrp_debug("pushed %s", __instance(userdatap, "*"));
+ }
+
+ return 1;
+}
+
+/** Obtain the class definition for the given object. */
+mrp_lua_classdef_t *mrp_lua_get_object_classdef(void *data)
+{
+ userdata_t *userdata = userdata_get(data, CHECK);
+ mrp_lua_classdef_t *def;
+
+ if (!userdata || userdata->dead)
+ def = NULL;
+ else
+ def = userdata->def;
+
+ return def;
+}
+
+
+static bool valid_id(const char *id)
+{
+ const char *p;
+ char c;
+
+ if (!(p = id) || !isalpha(*p))
+ return false;
+
+ while ((c = *p++)) {
+ if (!isalnum(c) && (c != '_'))
+ return false;
+ }
+
+ return true;
+}
+
+static int userdata_destructor(lua_State *L)
+{
+ userdata_t *userdata, **userdatap;
+ mrp_lua_classdef_t *def;
+
+ if (!(userdatap = lua_touserdata(L, -1)) || !lua_getmetatable(L, -1))
+ luaL_error(L, "attempt to destroy unknown type of userdata");
+ else {
+ userdata = *userdatap;
+ def = userdata->def;
+
+ if (mrp_unref_obj(userdata, refcnt)) {
+ mrp_debug("freeing %s", __instance(userdatap, "*"));
+
+ if (cfg.track)
+ mrp_list_delete(&userdata->hook[0]);
+
+ lua_getfield(L, LUA_REGISTRYINDEX, def->userdata_id);
+ if (!lua_rawequal(L, -1, -2))
+ luaL_typerror(L, -2, def->userdata_id);
+ else
+ def->destructor(USER_TO_DATA(userdata));
+
+ if (def->flags & MRP_LUA_CLASS_DYNAMIC) {
+ def->nactive--;
+
+ object_delete_reftbl(userdata, L);
+ object_delete_exttbl(userdata, L);
+ }
+ else {
+ def->ndead--;
+ }
+
+ def->ndestroyed++;
+
+ *userdatap = NULL;
+ mrp_free(userdata);
+ }
+ else {
+ mrp_debug("unreffed %s", __instance(userdatap, "*"));
+
+ if (!(def->flags & MRP_LUA_CLASS_DYNAMIC))
+ mrp_log_error("Hmm, more refs for a static object ?");
+
+ *userdatap = NULL;
+ }
+ }
+
+ return 0;
+}
+
+
+static int default_setter(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ userdata_t *u = DATA_TO_USER(data);
+ mrp_lua_class_member_t *m;
+ mrp_lua_value_t *vptr;
+ void **itemsp;
+ union {
+ void *ptr;
+ size_t *size;
+ uint32_t *u32;
+ } nitemp;
+ size_t nitem;
+
+ mrp_lua_checkstack(L, -1);
+
+ m = u->def->members + member;
+ vptr = data + m->offs;
+
+ if (L == NULL) {
+ switch (m->type) {
+ case MRP_LUA_STRING:
+ vptr->str = NULL;
+ goto ok;
+ case MRP_LUA_FUNC:
+ case MRP_LUA_LFUNC:
+ case MRP_LUA_CFUNC:
+ vptr->lfn = LUA_NOREF;
+ goto ok;
+ case MRP_LUA_BFUNC:
+ vptr->bfn = NULL;
+ goto ok;
+ case MRP_LUA_ANY:
+ vptr->any = LUA_NOREF;
+ goto ok;
+ case MRP_LUA_STRING_ARRAY:
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ itemsp = data + m->offs;
+ nitemp.ptr = data + m->size;
+ *itemsp = NULL;
+ if (m->sizew == 8)
+ *nitemp.size = 0;
+ else
+ *nitemp.u32 = 0;
+ goto ok;
+ case MRP_LUA_OBJECT:
+ if (m->type_id == MRP_LUA_NONE)
+ m->type_id = class_by_type_name(m->type_name)->type_id;
+ *((void **)(data + m->offs)) = NULL;
+ *((int *)(data + m->size)) = LUA_NOREF;
+ default:
+ goto error;
+ }
+ }
+
+ switch (m->type) {
+ case MRP_LUA_STRING:
+ mrp_free((void *)vptr->str);
+ vptr->str = v->str ? mrp_strdup(v->str) : NULL;
+
+ if (vptr->str == NULL && v->str != NULL)
+ goto error;
+ else
+ goto ok;
+
+ case MRP_LUA_BOOLEAN:
+ vptr->bln = v->bln;
+ goto ok;
+
+ case MRP_LUA_INTEGER:
+ vptr->s32 = v->s32;
+ goto ok;
+
+ case MRP_LUA_DOUBLE:
+ vptr->dbl = v->dbl;
+ goto ok;
+
+ case MRP_LUA_FUNC:
+ mrp_lua_object_unref_value(data, L, vptr->lfn);
+ vptr->lfn = v->lfn;
+ goto ok;
+
+ case MRP_LUA_LFUNC:
+ mrp_lua_object_unref_value(data, L, vptr->lfn);
+ vptr->lfn = v->lfn;
+ goto ok;
+
+ case MRP_LUA_CFUNC:
+ mrp_lua_object_unref_value(data, L, vptr->lfn);
+ vptr->lfn = v->lfn;
+ goto ok;
+
+ case MRP_LUA_BFUNC:
+ goto error;
+
+ case MRP_LUA_ANY:
+ mrp_lua_object_unref_value(data, L, vptr->any);
+ vptr->any = v->any;
+ goto ok;
+
+ case MRP_LUA_STRING_ARRAY:
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ itemsp = data + m->offs;
+ nitemp.ptr = data + m->size;
+ if (m->sizew == 8)
+ nitem = *nitemp.size;
+ else
+ nitem = *nitemp.u32;
+ mrp_lua_object_free_array(itemsp, &nitem, m->type);
+ *itemsp = *v->array.items;
+ if (m->sizew == 8)
+ *nitemp.size = *v->array.nitem64;
+ else
+ *nitemp.u32 = (uint32_t)*v->array.nitem64;
+ goto ok;
+
+ case MRP_LUA_OBJECT:
+ mrp_lua_object_unref_value(data, L, *((int *)(data + m->size)));
+ *((void **)(data + m->offs)) = v->obj.ptr;
+ *((int *)(data + m->size)) = v->obj.ref;
+ goto ok;
+
+ default:
+ goto error;
+ }
+
+ ok:
+ return 1;
+
+ error:
+ return -1;
+}
+
+
+static int default_getter(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ userdata_t *u = DATA_TO_USER(data);
+ mrp_lua_class_member_t *m;
+ mrp_lua_value_t *vptr;
+
+ MRP_UNUSED(L);
+
+ mrp_lua_checkstack(L, -1);
+
+ m = u->def->members + member;
+ vptr = data + m->offs;
+
+ switch (m->type) {
+ case MRP_LUA_STRING:
+ v->str = vptr->str;
+ goto ok;
+
+ case MRP_LUA_BOOLEAN:
+ v->bln = vptr->bln;
+ goto ok;
+
+ case MRP_LUA_INTEGER:
+ v->s32 = vptr->s32;
+ goto ok;
+
+ case MRP_LUA_DOUBLE:
+ v->dbl = vptr->dbl;
+ goto ok;
+
+ case MRP_LUA_FUNC:
+ v->lfn = vptr->lfn;
+ goto ok;
+
+ case MRP_LUA_LFUNC:
+ v->lfn = vptr->lfn;
+ goto ok;
+
+ case MRP_LUA_CFUNC:
+ v->lfn = vptr->lfn;
+ goto ok;
+
+ case MRP_LUA_BFUNC:
+ goto error;
+
+ case MRP_LUA_ANY:
+ v->any = vptr->any;
+ goto ok;
+
+ case MRP_LUA_STRING_ARRAY:
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ v->array = vptr->array;
+ goto ok;
+
+ case MRP_LUA_OBJECT:
+ v->obj.ptr = *((void **)(data + m->offs));
+ v->obj.ref = *((int *)(data + m->size));
+ goto ok;
+
+ default:
+ goto error;
+ }
+
+ ok:
+ return 1;
+
+ error:
+ return -1;
+}
+
+
+static int patch_overrides(mrp_lua_classdef_t *def)
+{
+ luaL_reg set = { NULL, NULL }, get = { NULL, NULL }, *r, *overrides;
+ int i, n, extra, tostring;
+
+ tostring = 0;
+ for (n = 0, r = def->overrides; r->name != NULL; r++, n++) {
+ if (!strcmp(r->name, "__newindex")) {
+ if (set.name != NULL) {
+ mrp_log_error("Class with multiple SETFIELD overrides.");
+ exit(1);
+ }
+
+ if (set.func == override_setfield) {
+ mrp_log_error("SETFIELD already overridden to setfield!");
+ exit(1);
+ }
+
+ set = *r;
+ r->func = override_setfield;
+ continue;
+ }
+
+ if (!strcmp(r->name, "__index")) {
+ if (get.name != NULL) {
+ mrp_log_info("Class with multiple GETFIELD overrides.");
+ exit(1);
+ }
+
+ if (get.func == override_getfield) {
+ mrp_log_error("GETFIELD already overridden to getfield!");
+ exit(1);
+ }
+
+ get = *r;
+ r->func = override_getfield;
+ continue;
+ }
+
+ if (!strcmp(r->name, "__tostring")) {
+ tostring = 1;
+ continue;
+ }
+ }
+
+ if (set.func && get.func && tostring) {
+ def->setfield = set.func;
+ def->getfield = get.func;
+
+ return 0;
+ }
+
+ extra = (set.func ? 0 : 1) + (get.func ? 0 : 1) + (tostring ? 0 : 1);
+
+ /* XXX TODO: currently this is leaked if/when a classdef is destroyed */
+ if ((overrides = mrp_allocz_array(typeof(*overrides), n+1 + extra)) == NULL)
+ return -1;
+
+ for (i = 0, r = def->overrides; r->name != NULL; i++, r++) {
+ overrides[i].name = r->name;
+ overrides[i].func = r->func;
+ }
+
+ if (set.func == NULL) {
+ mrp_debug("overriding __newindex for class %s", def->class_name);
+ overrides[i].name = "__newindex";
+ overrides[i].func = override_setfield;
+ i++;
+ }
+ else
+ def->setfield = set.func;
+
+ if (get.func == NULL) {
+ mrp_debug("overriding __index for class %s", def->class_name);
+ overrides[i].name = "__index";
+ overrides[i].func = override_getfield;
+ i++;
+ }
+ else
+ def->getfield = get.func;
+
+ if (!tostring) {
+ mrp_debug("overriding __tostring for class %s", def->class_name);
+ overrides[i].name = "__tostring";
+ overrides[i].func = override_tostring;
+ i++;
+ }
+
+ def->overrides = overrides;
+
+ return 0;
+}
+
+
+/** Declare automatically handled class members for the given class. */
+int mrp_lua_declare_members(mrp_lua_classdef_t *def, mrp_lua_class_flag_t flags,
+ mrp_lua_class_member_t *members, int nmember,
+ char **natives, int nnative,
+ mrp_lua_class_notify_t notify)
+{
+#define F(flag) MRP_LUA_CLASS_##flag
+#define INHERITED_FLAGS (F(READONLY)|F(RAWGETTER)|F(RAWSETTER))
+
+ mrp_lua_class_member_t *m;
+ int i;
+
+ def->flags = flags;
+
+ if (members == NULL || nmember <= 0) {
+ if (def->flags & MRP_LUA_CLASS_EXTENSIBLE)
+ goto update_overrides;
+ else
+ return 0;
+ }
+
+ def->members = mrp_allocz_array(typeof(*def->members), nmember);
+
+ if (def->members == NULL)
+ return -1;
+
+ for (i = 0, m = def->members; i < nmember; i++, m++) {
+ if (members[i].flags & MRP_LUA_CLASS_NOTIFY) {
+ if (notify == NULL) {
+ mrp_log_error("member '%s' needs a non-NULL notifier",
+ members[i].name);
+ goto fail;
+ }
+ }
+
+ if (MRP_LUA_BOOLEAN_ARRAY <= members[i].type &&
+ members[i].type <= MRP_LUA_DOUBLE_ARRAY) {
+ if (members[i].sizew != 8 && members[i].sizew != 4) {
+ mrp_log_error("array member '%s': size must be 32- or 64-bit",
+ members[i].name);
+ goto fail;
+ }
+ }
+
+ if ((m->name = mrp_strdup(members[i].name)) == NULL)
+ goto fail;
+
+ *m = members[i];
+
+ if (m->setter == NULL)
+ m->setter = default_setter;
+ if (m->getter == NULL)
+ m->getter = default_getter;
+
+ m->flags |= (flags & INHERITED_FLAGS); /* inherit flags we can */
+
+ /* clear flags the default setter and getter don't do */
+ if (m->setter == default_setter)
+ m->flags &= ~MRP_LUA_CLASS_RAWSETTER;
+
+ if (m->getter == default_getter)
+ m->flags &= ~MRP_LUA_CLASS_RAWGETTER;
+
+ def->nmember++;
+ }
+
+ def->flags = flags;
+ def->notify = notify;
+
+ if (natives == NULL || nnative == 0)
+ goto update_overrides;
+
+ def->natives = mrp_allocz_array(typeof(*def->natives), nnative);
+
+ if (def->natives == NULL)
+ goto fail;
+
+ for (i = 0; i < nnative; i++) {
+ if ((def->natives[i] = mrp_strdup(natives[i])) == NULL)
+ goto fail;
+
+ def->nnative++;
+ }
+
+ update_overrides:
+ if (!(def->flags & MRP_LUA_CLASS_NOOVERRIDE))
+ patch_overrides(def);
+
+ return 0;
+
+ fail:
+ for (i = 0, m = def->members; i < def->nmember; i++, m++)
+ mrp_free(m->name);
+ mrp_free(def->members);
+
+ def->members = NULL;
+ def->nmember = 0;
+
+ for (i = 0; i < def->nnative; i++)
+ mrp_free(def->natives[i]);
+ mrp_free(def->natives);
+
+ def->natives = NULL;
+ def->nnative = 0;
+
+ return -1;
+}
+
+
+static int object_setup_bridges(userdata_t *u, lua_State *L)
+{
+ mrp_lua_classdef_t *def = u->def;
+ mrp_lua_class_bridge_t *b;
+ int i, class_usestack;
+
+ class_usestack = (def->flags & MRP_LUA_CLASS_USESTACK) ? true : false;
+ for (i = 0, b = def->bridges; i < def->nbridge; i++, b++) {
+ b->fb = mrp_funcbridge_create_cfunc(L, b->name, b->signature, b->fc,
+ USER_TO_DATA(u));
+
+ if (b->fb == NULL)
+ return -1;
+
+ b->fb->autobridge = true;
+ b->fb->usestack = (b->flags & MRP_LUA_CLASS_USESTACK) ? true : false;
+ b->fb->usestack |= class_usestack;
+ }
+
+ return 0;
+}
+
+
+static int class_member(userdata_t *u, lua_State *L, int index)
+{
+ mrp_lua_class_member_t *members = u->def->members;
+ int nmember = u->def->nmember;
+ mrp_lua_class_member_t *m;
+ int i;
+ const char *name;
+
+ if (lua_type(L, index) != LUA_TSTRING)
+ return -1;
+
+ name = lua_tostring(L, index);
+
+ /*
+ * XXX TODO, check how to speed this up. For instance if Lua
+ * strings or references to string happened to be always
+ * represented by the same value as long as they are interned
+ * (ie. not collected) we could simply check for numeric
+ * equality of the stack item or a reference to thereof to one
+ * store in the object classdef...
+ *
+ * Alternatively if all else fails, at least pass in the length
+ * here and store it alongside all native names, to speed up
+ * negtive tests.
+ */
+
+ for (i = 0, m = members; i < nmember; i++, m++)
+ if (!strcmp(m->name, name))
+ return i;
+
+ return -1;
+}
+
+
+static int class_bridge(userdata_t *u, lua_State *L, int index)
+{
+ mrp_lua_class_bridge_t *bridges, *b;
+ int nbridge;
+ const char *name;
+ int bidx;
+
+ if ((bridges = u->def->bridges) == NULL || (nbridge = u->def->nbridge) == 0)
+ return -1;
+
+ if (lua_type(L, index) != LUA_TSTRING)
+ return -1;
+
+ name = lua_tostring(L, index);
+
+ /*
+ * XXX TODO, ditto as for class_member()
+ */
+
+ for (bidx = 0, b = bridges; bidx < nbridge; bidx++, b++)
+ if (!strcmp(b->name, name))
+ return bidx;
+
+ return -1;
+}
+
+
+static int seterr(lua_State *L, char *e, size_t size, const char *format, ...)
+{
+ va_list ap;
+ char msg[256];
+
+ va_start(ap, format);
+ vsnprintf(e ? e : msg, e ? size : sizeof(msg), format, ap);
+ va_end(ap);
+
+ if (!e && L) {
+ lua_pushstring(L, msg);
+ lua_error(L);
+ }
+
+ return -1;
+}
+
+
+static void object_create_reftbl(userdata_t *u, lua_State *L)
+{
+ lua_newtable(L);
+ u->refs.priv = luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
+
+static void object_delete_reftbl(userdata_t *u, lua_State *L)
+{
+ luaL_unref(L, LUA_REGISTRYINDEX, u->refs.priv);
+ u->refs.priv = LUA_NOREF;
+}
+
+
+/** Refcount the object @idx for or within the given object. */
+int mrp_lua_object_ref_value(void *data, lua_State *L, int idx)
+{
+ userdata_t *u = userdata_get(data, CHECK);
+ int ref;
+
+ if (u->refs.priv != LUA_NOREF) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.priv);
+ lua_pushvalue(L, idx > 0 ? idx : idx - 1);
+ ref = luaL_ref(L, -2);
+ lua_pop(L, 1);
+ }
+ else
+ ref = LUA_NOREF;
+
+ return ref;
+}
+
+/** Release the given reference for/from within the given object. */
+void mrp_lua_object_unref_value(void *data, lua_State *L, int ref)
+{
+ userdata_t *u = userdata_get(data, CHECK);
+
+ if (ref != LUA_NOREF && ref != LUA_REFNIL) {
+ if (u->refs.priv != LUA_NOREF) {
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.priv);
+ luaL_unref(L, -1, ref);
+ lua_pop(L, 1);
+ }
+ }
+}
+
+/** Obtain and push the object for the given reference on the stack. */
+int mrp_lua_object_deref_value(void *data, lua_State *L, int ref, int pushnil)
+{
+ userdata_t *u = userdata_get(data, CHECK);
+
+ if (ref == LUA_REFNIL) {
+ nilref:
+ lua_pushnil(L);
+ return 1;
+ }
+
+ if (ref == LUA_NOREF) {
+ if (pushnil)
+ goto nilref;
+ else
+ return 0;
+ }
+
+ if (u->refs.priv == LUA_NOREF) {
+ if (pushnil)
+ goto nilref;
+ else
+ return 0;
+ }
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.priv);
+ lua_rawgeti(L, -1, ref);
+ lua_remove(L, -2);
+
+ return 1;
+}
+
+/** Obtain a new reference based on the reference owned by owner. */
+int mrp_lua_object_getref(void *owner, void *data, lua_State *L, int ref)
+{
+ userdata_t *uo = userdata_get(owner, CHECK);
+ userdata_t *ud = userdata_get(data , CHECK);
+
+ if (ref == LUA_NOREF || ref == LUA_REFNIL)
+ return ref;
+
+ if (uo->refs.priv == LUA_NOREF || ud->refs.priv == LUA_NOREF)
+ return LUA_NOREF;
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, uo->refs.priv);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ud->refs.priv);
+
+ lua_rawgeti(L, -2, ref);
+ ref = luaL_ref(L, -2);
+
+ lua_pop(L, 2);
+
+ return ref;
+}
+
+
+static void object_create_exttbl(userdata_t *u, lua_State *L)
+{
+ lua_newtable(L);
+ u->refs.ext = luaL_ref(L, LUA_REGISTRYINDEX);
+}
+
+
+static void object_delete_exttbl(userdata_t *u, lua_State *L)
+{
+ int extidx;
+
+ if (u->refs.ext == LUA_NOREF)
+ return;
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.ext);
+ extidx = lua_gettop(L);
+
+ /*
+ * Notes:
+ * I'm not sure whether explicitly unreffing all references
+ * is necessary... In principle this should not be necessary.
+ * We should have the only reference to our exttbl and we are
+ * about to remove that making exttb garbage-collectable.
+ */
+
+ lua_pushnil(L);
+ while (lua_next(L, extidx) != 0) {
+ lua_pop(L, 1);
+ lua_pushvalue(L, -1);
+ lua_pushnil(L);
+
+ mrp_debug("freeing extended member [%s] of %s", lua_tostring(L, -2),
+ __object(u, "*"));
+
+ lua_rawset(L, extidx);
+ }
+
+ luaL_unref(L, LUA_REGISTRYINDEX, u->refs.ext);
+ u->refs.ext = LUA_NOREF;
+
+ lua_settop(L, extidx);
+ lua_pop(L, 1);
+}
+
+
+static int object_setext(void *data, lua_State *L, const char *name,
+ int vidx, char *err, size_t esize)
+{
+ userdata_t *u = DATA_TO_USER(data);
+
+ if (u->refs.ext == LUA_NOREF) {
+ if (err)
+ return seterr(L, err, esize, "trying to set user-defined field %s "
+ "for non-extensible object %s", name,
+ u->def->class_name);
+ else
+ return luaL_error(L, "trying to set user-defined field %s "
+ "for non-extensible object %s", name,
+ u->def->class_name);
+ }
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.ext);
+ lua_pushvalue(L, vidx > 0 ? vidx : vidx - 1);
+ lua_setfield(L, -2, name);
+ lua_pop(L, 1);
+
+ return 1;
+}
+
+
+static int object_getext(void *data, lua_State *L, const char *name)
+{
+ userdata_t *u = DATA_TO_USER(data);
+
+ if (u->refs.ext == LUA_NOREF) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.ext);
+ lua_getfield(L, -1, name);
+ lua_remove(L, -2);
+
+ return 1;
+}
+
+
+static int object_setiext(void *data, lua_State *L, int idx, int val)
+{
+ userdata_t *u = DATA_TO_USER(data);
+
+ if (u->refs.ext == LUA_NOREF) {
+ return luaL_error(L, "trying to set user-defined index %d "
+ "for non-extensible object %s", idx,
+ u->def->class_name);
+ }
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.ext);
+ lua_pushvalue(L, val > 0 ? val : val - 1);
+ lua_rawseti(L, -2, idx);
+ lua_pop(L, 1);
+
+ return 1;
+}
+
+
+static int object_getiext(void *data, lua_State *L, int idx)
+{
+ userdata_t *u = DATA_TO_USER(data);
+
+ if (u->refs.ext == LUA_NOREF) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, u->refs.ext);
+ lua_rawgeti(L, -1, idx);
+ lua_remove(L, -2);
+
+ return 1;
+}
+
+
+static inline int array_lua_type(int type)
+{
+ switch (type) {
+ case MRP_LUA_STRING_ARRAY: return LUA_TSTRING;
+ case MRP_LUA_BOOLEAN_ARRAY: return LUA_TBOOLEAN;
+ case MRP_LUA_INTEGER_ARRAY: return LUA_TNUMBER;
+ case MRP_LUA_DOUBLE_ARRAY: return LUA_TNUMBER;
+ default: return LUA_TNONE;
+ }
+}
+
+
+static inline int array_murphy_type(int type)
+{
+ switch (type) {
+ case LUA_TSTRING: return MRP_LUA_STRING_ARRAY;
+ case LUA_TBOOLEAN: return MRP_LUA_BOOLEAN_ARRAY;
+ case LUA_TNUMBER: return MRP_LUA_INTEGER_ARRAY;
+ default: return MRP_LUA_NONE;
+ }
+}
+
+
+static inline int array_item_size(int type)
+{
+ switch (type) {
+ case MRP_LUA_STRING_ARRAY: return sizeof(char *);
+ case MRP_LUA_BOOLEAN_ARRAY: return sizeof(bool);
+ case MRP_LUA_INTEGER_ARRAY: return sizeof(int32_t);
+ case MRP_LUA_DOUBLE_ARRAY: return sizeof(double);
+ default: return 0;
+ }
+}
+
+
+static inline const char *array_type_name(int type)
+{
+ switch (type) {
+ case MRP_LUA_STRING_ARRAY: return "string";
+ case MRP_LUA_BOOLEAN_ARRAY: return "boolean";
+ case MRP_LUA_INTEGER_ARRAY: return "integer";
+ case MRP_LUA_DOUBLE_ARRAY: return "double";
+ case MRP_LUA_ANY: return "any";
+ default: return "<invalid array type>";
+ }
+}
+
+
+/** Collect, optionally dupping, all items from an assumed array @tidx. */
+int mrp_lua_object_collect_array(lua_State *L, int tidx, void **itemsp,
+ size_t *nitemp, int *expectedp, int dup,
+ char *e, size_t esize)
+{
+ const char *name, *str;
+ int ktype, vtype, ltype, i, expected, popnil;
+ size_t max, idx, isize;
+ void *items;
+
+ max = *nitemp;
+ tidx = mrp_lua_absidx(L, tidx);
+ items = *itemsp;
+ ltype = LUA_TNONE;
+ isize = 0;
+
+ expected = *expectedp;
+ popnil = false;
+
+ if (expected != MRP_LUA_ANY) {
+ ltype = array_lua_type(expected);
+ isize = array_item_size(expected);
+
+ if (ltype == LUA_TNONE || !isize)
+ goto type_error;
+ }
+
+ lua_pushnil(L);
+ popnil = true;
+ MRP_LUA_FOREACH_ALL(L, i, tidx, ktype, name, idx) {
+ vtype = lua_type(L, -1);
+
+ mrp_debug("collecting <%s>:<%s> element for %s array",
+ lua_typename(L, ktype), lua_typename(L, vtype),
+ array_type_name(expected));
+
+ if (ktype != LUA_TNUMBER)
+ goto not_pure;
+
+ if (expected == MRP_LUA_ANY) {
+ expected = array_murphy_type(vtype);
+
+ if (!expected)
+ goto type_error;
+
+ if (expected == MRP_LUA_INTEGER_ARRAY)
+ expected = MRP_LUA_DOUBLE_ARRAY; /* safer for ANY */
+
+ ltype = array_lua_type(expected);
+ isize = array_item_size(expected);
+ }
+ else
+ /* bail out for type mismatch (null is a valid string array) */
+ if (vtype != ltype &&
+ !(expected == MRP_LUA_STRING_ARRAY && vtype == LUA_TNIL))
+ goto type_error;
+
+ if (max != (size_t)-1 && i >= (int)max)
+ goto overflow;
+
+ if (dup && mrp_realloc(items, (i + 1) * isize) == NULL)
+ goto nomem;
+
+ switch (expected) {
+ case MRP_LUA_STRING_ARRAY:
+ str = (vtype != LUA_TNIL ? lua_tostring(L, -1) : NULL);
+ if (dup) {
+ ((char **)items)[i] = str ? mrp_strdup(str) : NULL;
+ if (!((char **)items)[i] && str)
+ goto nomem;
+ }
+ else
+ ((char **)items)[i] = (char *)str;
+ break;
+ case MRP_LUA_BOOLEAN_ARRAY:
+ ((bool *)items)[i] = lua_toboolean(L, -1);
+ break;
+ case MRP_LUA_INTEGER_ARRAY:
+ ((int32_t *)items)[i] = lua_tointeger(L, -1);
+ break;
+ case MRP_LUA_DOUBLE_ARRAY:
+ ((double *)items)[i] = lua_tonumber(L, -1);
+ break;
+ default:
+ goto type_error;
+ }
+ }
+ lua_pop(L, 1);
+
+ *itemsp = items;
+ *nitemp = i;
+ *expectedp = expected;
+
+ return 0;
+
+
+#define CLEANUP() do { \
+ mrp_lua_object_free_array(itemsp, nitemp, expected); \
+ if (popnil) lua_pop(L, 1); \
+ } while (0)
+
+ type_error:
+ CLEANUP(); return seterr(L, e, esize, "array or element of wrong type");
+ not_pure:
+ CLEANUP(); return seterr(L, e, esize, "not a pure array");
+ nomem:
+ CLEANUP(); return seterr(L, e, esize, "could not allocate array");
+ overflow:
+ CLEANUP(); return seterr(L, e, esize, "array too large");
+#undef CLEANUP
+}
+
+/** Free an array collected and duplicated by the collector above. */
+void mrp_lua_object_free_array(void **itemsp, size_t *nitemp, int type)
+{
+ size_t nitem = *nitemp;
+ char **saptr;
+ size_t i;
+
+ switch (type) {
+ case MRP_LUA_STRING_ARRAY:
+ saptr = *itemsp;
+ for (i = 0; i < nitem; i++)
+ mrp_free(saptr[i]);
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ mrp_free(*itemsp);
+ *itemsp = NULL;
+ *nitemp = 0;
+ break;
+ default:
+ break;
+ }
+}
+
+/** Push the given array of simple native C type on the stack. */
+int mrp_lua_object_push_array(lua_State *L, int type, void *items, size_t nitem)
+{
+ int i;
+
+ lua_createtable(L, nitem, 0);
+
+ for (i = 0; i < (int)nitem; i++) {
+ switch (type) {
+ case MRP_LUA_STRING_ARRAY:
+ lua_pushstring(L, ((char **)items)[i]);
+ break;
+ case MRP_LUA_BOOLEAN_ARRAY:
+ lua_pushboolean(L, ((bool *)items)[i]);
+ break;
+ case MRP_LUA_INTEGER_ARRAY:
+ lua_pushinteger(L, ((int32_t *)items)[i]);
+ break;
+ case MRP_LUA_DOUBLE_ARRAY:
+ lua_pushnumber(L, ((double *)items)[i]);
+ break;
+ default:
+ lua_pop(L, 1);
+ return -1;
+ }
+
+ lua_rawseti(L, -2, i + 1);
+ }
+
+ return 1;
+}
+
+/** Perform a setfield-like member assignment on the given object. */
+int mrp_lua_set_member(void *data, lua_State *L, char *err, size_t esize)
+{
+ userdata_t *u = DATA_TO_USER(data);
+ int midx = class_member(u, L, -2);
+ mrp_lua_class_member_t *m;
+ mrp_lua_value_t v;
+ int vtype, etype;
+ void *items;
+ size_t nitem;
+
+ if (midx < 0)
+ goto notfound;
+
+ mrp_lua_checkstack(L, -1);
+
+ m = u->def->members + midx;
+ vtype = lua_type(L, -1);
+
+ mrp_debug("setting %s.%s of Lua object %p(%p)", u->def->class_name,
+ m->name, u, data);
+
+ if (!u->initializing && (m->flags & MRP_LUA_CLASS_READONLY))
+ return seterr(L, err, esize, "%s.%s of Lua object is readonly",
+ u->def->class_name, m->name);
+
+ if (u->initializing && (m->flags & MRP_LUA_CLASS_NOINIT))
+ goto ok_noinit;
+
+ if (m->flags & MRP_LUA_CLASS_RAWSETTER) {
+ if (m->setter(data, L, midx, NULL) == 1)
+ goto ok;
+ else
+ goto error;
+ }
+
+ switch (m->type) {
+ case MRP_LUA_STRING:
+ if (vtype != LUA_TSTRING && vtype != LUA_TNIL)
+ return seterr(L, err, esize, "%s.%s expects string or nil, got %s",
+ u->def->class_name, m->name,
+ lua_typename(L, vtype), m->name);
+
+ v.str = lua_tostring(L, -1);
+
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+
+ case MRP_LUA_BOOLEAN:
+ v.bln = lua_toboolean(L, -1);
+
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+
+ case MRP_LUA_INTEGER:
+ if (vtype != LUA_TNUMBER)
+ return seterr(L, err, esize, "%s.%s expects number, got %s",
+ u->def->class_name, m->name, lua_typename(L, vtype));
+
+ v.s32 = lua_tointeger(L, -1);
+
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+
+ case MRP_LUA_DOUBLE:
+ if (vtype != LUA_TNUMBER)
+ return seterr(L, err, esize, "%s.%s expects number, got %s",
+ u->def->class_name, m->name, lua_typename(L, vtype));
+
+ v.dbl = lua_tonumber(L, -1);
+
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+
+ case MRP_LUA_CFUNC:
+ if (vtype != LUA_TFUNCTION && vtype != LUA_TNIL)
+ return seterr(L, err, esize, "%s.%s expects function, got %s",
+ u->def->class_name, m->name, lua_typename(L, vtype));
+ if (!lua_iscfunction(L, -1))
+ return seterr(L, err, esize, "%s.%s expects Lua C-function.",
+ u->def->class_name, m->name);
+ goto setfn;
+
+ case MRP_LUA_LFUNC:
+ if (vtype != LUA_TFUNCTION && vtype != LUA_TNIL)
+ return seterr(L, err, esize, "%s.%s expects function, got %s",
+ u->def->class_name, m->name, lua_typename(L, vtype));
+ if (lua_iscfunction(L, -1))
+ return seterr(L, err, esize, "%s.%s expects pure Lua function.",
+ u->def->class_name, m->name);
+ goto setfn;
+
+ case MRP_LUA_FUNC:
+ if (vtype != LUA_TFUNCTION && vtype != LUA_TNIL)
+ return seterr(L, err, esize, "%s.%s expects function, got %s",
+ u->def->class_name, m->name, lua_typename(L, vtype));
+
+ setfn:
+ v.lfn = mrp_lua_object_ref_value(data, L, -1);
+
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+
+ case MRP_LUA_BFUNC:
+ seterr(L, err, esize, "BFUNC is not implemented");
+ goto error;
+
+ case MRP_LUA_NULL:
+ seterr(L, err, esize, "setting member of invalid type NULL");
+ goto error;
+
+ case MRP_LUA_NONE:
+ seterr(L, err, esize, "setting member of invalid type NONE");
+ goto error;
+
+ case MRP_LUA_ANY:
+ v.any = mrp_lua_object_ref_value(data, L, -1);
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+
+ case MRP_LUA_STRING_ARRAY:
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ items = NULL;
+ nitem = (size_t)-1;
+ etype = m->type;
+ if (mrp_lua_object_collect_array(L, -1, &items, &nitem, &etype, true,
+ err, esize) < 0)
+ return -1;
+ else {
+ v.array.items = data + m->offs;
+ v.array.nitem64 = data + m->size;
+
+ *v.array.items = items;
+ if (m->sizew == 8)
+ *v.array.nitem64 = nitem;
+ else
+ *v.array.nitem32 = (uint32_t)nitem;
+ }
+ goto ok;
+
+ case MRP_LUA_OBJECT:
+ if (m->type_id == MRP_LUA_NONE)
+ m->type_id = class_by_type_name(m->type_name)->type_id;
+
+ if (m->type_id == MRP_LUA_NONE) {
+ seterr(L, err, esize, "can't set member of unknown type %s",
+ m->type_name);
+ goto error;
+ }
+
+ if (!mrp_lua_object_of_type(L, -1, m->type_id)) {
+ seterr(L, err, esize, "object type mismatch, expecting '%s'",
+ class_by_type(m->type_id)->type_name);
+ goto error;
+ }
+
+ v.obj.ref = mrp_lua_object_ref_value(data, L, -1);
+
+ lua_pushliteral(L, "userdata");
+ lua_rawget(L, -2);
+ if ((v.obj.ptr = lua_touserdata(L, -1)) != NULL) {
+ v.obj.ptr = *(void **)v.obj.ptr;
+ v.obj.ptr = USER_TO_DATA(v.obj.ptr);
+ }
+ lua_pop(L, 1);
+
+ if (m->setter(data, L, midx, &v) == 1)
+ goto ok;
+ else
+ goto error;
+ break;
+
+ default:
+ seterr(L, err, esize, "type %d not implemented");
+ break;
+ }
+
+ ok:
+ if ((m->flags & MRP_LUA_CLASS_NOTIFY) && u->def->notify)
+ u->def->notify(data, L, midx);
+ ok_noinit:
+ return 1;
+
+ notfound:
+ return 0;
+
+ error:
+ return -1;
+}
+
+/** Perform a getfield-like member-lookup on the given object. */
+int mrp_lua_get_member(void *data, lua_State *L, char *err, size_t esize)
+{
+ userdata_t *u = DATA_TO_USER(data);
+ mrp_lua_class_member_t *m;
+ mrp_lua_class_bridge_t *b;
+ int midx, bidx;
+ mrp_lua_value_t v;
+ void **items;
+ size_t *nitem;
+
+ mrp_lua_checkstack(L, -1);
+
+ if ((midx = class_member(u, L, -1)) >= 0)
+ m = u->def->members + midx;
+ else {
+ if ((bidx = class_bridge(u, L, -1)) >= 0) {
+ b = u->def->bridges + bidx;
+
+ return mrp_funcbridge_push(L, b->fb);
+ }
+
+ goto notfound;
+ }
+
+ if (m->getter(data, L, midx, &v) != 1)
+ goto error;
+
+ if (m->flags & MRP_LUA_CLASS_RAWGETTER)
+ goto ok;
+
+ switch (m->type) {
+ case MRP_LUA_STRING:
+ if (v.str != NULL)
+ lua_pushstring(L, v.str);
+ else
+ lua_pushnil(L);
+ goto ok;
+
+ case MRP_LUA_BOOLEAN:
+ lua_pushboolean(L, v.bln);
+ goto ok;
+
+ case MRP_LUA_INTEGER:
+ lua_pushinteger(L, v.s32);
+ goto ok;
+
+ case MRP_LUA_DOUBLE:
+ lua_pushnumber(L, v.dbl);
+ goto ok;
+
+ case MRP_LUA_FUNC:
+ mrp_lua_object_deref_value(data, L, v.lfn, true);
+ goto ok;
+
+ case MRP_LUA_LFUNC:
+ mrp_lua_object_deref_value(data, L, v.lfn, true);
+ goto ok;
+
+ case MRP_LUA_CFUNC:
+ mrp_lua_object_deref_value(data, L, v.lfn, true);
+ goto ok;
+
+ case MRP_LUA_BFUNC:
+ seterr(L, err, esize, "BFUNC is not implemented");
+ goto error;
+
+ case MRP_LUA_NULL:
+ lua_pushnil(L);
+ goto ok;
+
+ case MRP_LUA_NONE:
+ seterr(L, err, esize, "invalid type");
+ goto error;
+
+ case MRP_LUA_ANY:
+ mrp_lua_object_deref_value(data, L, v.any, true);
+ goto ok;
+
+ case MRP_LUA_STRING_ARRAY:
+ case MRP_LUA_BOOLEAN_ARRAY:
+ case MRP_LUA_INTEGER_ARRAY:
+ case MRP_LUA_DOUBLE_ARRAY:
+ items = data + m->offs;
+ nitem = data + m->size;
+ if (mrp_lua_object_push_array(L, m->type, *items, *nitem) > 0)
+ goto ok;
+ else {
+ seterr(L, err, esize, "failed to push array");
+ goto error;
+ }
+
+ case MRP_LUA_OBJECT:
+ mrp_lua_object_deref_value(data, L, v.obj.ref, true);
+ break;
+
+ default:
+ goto error;
+ }
+
+ ok:
+ return 1;
+
+ notfound:
+ return 0;
+
+ error:
+ return -1;
+}
+
+/** Perform a table-based member initialization for the given object. */
+int mrp_lua_init_members(void *data, lua_State *L, int idx,
+ char *err, size_t esize)
+{
+ userdata_t *u = userdata_get(data, CHECK);
+ const char *n;
+ size_t l;
+ int top, set;
+
+ if (err != NULL)
+ *err = '\0';
+
+ if (idx < 0)
+ idx = lua_gettop(L) + idx + 1;
+
+ if (lua_type(L, idx) != LUA_TTABLE)
+ return 0;
+
+ if (u->def->flags & MRP_LUA_CLASS_NOINIT) {
+ mrp_log_warning("Explicit table-based member initializer called for");
+ mrp_log_warning("object %s marked for NOINIT.", u->def->class_name);
+ }
+
+ top = lua_gettop(L);
+ u->initializing = true;
+ MRP_LUA_FOREACH_FIELD(L, idx, n, l) {
+ mrp_debug("initializing %s.%s", u->def->class_name, n);
+
+ lua_pushvalue(L, -2);
+ lua_pushvalue(L, -2);
+
+ switch (mrp_lua_set_member(data, L, err, esize)) {
+ case -1:
+ goto error;
+ case 0:
+ /*
+ * Okay, this was not an ordinary predefined member, so
+ * - pass this to setfield if we have one and this field is
+ * declared native, or no native fields have been declared
+ * - if there is no setfield or setfield said it does not
+ * handle this field, pass this to setext if the class is
+ * extensible
+ *
+ * Note that, in principle, we probably should treat it an error
+ * if setfield does not handle a field that has been declared
+ * native but currently we don't.
+ */
+
+ set = 0;
+
+ if (u->def->setfield && (!u->def->natives || is_native(u, n))) {
+ mrp_lua_push_object(L, data);
+ lua_insert(L, -3);
+ set = u->def->setfield(L);
+ lua_remove(L, -3);
+ }
+
+ if (set == 0 && (u->def->flags & MRP_LUA_CLASS_EXTENSIBLE))
+ set = object_setext(data, L, n, -1, NULL, 0);
+
+ if (set <= 0)
+ goto error;
+ break;
+ case 1:
+ break;
+ }
+ }
+ u->initializing = false;
+ lua_settop(L, top);
+ return 1;
+
+ error:
+ u->initializing = false;
+ lua_settop(L, top);
+ return -1;
+
+}
+
+
+static inline int is_native(userdata_t *u, const char *name)
+{
+ int i;
+
+ /*
+ * XXX TODO, ditto as for class_member() and class_bridge()
+ */
+
+ for (i = 0; i < u->def->nnative; i++)
+ if (u->def->natives[i][0] == name[0] &&
+ !strcmp(u->def->natives[i] + 1, name + 1))
+ return 1;
+
+ return 0;
+}
+
+
+static int override_setfield(lua_State *L)
+{
+ void *data = mrp_lua_check_object(L, NULL, 1);
+ userdata_t *u = DATA_TO_USER(data);
+ char err[128] = "";
+ const char *name;
+ int status;
+
+ if (data == NULL)
+ return luaL_error(L, "failed to find class userdata");
+
+ mrp_debug("setting field for object of type '%s'", u->def->class_name);
+
+ switch (mrp_lua_set_member(data, L, err, sizeof(err))) {
+ case 1: /* ok */
+ status = 0;
+ goto out;
+ case 0: /* field not found */
+ break;
+ default: /* error */
+ return luaL_error(L, "failed to set member (%s)", err);
+ }
+
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ name = lua_tostring(L, 2);
+ break;
+ case LUA_TNUMBER:
+ name = NULL;
+ break;
+ default:
+ return luaL_argerror(L, 2, "expecting string or integer");
+ }
+
+ luaL_checkany(L, 3);
+
+ /*
+ * Okay, this was not an ordinary predefined member, so
+ * - if this is a field (named vs. indexed member)
+ * * pass this to setfield if we have one and this field is
+ * declared native, or no native fields have been declared
+ * * if there is no setfield or setfield said it does not handle
+ * this field, pass this to setext if the class is extensible
+ * - otherwise (IOW an indexed member) pass this to setixet if the
+ * class is extensible
+ *
+ * Note that, in principle, we probably should treat it an error if
+ * setfield does not handle a field that has been declared native but
+ * currently we don't.
+ */
+
+ status = 0;
+
+ if (name != NULL) {
+ if (u->def->setfield && (!u->def->natives || is_native(u, name)))
+ status = u->def->setfield(L);
+
+ if (status == 0 && u->def->flags & MRP_LUA_CLASS_EXTENSIBLE)
+ status = object_setext(data, L, name, 3, NULL, 0);
+ }
+ else
+ status = object_setiext(data, L, lua_tointeger(L, 2), 3);
+
+ out:
+ return status;
+}
+
+
+static int override_getfield(lua_State *L)
+{
+ void *data = mrp_lua_check_object(L, NULL, 1);
+ userdata_t *u = DATA_TO_USER(data);
+ char err[128] = "";
+ const char *name;
+ int status;
+
+ mrp_debug("getting field for object of type '%s'", u->def->class_name);
+
+ switch (mrp_lua_get_member(data, L, err, sizeof(err))) {
+ case 1: /* ok */
+ status = 1;
+ goto out;
+ case 0: /* field not found */
+ break;
+ default: /* error */
+ return luaL_error(L, "failed to set member (%s)", err);
+ }
+
+ switch (lua_type(L, 2)) {
+ case LUA_TSTRING:
+ name = lua_tostring(L, 2);
+ break;
+ case LUA_TNUMBER:
+ name = NULL;
+ break;
+ default:
+ return luaL_argerror(L, 2, "expecting string or integer");
+ }
+
+ /*
+ * Okay, this was not an ordinary predefined member, so
+ * - if this is a field (named vs. indexed member)
+ * * pass this to setfield if we have one and this field is
+ * declared native, or no native fields have been declared
+ * * if there is no setfield or setfield said it does not handle
+ * this field, pass this to setext if the class is extensible
+ * - otherwise (IOW an indexed member) pass this to setixet if the
+ * class is extensible
+ *
+ * Note that, in principle, we probably should treat it an error if
+ * setfield does not handle a field that has been declared native but
+ * currently we don't.
+ */
+
+ status = 0;
+
+ if (name != NULL) {
+ if (u->def->getfield && (!u->def->natives || is_native(u, name)))
+ status = u->def->getfield(L);
+
+ if (status == 0 && u->def->flags & MRP_LUA_CLASS_EXTENSIBLE)
+ status = object_getext(data, L, name);
+ }
+ else
+ status = object_getiext(data, L, 2);
+
+ out:
+ return status;
+}
+
+
+static int override_tostring(lua_State *L)
+{
+ void *data;
+ userdata_t *u;
+ char buf[1024];
+
+ data = mrp_lua_check_object(L, NULL, 1);
+ u = DATA_TO_USER(data);
+
+ if (u != NULL) {
+ if (u->def->tostring == NULL ||
+ u->def->tostring(MRP_LUA_TOSTR_LUA, buf, sizeof(buf),
+ L, data) <= 0) {
+ snprintf(buf, sizeof(buf), "<%s)", __object(u, "*"));
+ }
+
+ lua_pushstring(L, buf);
+
+ return 1;
+ }
+ else {
+ snprintf(buf, sizeof(buf),
+ "<tostring called for invalid Murphy Lua object %p>", data);
+ return luaL_error(L, buf);
+ }
+
+}
+
+
+static ssize_t userdata_minimal_tostr(char *buf, size_t size, userdata_t *u)
+{
+ return snprintf(buf, size, "<%d:%p>", u->def->type_id, u);
+}
+
+
+static ssize_t userdata_compact_tostr(char *buf, size_t size, userdata_t *u)
+{
+ return snprintf(buf, size, "<%c:%s(%p)>",
+ (u->initializing ? 'I' : (u->dead ? 'D' : 'a')),
+ u->def->type_name, u);
+}
+
+
+static ssize_t userdata_oneline_tostr(char *buf, size_t size, userdata_t *u)
+{
+ void *data = USER_TO_DATA(u);
+
+ return snprintf(buf, size, "<%c:%s(%p/%p)>",
+ (u->initializing ? 'I' : (u->dead ? 'D' : 'a')),
+ u->def->type_name, u, data);
+}
+
+
+static ssize_t userdata_short_tostr(char *buf, size_t size, userdata_t *u)
+{
+ return userdata_oneline_tostr(buf, size, u);
+}
+
+
+static ssize_t userdata_medium_tostr(char *buf, size_t size, userdata_t *u)
+{
+ return userdata_oneline_tostr(buf, size, u);
+}
+
+
+static ssize_t userdata_full_tostr(char *buf, size_t size, userdata_t *u)
+{
+ return userdata_oneline_tostr(buf, size, u);
+}
+
+
+static ssize_t userdata_verbose_tostr(char *buf, size_t size, userdata_t *u)
+{
+ return userdata_oneline_tostr(buf, size, u);
+}
+
+
+static ssize_t userdata_tostr(mrp_lua_tostr_mode_t mode, char *buf, size_t size,
+ userdata_t *u)
+{
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_MINIMAL: return userdata_minimal_tostr(buf, size, u);
+ default:
+ case MRP_LUA_TOSTR_COMPACT: return userdata_compact_tostr(buf, size, u);
+ case MRP_LUA_TOSTR_ONELINE: return userdata_oneline_tostr(buf, size, u);
+ case MRP_LUA_TOSTR_SHORT: return userdata_short_tostr (buf, size, u);
+ case MRP_LUA_TOSTR_MEDIUM: return userdata_medium_tostr (buf, size, u);
+ case MRP_LUA_TOSTR_FULL: return userdata_full_tostr (buf, size, u);
+ case MRP_LUA_TOSTR_VERBOSE: return userdata_verbose_tostr(buf, size, u);
+ }
+
+ return 0;
+}
+
+
+/** Dump the given object to the provided buffer. */
+ssize_t mrp_lua_object_tostr(mrp_lua_tostr_mode_t mode, char *buf, size_t size,
+ lua_State *L, void *data)
+{
+ userdata_t *u = userdata_get(data, CHECK);
+ char *p = buf;
+ ssize_t n = 0;;
+
+ if (u == NULL)
+ return snprintf(p, size, "<non-object %p>", u);
+
+ if (mode & MRP_LUA_TOSTR_META) {
+ n = userdata_tostr(mode, p, size, u);
+
+ if (n <= 0)
+ goto error;
+ if ((size_t)n >= size)
+ goto overflow;
+ }
+
+ if (mode & MRP_LUA_TOSTR_DATA) {
+ p += (size_t)n;
+ size -= (size_t)n;
+
+ if (u->def->tostring != NULL) {
+ n = u->def->tostring(mode, p, size, L, data);
+
+ if (n < 0)
+ goto error;
+ if ((size_t)n >= size)
+ goto overflow;
+ }
+ }
+
+ return (ssize_t)(p + n - buf);
+
+ overflow:
+ return (ssize_t)(p + n - buf);
+
+ error:
+ return n;
+}
+
+
+/** Dump the object at the given stack location to the provided buffer. */
+ssize_t mrp_lua_index_tostr(mrp_lua_tostr_mode_t mode, char *buf, size_t size,
+ lua_State *L, int index)
+{
+ userdata_t **userdatap;
+
+ userdatap = (userdata_t **)lua_touserdata(L, index);
+
+ if (userdatap != NULL)
+ return mrp_lua_object_tostr(mode, buf, size, L, *userdatap);
+ else
+ return snprintf(buf, size, "<invalid object, no userdata>");;
+}
+
+
+/** Dump all live or zombie Lua objects. */
+void mrp_lua_dump_objects(mrp_lua_tostr_mode_t mode, lua_State *L, FILE *fp)
+{
+ mrp_lua_classdef_t *def;
+ mrp_list_hook_t *p, *n;
+ userdata_t *u;
+ void *d;
+ int i, cnt;
+ char active[64], dead[64], created[64], destroyed[64];
+ char obj[4096];
+
+ fprintf(fp, "Lua memory usage: %d k, %.2f M\n",
+ lua_gc(L, LUA_GCCOUNT, 0), lua_gc(L, LUA_GCCOUNT, 0) / 1024.0);
+
+ fprintf(fp, "Objects by class/type: A=active, D=dead, "
+ "c=created, d=destroyed\n");
+ for (i = 0; i < nclassdef; i++) {
+ def = classdefs[i];
+
+ snprintf(active, sizeof(active), "%u", def->nactive);
+ snprintf(dead, sizeof(dead), "%u", def->ndead);
+ snprintf(created, sizeof(created), "%u", def->ncreated);
+ snprintf(destroyed, sizeof(destroyed), "%u", def->ndestroyed);
+
+ fprintf(fp, "<%s/%s>: A:%s, D:%s, c:%s, d:%s\n",
+ def->class_name, def->type_name,
+ active, dead, created, destroyed);
+
+ cnt = 0;
+ mrp_list_foreach(&def->objects, p, n) {
+ u = mrp_list_entry(p, typeof(*u), hook[0]);
+ d = USER_TO_DATA(u);
+
+ if (mrp_lua_object_tostr(mode, obj, sizeof(obj), L, d) > 0)
+ fprintf(fp, " #%d: %s\n", cnt, obj);
+ else
+ fprintf(fp, " failed to dump object %p(%p)\n", u, d);
+ cnt++;
+ }
+ }
+}
+
+
+void mrp_lua_track_objects(bool enable)
+{
+ if (cfg.busy) {
+ if (cfg.track != enable)
+ mrp_log_warning("Can't %s Murphy Lua object tracking, "
+ "already in use.", enable ? "enable" : "disable");
+ return;
+ }
+
+ if (!cfg.set) {
+ cfg.track = enable;
+ cfg.set = true;
+
+ mrp_log_info("Murphy Lua object tracking is now %s.",
+ enable ? "enabled" : "disabled");
+ }
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_OBJECT_H__
+#define __MURPHY_LUA_OBJECT_H__
+
+#include <stdbool.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include "murphy/common/list.h"
+#include "murphy/core/lua-utils/lua-utils.h"
+
+#define MRP_LUA_CONFIG_ENVVAR "__MURPHY_LUA_CONFIG"
+
+#define MRP_LUA_ENTER \
+ mrp_debug("enter")
+
+#define MRP_LUA_LEAVE(_v) \
+ do { \
+ mrp_debug("leave (%d)", (_v)); \
+ return (_v); \
+ } while (0)
+
+#define MRP_LUA_LEAVE_NOARG \
+ mrp_debug("leave")
+
+#define MRP_LUA_LEAVE_ERROR(L, fmt, args...) \
+ luaL_error(L, fmt , ## args)
+
+#define MRP_LUA_CLASSID_ROOT "LuaBook."
+
+#define MRP_LUA_CLASS(_name, _constr) & _name ## _ ## _constr ## _class_def
+#define MRP_LUA_CLASS_SIMPLE(_name) & _name ## _class_def
+
+#define MRP_LUA_METHOD_LIST(...) { __VA_ARGS__ {NULL, NULL}}
+
+#define MRP_LUA_METHOD(_name, _func) { # _name, _func } ,
+#define MRP_LUA_METHOD_CONSTRUCTOR(_func) { "new", _func } ,
+
+#define MRP_LUA_OVERRIDE_GETFIELD(_func) { "__index", _func } ,
+#define MRP_LUA_OVERRIDE_SETFIELD(_func) { "__newindex", _func } ,
+#define MRP_LUA_OVERRIDE_CALL(_func) { "__call", _func } ,
+#define MRP_LUA_OVERRIDE_STRINGIFY(_func) { "__tostring", _func } ,
+#define MRP_LUA_OVERRIDE_GETLENGTH(_func) { "__len", _func } ,
+
+#define MRP_LUA_METHOD_LIST_TABLE(_name, ... ) \
+ static luaL_reg _name[] = { \
+ __VA_ARGS__ \
+ { NULL, NULL } \
+ }
+
+#define MRP_LUA_CLASS_NAME(_name) #_name
+#define MRP_LUA_CLASS_ID(_name, _constr) MRP_LUA_CLASSID_ROOT#_name"_"#_constr
+#define MRP_LUA_UDATA_ID(_name, _constr) \
+ MRP_LUA_CLASSID_ROOT#_name"."#_constr".userdata"
+
+#define MRP_LUA_TYPE_ID(_class_def) ((_class_def)->type_id)
+#define MRP_LUA_TYPE_NAME(_class_def) ((_class_def)->type_name)
+
+#define MRP_LUA_CLASS_DEF(_name, _constr, _type, _destr, _methods, _overrides)\
+ static mrp_lua_classdef_t _name ## _ ## _constr ## _class_def = { \
+ .class_name = MRP_LUA_CLASS_NAME(_name), \
+ .class_id = MRP_LUA_CLASS_ID(_name, _constr), \
+ .constructor = # _name "." # _constr, \
+ .destructor = _destr, \
+ .type_name = #_type, \
+ .type_id = -1, \
+ .userdata_id = MRP_LUA_UDATA_ID(_name, _constr), \
+ .userdata_size = sizeof(_type), \
+ .methods = _methods, \
+ .overrides = _overrides, \
+ .members = NULL, \
+ .nmember = 0, \
+ .natives = NULL, \
+ .nnative = 0, \
+ .notify = NULL, \
+ .flags = 0, \
+ }
+
+#define MRP_LUA_CLASS_DEF_SIMPLE(_name, _type, _destr, _methods, _overrides) \
+ static luaL_reg _name ## _class_methods[] = _methods; \
+ static luaL_reg _name ## _class_overrides[] = _overrides; \
+ \
+ static mrp_lua_classdef_t _name ## _class_def = { \
+ .class_name = MRP_LUA_CLASS_NAME(_name), \
+ .class_id = MRP_LUA_CLASS_ID(_name, _constr), \
+ .constructor = # _name, \
+ .destructor = _destr, \
+ .type_name = #_type, \
+ .type_id = -1, \
+ .userdata_id = MRP_LUA_UDATA_ID(_name, _constr), \
+ .userdata_size = sizeof(_type), \
+ .methods = _name ## _class_methods, \
+ .overrides = _name ## _class_overrides, \
+ .members = NULL, \
+ .nmember = 0, \
+ .natives = NULL, \
+ .nnative = 0, \
+ .notify = NULL, \
+ .flags = 0, \
+ }
+
+#define MRP_LUA_CLASS_DEF_FLAGS(_name, _constr, _type, _destr, _methods, \
+ _overrides, _class_flags) \
+ static mrp_lua_classdef_t _name ## _ ## _constr ## _class_def = { \
+ .class_name = MRP_LUA_CLASS_NAME(_name), \
+ .class_id = MRP_LUA_CLASS_ID(_name, _constr), \
+ .constructor = # _name "." # _constr, \
+ .destructor = _destr, \
+ .type_name = #_type, \
+ .type_id = -1, \
+ .userdata_id = MRP_LUA_UDATA_ID(_name, _constr), \
+ .userdata_size = sizeof(_type), \
+ .methods = _methods, \
+ .overrides = _overrides, \
+ .members = NULL, \
+ .nmember = 0, \
+ .natives = NULL, \
+ .nnative = 0, \
+ .notify = NULL, \
+ .flags = _class_flags, \
+ }
+
+
+#define MRP_LUA_CLASS_DEF_SIMPLE_FLAGS(_name, _type, _destr, _methods, \
+ _overrides, _class_flags) \
+ static luaL_reg _name ## _class_methods[] = _methods; \
+ static luaL_reg _name ## _class_overrides[] = _overrides; \
+ \
+ static mrp_lua_classdef_t _name ## _class_def = { \
+ .class_name = MRP_LUA_CLASS_NAME(_name), \
+ .class_id = MRP_LUA_CLASS_ID(_name, _constr), \
+ .constructor = # _name, \
+ .destructor = _destr, \
+ .type_name = #_type, \
+ .type_id = -1, \
+ .userdata_id = MRP_LUA_UDATA_ID(_name, _constr), \
+ .userdata_size = sizeof(_type), \
+ .methods = _name ## _class_methods, \
+ .overrides = _name ## _class_overrides, \
+ .members = NULL, \
+ .nmember = 0, \
+ .natives = NULL, \
+ .nnative = 0, \
+ .notify = NULL, \
+ .flags = _class_flags, \
+ }
+
+
+#define MRP_LUA_FOREACH_FIELD(_L, _i, _n, _l) \
+ for (lua_pushnil(_L); \
+ \
+ !(_l = 0) && lua_next(_L, _i) && \
+ (_n = (lua_type(_L, -2) == LUA_TSTRING) ? \
+ lua_tolstring(_L, -2, &_l) : "" ); \
+ \
+ lua_pop(_L, 1))
+
+
+/** _i=loopcount, _t=table idx, _type=key type, _n=field, _l=len or idx */
+#define MRP_LUA_FOREACH_ALL(_L, _i, _t, _type, _n, _l) \
+ for (lua_pushnil(_L), _i = 0; \
+ \
+ (lua_next(_L, _t) && \
+ (((_type = lua_type(_L, -2)) == LUA_TSTRING ? \
+ (_n = lua_tolstring(_L, -2, &_l)) : (_n = NULL)) || \
+ ((_type == LUA_TNIL ? \
+ !(_n = NULL, _l = 0) : \
+ (_type == LUA_TNUMBER ? \
+ (_n = NULL, _l = lua_tointeger(_L, -2)) : \
+ !(_n = NULL, _l = 0)) || \
+ _type != LUA_TSTRING)))); \
+ \
+ lua_pop(_L, 1), _i++)
+
+
+enum mrp_lua_event_type_e {
+ MRP_LUA_OBJECT_DESTRUCTION = 1,
+};
+
+
+/*
+ * stringification (debugging and Lua __tostring)
+ */
+
+/**
+ * tostring/stringification mode
+ */
+typedef enum {
+ /* verbosity modifiers */
+ MRP_LUA_TOSTR_LUA = 0x000, /* native Lua tostr */
+ MRP_LUA_TOSTR_MINIMAL = 0x010, /* minimal useful output */
+ MRP_LUA_TOSTR_COMPACT = 0x020, /* compact (sub-oneline) output */
+ MRP_LUA_TOSTR_ONELINE = 0x040, /* all output that fits into a line */
+ MRP_LUA_TOSTR_SHORT = 0x080, /* shortest multiline output */
+ MRP_LUA_TOSTR_MEDIUM = 0x100, /* medium multiline output */
+ MRP_LUA_TOSTR_FULL = 0x200, /* full object output */
+ MRP_LUA_TOSTR_VERBOSE = 0x400, /* dump all data imaginable */
+ MRP_LUA_TOSTR_MODEMASK = 0xff0, /* dump mode mask */
+
+ /* content modifiers */
+ MRP_LUA_TOSTR_META = 0x001, /* show metadata (userdata_t) part */
+ MRP_LUA_TOSTR_DATA = 0x002, /* show user-visible data */
+ MRP_LUA_TOSTR_BOTH = 0x003, /* both meta- and user-visible data */
+} mrp_lua_tostr_mode_t;
+
+/** Default configuration. */
+#define MRP_LUA_TOSTR_DEFAULT (MRP_LUA_TOSTR_COMPACT | MRP_LUA_TOSTR_META)
+
+/** Stack-dump configuration. */
+#define MRP_LUA_TOSTR_STACKDUMP (MRP_LUA_TOSTR_MINIMAL | MRP_LUA_TOSTR_META)
+
+/** Stack-dump configuration for errors. */
+#define MRP_LUA_TOSTR_ERRORDUMP (MRP_LUA_TOSTR_ONELINE | MRP_LUA_TOSTR_BOTH)
+
+/** Stack-dump confguration for object-tracking check dump. */
+#define MRP_LUA_TOSTR_CHECKDUMP (MRP_LUA_TOSTR_ONELINE | MRP_LUA_TOSTR_BOTH)
+
+/** Type of an object stringification handler. */
+typedef ssize_t (*mrp_lua_tostr_t)(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+
+/** Dump the given object to the provided buffer. */
+ssize_t mrp_lua_object_tostr(mrp_lua_tostr_mode_t mode, char *buf, size_t size,
+ lua_State *L, void *data);
+
+/** Dump the object at the given stack location to the provided buffer. */
+ssize_t mrp_lua_index_tostr(mrp_lua_tostr_mode_t mode, char *buf, size_t size,
+ lua_State *L, int index);
+
+/*
+ * pre-declared class members
+ */
+
+typedef struct mrp_lua_class_member_s mrp_lua_class_member_t;
+typedef union mrp_lua_value_u mrp_lua_value_t;
+typedef int (*mrp_lua_setter_t)(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+typedef int (*mrp_lua_getter_t)(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+
+/*
+ * class member/extension flags
+ */
+
+typedef enum {
+ MRP_LUA_CLASS_NOFLAGS = 0x000, /* empty flags */
+ MRP_LUA_CLASS_EXTENSIBLE = 0x001, /* class is user-extensible from Lua */
+ MRP_LUA_CLASS_READONLY = 0x002, /* class or member is readonly */
+ MRP_LUA_CLASS_NOTIFY = 0x004, /* notify when member is changed */
+ MRP_LUA_CLASS_NOINIT = 0x008, /* don't initialize member */
+ MRP_LUA_CLASS_NOOVERRIDE = 0x010, /* don't override setters, getters */
+ MRP_LUA_CLASS_RAWGETTER = 0x020, /* getter pushes to the stack */
+ MRP_LUA_CLASS_RAWSETTER = 0x040, /* setter takes args from the stack */
+ MRP_LUA_CLASS_USESTACK = 0x080, /* autobridged method uses the stack */
+ MRP_LUA_CLASS_DYNAMIC = 0x100, /* allow dynamic GC for this class */
+} mrp_lua_class_flag_t;
+
+/*
+ * getter/setter statuses and macros
+ */
+
+#define MRP_LUA_OK_ 1 /* successfully get/set */
+#define MRP_LUA_NOTFOUND 0 /* member not found */
+#define MRP_LUA_FAIL -1 /* failed to get/set member */
+
+/*
+ * supported class member types
+ */
+
+#define MRP_LUA_VBASE (LUA_TTHREAD + 1)
+#define MRP_LUA_VMAX 8192
+#define MRP_LUA_VTYPE(idx) (MRP_LUA_VBASE + (idx))
+
+typedef enum {
+ MRP_LUA_NONE = LUA_TNONE, /* not a valid type */
+ MRP_LUA_NULL = LUA_TNIL, /* don't use, nil */
+
+ MRP_LUA_BOOLEAN = LUA_TBOOLEAN, /* boolean member */
+ MRP_LUA_STRING = LUA_TSTRING, /* string member */
+ MRP_LUA_DOUBLE = LUA_TNUMBER, /* double member */
+ MRP_LUA_FUNC = LUA_TFUNCTION, /* any Lua function member */
+ MRP_LUA_INTEGER = MRP_LUA_VTYPE(0), /* integer member */
+ MRP_LUA_LFUNC = MRP_LUA_VTYPE(1), /* pure Lua function member */
+ MRP_LUA_CFUNC = MRP_LUA_VTYPE(2), /* C function member */
+ MRP_LUA_BFUNC = MRP_LUA_VTYPE(3), /* bridged function member */
+
+ MRP_LUA_BOOLEAN_ARRAY = MRP_LUA_VTYPE(4),
+ MRP_LUA_STRING_ARRAY = MRP_LUA_VTYPE(5),
+ MRP_LUA_INTEGER_ARRAY = MRP_LUA_VTYPE(6),
+ MRP_LUA_DOUBLE_ARRAY = MRP_LUA_VTYPE(7),
+
+ MRP_LUA_ANY = MRP_LUA_VTYPE(8), /* member of any type */
+ MRP_LUA_OBJECT = MRP_LUA_VTYPE(9), /* object member */
+ /* dynamically registered types */
+ MRP_LUA_MAX = MRP_LUA_VTYPE(MRP_LUA_VMAX)
+} mrp_lua_type_t;
+
+
+/*
+ * a generic class member value
+ */
+
+union mrp_lua_value_u {
+ const char *str; /* string value */
+ bool bln; /* boolean value */
+ int32_t s32; /* integer value */
+ double dbl; /* double value */
+ int lfn; /* Lua function reference value */
+ void *bfn; /* bridged function value */
+ int any; /* Lua reference */
+ struct { /* array value */
+ void **items; /* array items */
+ union {
+ size_t *nitem64; /* number of items */
+ int *nitem32; /* number of items */
+ };
+ } array;
+ struct {
+ int ref; /* object reference */
+ void *ptr; /* object pointer */
+ } obj;
+};
+
+
+/*
+ * a class member descriptor
+ */
+
+struct mrp_lua_class_member_s {
+ char *name; /* member name */
+ mrp_lua_type_t type; /* member type */
+ size_t offs; /* offset within type buffer */
+ size_t size; /* offset to size within type buffer */
+ size_t sizew; /* width of size within type buffer */
+ const char *type_name; /* object type name */
+ mrp_lua_type_t type_id; /* object type id */
+ mrp_lua_setter_t setter; /* setter if any */
+ mrp_lua_getter_t getter; /* getter if any */
+ int flags; /* member flags */
+};
+
+
+
+/**
+ * Murphy Lua Object Infrastructure
+ *
+ * The Murphy Lua object infrastructure allow you to declare and define
+ * the C implementation of a Lua class, along with a set of explicitly
+ * and or implicitly defined class members and class methods.
+ *
+ * Implicitly-defined members and methods - direct full control
+ *
+ * With implicitly defined members and methods you have almost absolute
+ * control over what members and methods, when and how your C object
+ * backend exposes to the Lua runtime, with minimal intervention from
+ * the Murphy object infrastructure.
+ *
+ * You can almost freely override any method of your Lua object. Ignoring
+ * some Lua-intrinsic details about raw versus ordinary member lookup, in
+ * practice overriding a method causes your corresponding C handler to be
+ * invoked whenever the Lua runtime makes a call to a member you have
+ * overridden in the Murphy Lua class definition of the object.
+ *
+ * A basic technique to gain almost full control of the behavior of your
+ * objects exposed behavior is simply to override the getfield (Lua
+ * __index) and setfield (Lua __newindex) meta-methods in the objects
+ * class definition. This way whenever someone tries to fetch, or set
+ * a specific member in your object you will be called backed to either
+ * provide the associated data, for getfield, or take the provided data
+ * and associate it with the supplied field name or index, for setfield.
+ *
+ * In your overridden C callbacks you take care of all the necessary
+ * actions of pushing the values corresponfing to fetched fields/indices
+ * and storing values associated with set fields or indices. You can do
+ * value, object state, accessibility (read-write) checks or any other
+ * check you see necessary and reject the operation by returning an error,
+ * or throwing an exception to the Lua runtime.
+ *
+ * Explicitly-defined members and methods - less control, but less code
+ *
+ * With explicitly defined members and/or methods, you specify the
+ * members and/or methods for your object class along with metainformation
+ * such as read-write/readonly status, write/update notifications,
+ * dedicated member-specific getter/setter callbacks, etc. and let the
+ * infrastructure take care of the details of passing information between
+ * your objects and the Lua interpreter. In the case of explicitly-
+ * defined members/methods, the Murphy Lua object infrastructure still
+ * offers a certain degree of flexibility about the details of how you
+ * want to handle specific members/methods. You can configure these by
+ * setting flags, each representing a certain option, on the member or
+ * for some flags on the full class.
+ *
+ * To provide a good balance between maximum flexibility and maximum
+ * control, the infrastructure allows you to mix and match pretty
+ * explicit and implicit member/method control pretty freely. You can
+ * declare parts of your members (typically the regularly behaving easy
+ * ones) explicitly, while handle the hairy ones (or those that are
+ * difficult or impossible to map to C by the metadata) with your own
+ * overridden getfield and setfield handlers. Which approach you take is
+ * fully up to you and you are very much encouraged to experiment with
+ * both to understand the limitations vs. conveniences presented by
+ * each in practical terms.
+ *
+ * As stated earlier, in pratice, you can select how much assistance you
+ * want from the infra on a per-member basis. You can relatively freely
+ * choose between fully automatic handling, where you do not need to do
+ * much apart from providing the corret metadata describing your object,
+ * and fully manual handling where you need to take care of almost all
+ * details of setting and retrieval (setfield/getfield).
+ *
+ * Occasionally the infra has technical limitations and if you hit any of
+ * these you have no choice but take care of the details yourself. Usually
+ * such limitations should not exist, however, and the chosen level of
+ * detail is mostly dictated by how complex rules or constrains a class
+ * member needs to obey. For instance, scalar members (strings, numbers,
+ * booleans, etc.) and functions without much restrictions you can let be
+ * handled automatically. Members with semantic restrictions, such as a
+ * restricted range of acceptable values, or with contextual dependencies,
+ * for instance dependencies on the values of other class members, you need
+ * to handle with a varying level of detail yourself.
+ *
+ * Even in the more complex cases, there are a few facilities offered by
+ * the infrastructure which were designed to let you get along in as many
+ * of the cases as possibly without having to write much extra code.
+ *
+ * Automatic Members (perhaps should be called automatic explicit members):
+ *
+ * 1. Automatic (maybe we should call them explicit) class members
+ * You can declare a member with its name, type, storage offset,
+ * and let the infra take care of setting and retrieving it.
+ *
+ * 2. Read-only Members
+ * You can mark members (or a full class) read-only. Read-only members
+ * can only be set from C. Trying to set a read-only member from Lua
+ * will raise a runtime exception.
+ *
+ * 3. Change Notifications
+ * You can request change notifications on a per-member basis. Whenever
+ * a member with notifications on is changed from Lua, a class-wide
+ * notification callback is invoked. You can check the newly set value
+ * of the member and take any actions necessary to reflect the updated
+ * value of the member. For instance, when setting a 'disabled' member
+ * to true, you might disable the normal actions you class instance is
+ * performing. Note that currently the notification is called back only
+ * after the member has been updated and the callback has no return
+ * value. IOW, there is no straightforward way to reject a change from
+ * the notification callback (although, you can restore the old value
+ * from a saved copy if the new one is not kosher and then raise a Lua
+ * exception). However, this is still subject to change and probably
+ * we'll change notification callbacks to receive both the old and new
+ * values and to return a accepted/rejected/I-ve-taken-care-of-it type
+ * of verdict.
+ *
+ * 4. Optional Getters and Setters
+ * You can set on a per-members basis a getter, a setter, or both. If
+ * a member has a getter it will be invoked instead of the common default
+ * getter when the member is read/retrieved. Similary, when a member has
+ * a setter this will be called instead of the default setter when the
+ * value of the member is set.
+ *
+ * 5. Native (Fully Manual) Members
+ * You can declare a set of members native (perhaps manual members would
+ * be a bit more descriptive term). These will always be handed to your
+ * getfield/setfield class methods so you need to take care of every
+ * detail of setting and retrieving these variables.
+ *
+ * 6. Extended Members
+ * You can mark your class extensible at will. If you do so, your users
+ * will be able to extend your class from Lua by simply setting other
+ * members than the ones you have declared for the class. This allows
+ * easy duck-typing and extensions to your class be implemented in a
+ * straightforward manner usually without complex layers of wrappers.
+ *
+ * Classes with Mixed Members
+ *
+ * You can quite freely mix and match automatic (both with and without
+ * getters and/or setters), fully manual, and extended class members,
+ * although the dominant usage tends to be to mix only automatic and
+ * extended members and keep manual classes strictly as such.
+ */
+
+/** Macro to define a Murphy Lua class. */
+#define MRP_LUA_DEFINE_CLASS(_name, _constr, _type, _destr, \
+ _methods, _overrides, _members, \
+ _blacklist, _notify, _tostring, \
+ _bridges, _class_flags) \
+ static mrp_lua_classdef_t _name ## _ ## _constr ## _class_def = { \
+ .class_name = MRP_LUA_CLASS_NAME(_name), \
+ .class_id = MRP_LUA_CLASS_ID(_name, _constr), \
+ .constructor = # _name "." # _constr, \
+ .destructor = _destr, \
+ .type_name = #_type, \
+ .type_id = -1, \
+ .userdata_id = MRP_LUA_UDATA_ID(_name, _constr), \
+ .userdata_size = sizeof(_type), \
+ .methods = _methods, \
+ .overrides = _overrides, \
+ .members = _members, \
+ .nmember = _members == NULL ? 0 : MRP_ARRAY_SIZE(_members), \
+ .natives = _blacklist, \
+ .nnative = _blacklist==NULL ? 0:MRP_ARRAY_SIZE(_blacklist), \
+ .bridges = _bridges, \
+ .nbridge = _bridges == NULL ? 0 : MRP_ARRAY_SIZE(_bridges), \
+ .notify = _notify, \
+ .tostring = _tostring, \
+ .flags = _class_flags, \
+ }
+
+/** Macro to declare the list of class members. */
+#define MRP_LUA_MEMBER_LIST_TABLE(_name, ...) \
+ static mrp_lua_class_member_t _name[] = { __VA_ARGS__ }
+
+/**
+ * Generic generic macro to declare an automatic member for a class.
+ *
+ * @param _type member type
+ * @param _name member name in Lua
+ * @param _offs member offset with visible part of userdata (if relevant)
+ * @param _set optional member-specific setter
+ * @param _get optional member-specific getter
+ * @param _flags member flags, bitwise or of MRP_LUA_CLASS_READONLY,
+ * MRP_LUA_CLASS_NOTIFY, and MRP_LUA_CLASS_NOINIT. Also
+ * MRP_LUA_CLASS_NOFLAGS is available to denote the empty
+ * set of flags.
+ */
+#define MRP_LUA_CLASS_MEMBER(_type, _name, _offs, _set, _get, _flags) \
+ \
+ .name = _name, \
+ .type = _type, \
+ .offs = _offs, \
+ .setter = _set, \
+ .getter = _get, \
+ .flags = _flags, \
+
+/*
+ * type-specific convenience macros
+ */
+
+/** Declare an automatic string member. */
+#define MRP_LUA_CLASS_STRING(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_STRING, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic (signed 32-bit) integer member. */
+#define MRP_LUA_CLASS_INTEGER(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_INTEGER, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic double-precision floating point member. */
+#define MRP_LUA_CLASS_DOUBLE(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_DOUBLE, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic boolean member. */
+#define MRP_LUA_CLASS_BOOLEAN(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_BOOLEAN, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic Lua function member. */
+#define MRP_LUA_CLASS_LFUNC(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_LFUNC, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic C function member. */
+#define MRP_LUA_CLASS_CFUNC(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_CFUNC, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic member with a value of any acceptable type. */
+#define MRP_LUA_CLASS_ANY(_name, _offs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_ANY, _name, _offs, _set, _get, _flags)},
+
+/** Declare an automatic array and size member of the given type. */
+#define MRP_LUA_CLASS_ARRAY(_name, _type, _ctype, _p, _n, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_##_type##_ARRAY, _name, \
+ MRP_OFFSET(_ctype, _p), _set, _set, _flags) \
+ .size = MRP_OFFSET(_ctype, _n), \
+ .sizew = sizeof(((_ctype *)NULL)->_n) },
+
+/** Declare an automatic object and reference member of the given type. */
+#define MRP_LUA_CLASS_OBJECT(_name, _type, _poffs, _roffs, _set, _get, _flags) \
+ {MRP_LUA_CLASS_MEMBER(MRP_LUA_OBJECT, _name, _poffs, _set, _get, \
+ _flags) \
+ .size = _roffs, \
+ .type_name = #_type, \
+ .type_id = -1 },
+
+
+#include "murphy/core/lua-utils/funcbridge.h"
+
+/*
+ * a bridged class method descriptor
+ */
+
+typedef struct {
+ char *name; /* function name */
+ const char *signature; /* function signature */
+ union {
+ mrp_funcbridge_cfunc_t fc; /* C function to bridge to */
+ mrp_funcbridge_t *fb; /* function bridge */
+ };
+ int flags; /* method flags */
+} mrp_lua_class_bridge_t;
+
+
+/** Macro to declare the list of bridged class methods. */
+#define MRP_LUA_BRIDGE_LIST_TABLE(_name, ...) \
+ static mrp_lua_class_bridge_t _name[] = { __VA_ARGS__ }
+
+/**
+ * Generic generic macro to declare a bridged method for a class.
+ *
+ * @param _name member name in Lua for this method
+ * @param _signature signature of this method (see funcbridge.h)
+ * @param _fn bridged function to be invoked for this method
+ * @param _flags member flags, currently either MRP_LUA_CLASS_NOFLAGS,
+ * or MRP_LUA_CLASS_USESTACK.
+ */
+
+#define MRP_LUA_CLASS_BRIDGE(_method, _signature, _fn, _flags) \
+ { \
+ .name = _method, \
+ .signature = _signature, \
+ .fc = _fn, \
+ .flags = _flags, \
+ }
+
+
+#define MRP_LUA_CLASS_CHECKER(_type, _prefix, _class) \
+ static _type *_prefix##_check(lua_State *L, int idx) \
+ { \
+ return (_type *)mrp_lua_check_object(L, _class, idx); \
+ } struct _prefix##_kludge_for_trailing_semicolon
+
+
+typedef struct mrp_lua_classdef_s mrp_lua_classdef_t;
+typedef enum mrp_lua_event_type_e mrp_lua_event_type_t;
+
+typedef void (*mrp_lua_class_notify_t)(void *data, lua_State *L, int member);
+
+
+struct mrp_lua_classdef_s {
+ const char *class_name;
+ const char *class_id;
+ const char *constructor;
+ void (*destructor)(void *);
+ const char *type_name;
+ int type_id;
+ const void *type_meta;
+ const char *userdata_id;
+ size_t userdata_size;
+ luaL_reg *methods;
+ luaL_reg *overrides;
+
+ mrp_lua_tostr_t tostring; /* stringification handler */
+ mrp_list_hook_t objects; /* instances of this class */
+ uint32_t ncreated; /* number of objects created */
+ uint32_t ndestroyed; /* number of objects destroyed */
+ int nactive; /* number of active objects */
+ int ndead; /* nuber of dead objects */
+
+ /* pre-declared members */
+ mrp_lua_class_member_t *members; /* pre-declared members */
+ int nmember; /* number of pre-declared members */
+ char **natives; /* 'native' member names */
+ int nnative; /* number of native member names */
+ mrp_lua_class_bridge_t *bridges; /* bridged methods */
+ int nbridge; /* number of bridged methods */
+ int brmeta; /* reference to bridging metatable */
+ mrp_lua_class_flag_t flags; /* class member flags */
+ mrp_lua_class_notify_t notify; /* member change notify callback */
+ lua_CFunction setfield; /* overridden setfield, if any */
+ lua_CFunction getfield; /* overridden getfield, if any */
+};
+
+int mrp_lua_create_object_class(lua_State *L, mrp_lua_classdef_t *def);
+void mrp_lua_get_class_table(lua_State *L, mrp_lua_classdef_t *def);
+void *mrp_lua_create_object(lua_State *L, mrp_lua_classdef_t *def,
+ const char *name, int);
+void mrp_lua_set_object_name(lua_State *L, mrp_lua_classdef_t *def,
+ const char *name);
+void mrp_lua_set_object_index(lua_State *L, mrp_lua_classdef_t *def, int idx);
+
+void mrp_lua_destroy_object(lua_State *L, const char *name,int, void *object);
+
+int mrp_lua_find_object(lua_State *L, mrp_lua_classdef_t *def,
+ const char *name);
+
+void *mrp_lua_check_object(lua_State *L, mrp_lua_classdef_t *def, int argnum);
+void *mrp_lua_to_object(lua_State *L, mrp_lua_classdef_t *def, int idx);
+int mrp_lua_push_object(lua_State *L, void *object);
+
+mrp_lua_classdef_t *mrp_lua_get_object_classdef(void *);
+
+/** Specify pre-declared object members. */
+int mrp_lua_declare_members(mrp_lua_classdef_t *def, mrp_lua_class_flag_t flags,
+ mrp_lua_class_member_t *members, int nmember,
+ char **natives, int nnative,
+ mrp_lua_class_notify_t notify);
+/** Store and return a reference the value at the given stack location. */
+int mrp_lua_object_ref_value(void *object, lua_State *L, int idx);
+/** Remove the given stored reference. */
+void mrp_lua_object_unref_value(void *object, lua_State *L, int ref);
+/** Retrieve and push to the stack the value for the fiven reference. */
+int mrp_lua_object_deref_value(void *object, lua_State *L, int ref,
+ int pushnil);
+/** Get a private reference for the given reference owned by the given object. */
+int mrp_lua_object_getref(void *owner, void *object, lua_State *L, int ref);
+/** Decreate the reference count of the given object reference. */
+#define mrp_lua_object_putref(o, L, ref) mrp_lua_object_unref_value(o, L, ref)
+/** Set a pre-declared object member. */
+int mrp_lua_set_member(void *data, lua_State *L, char *err, size_t esize);
+/** Get and push a pre-declared object member. */
+int mrp_lua_get_member(void *data, lua_State *L, char *err, size_t esize);
+/** Initialize pre-declared object members from table at the given index. */
+int mrp_lua_init_members(void *data, lua_State *L, int idx,
+ char *err, size_t esize);
+/** Attempt to an array at tidx of expected type, with *nitemp max items. */
+int mrp_lua_object_collect_array(lua_State *L, int tidx, void **itemsp,
+ size_t *nitemp, int *expected, int dup,
+ char *e, size_t esize);
+/** Free an array duplicated by mrp_lua_object_collect_array. */
+void mrp_lua_object_free_array(void **itemsp, size_t *nitemp, int type);
+/** Get the class type id for the given class name. */
+mrp_lua_type_t mrp_lua_class_name_type(const char *class_name);
+/** Get the class type id for the given class id. */
+mrp_lua_type_t mrp_lua_class_id_type(const char *class_id);
+/** Get the class type id for the given type name. */
+mrp_lua_type_t mrp_lua_class_type(const char *type_name);
+/** Check if the object at the given stack index is of the given type. */
+int mrp_lua_object_of_type(lua_State *L, int idx, mrp_lua_type_t type);
+/** Check if the given (known to be murphy Lua) object is of given type. */
+int mrp_lua_pointer_of_type(void *data, mrp_lua_type_t type);
+/** Enable/disable per-class Murphy Lua object tracking. */
+void mrp_lua_track_objects(bool enable);
+/** Dump all active murphy Lua objects. */
+void mrp_lua_dump_objects(mrp_lua_tostr_mode_t mode, lua_State *L, FILE *fp);
+
+#endif /* __MURPHY_LUA_OBJECT_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/core/lua-utils/strarray.h>
+#include <murphy/core/lua-utils/lua-utils.h>
+
+
+mrp_lua_strarray_t *mrp_lua_check_strarray(lua_State *L, int t)
+{
+ size_t len;
+ size_t size;
+ mrp_lua_strarray_t *arr;
+ size_t i;
+
+ luaL_checktype(L, t, LUA_TTABLE);
+ len = luaL_getn(L, t);
+ size = sizeof(mrp_lua_strarray_t) + sizeof(const char *) * (len + 1);
+
+ if (!(arr = malloc(size)))
+ luaL_error(L, "can't allocate %d byte long memory", size);
+
+ arr->nstring = len;
+
+ lua_pushvalue(L, t);
+
+ for (i = 0; i < len; i++) {
+ lua_pushnumber(L, (int)(i+1));
+ lua_gettable(L, -2);
+
+ arr->strings[i] = strdup(luaL_checklstring(L, -1, NULL));
+
+ lua_pop(L, 1);
+ }
+
+ arr->strings[i] = NULL;
+
+ lua_pop(L, 1);
+
+ return arr;
+}
+
+int mrp_lua_push_strarray(lua_State *L, mrp_lua_strarray_t *arr)
+{
+ size_t i;
+
+ if (!arr)
+ lua_pushnil(L);
+ else {
+ lua_createtable(L, arr->nstring, 0);
+
+ for (i = 0; i < arr->nstring; i++) {
+ lua_pushinteger(L, (int)(i+1));
+ lua_pushstring(L, arr->strings[i]);
+ lua_settable(L, -3);
+ }
+ }
+
+ return 1;
+}
+
+void mrp_lua_free_strarray(mrp_lua_strarray_t *arr)
+{
+ size_t i;
+
+ if (arr) {
+ for (i = 0; i < arr->nstring; i++)
+ free((void *)arr->strings[i]);
+ free(arr);
+ }
+}
+
+
+char *mrp_lua_print_strarray(mrp_lua_strarray_t *arr, char *buf, int len)
+{
+ char *p, *e, *s;
+ size_t i;
+
+ e = (p = buf) + len;
+
+ if (len > 0) {
+ if (!arr->nstring)
+ p += snprintf(p, e-p, "<empty>");
+ else {
+ for (i = 0, s = ""; i < arr->nstring && p < e; i++, s = ", ")
+ p += snprintf(p, e-p, "%s%s", s, arr->strings[i]);
+ }
+ }
+
+ return buf;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_LUA_STRARRAY_H__
+#define __MURPHY_LUA_STRARRAY_H__
+
+typedef struct mrp_lua_strarray_s mrp_lua_strarray_t;
+
+struct mrp_lua_strarray_s {
+ size_t nstring;
+ const char *strings[];
+};
+
+mrp_lua_strarray_t *mrp_lua_check_strarray(lua_State *, int);
+int mrp_lua_push_strarray(lua_State *L, mrp_lua_strarray_t *);
+void mrp_lua_free_strarray(mrp_lua_strarray_t *);
+char *mrp_lua_print_strarray(mrp_lua_strarray_t *, char *, int);
+
+
+#endif /* __MURPHY_LUA_STRARRAY_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/method.h>
+
+
+typedef struct {
+ char *name; /* name of methods in this chain */
+ mrp_list_hook_t methods; /* list of exported methods */
+} method_list_t;
+
+
+typedef struct {
+ __MRP_METHOD_FIELDS(); /* method fields */
+ mrp_list_hook_t hook; /* to method table */
+} method_t;
+
+
+static void purge_method_list(void *key, void *object);
+
+
+static mrp_htbl_t *methods = NULL; /* hash table of method lists */
+
+
+static int create_method_table(void)
+{
+ mrp_htbl_config_t hcfg;
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = purge_method_list;
+
+ methods = mrp_htbl_create(&hcfg);
+
+ if (methods != NULL)
+ return 0;
+ else
+ return -1;
+}
+
+
+MRP_EXIT static void destroy_method_table(void)
+{
+ mrp_htbl_destroy(methods, TRUE);
+ methods = NULL;
+}
+
+
+static void free_method(method_t *m)
+{
+ if (m != NULL) {
+ mrp_free(m->name);
+ mrp_free(m->signature);
+ mrp_free(m);
+ }
+}
+
+
+static method_t *alloc_method(mrp_method_descr_t *method)
+{
+ method_t *m;
+
+ m = mrp_allocz(sizeof(*m));
+
+ if (m != NULL) {
+ mrp_list_init(&m->hook);
+ m->name = mrp_strdup(method->name);
+ m->signature = mrp_strdup(method->signature);
+
+ if (m->name != NULL &&
+ (m->signature != NULL || method->signature == NULL)) {
+ m->native_ptr = method->native_ptr;
+ m->script_ptr = method->script_ptr;
+ m->plugin = method->plugin;
+ }
+ else {
+ free_method(m);
+ m = NULL;
+ }
+ }
+
+ return m;
+}
+
+
+static method_list_t *create_method_list(const char *name)
+{
+ method_list_t *l;
+
+ if (MRP_UNLIKELY(methods == NULL)) {
+ if (create_method_table() < 0)
+ return NULL;
+ }
+
+ l = mrp_allocz(sizeof(*l));
+
+ if (l != NULL) {
+ mrp_list_init(&l->methods);
+ l->name = mrp_strdup(name);
+
+ if (l->name != NULL) {
+ if (mrp_htbl_insert(methods, (void *)l->name, (void *)l))
+ return l;
+ }
+
+ mrp_free(l->name);
+ mrp_free(l);
+ }
+
+ return NULL;
+}
+
+
+static void free_method_list(method_list_t *l)
+{
+ mrp_list_hook_t *p, *n;
+ method_t *m;
+
+ if (l != NULL) {
+ mrp_list_foreach(&l->methods, p, n) {
+ m = mrp_list_entry(p, typeof(*m), hook);
+
+ mrp_list_delete(&m->hook);
+ free_method(m);
+ }
+
+ mrp_free(l->name);
+ mrp_free(l);
+ }
+}
+
+
+static void purge_method_list(void *key, void *object)
+{
+ MRP_UNUSED(key);
+
+ free_method_list(object);
+}
+
+
+static method_list_t *lookup_method_list(const char *name)
+{
+ method_list_t *l;
+
+ if (methods != NULL)
+ l = mrp_htbl_lookup(methods, (void *)name);
+ else
+ l = NULL;
+
+ return l;
+}
+
+
+static inline int check_signatures(const char *sig1, const char *sig2)
+{
+ static int warned = FALSE;
+
+ MRP_UNUSED(sig1);
+ MRP_UNUSED(sig2);
+
+ if (!warned) {
+ mrp_log_warning("XXX TODO: implement signature checking (%s@%s:%d)",
+ __FUNCTION__, __FILE__, __LINE__);
+ warned = TRUE;
+ }
+
+ return TRUE;
+}
+
+
+static method_t *lookup_method(const char *name, const char *signature,
+ void *native_ptr,
+ int (*script_ptr)(mrp_plugin_t *plugin,
+ const char *name,
+ mrp_script_env_t *env),
+ mrp_plugin_t *plugin)
+{
+ method_list_t *l;
+ method_t *m;
+ mrp_list_hook_t *p, *n;
+
+ l = lookup_method_list(name);
+
+ if (l != NULL) {
+ mrp_list_foreach(&l->methods, p, n) {
+ m = mrp_list_entry(p, typeof(*m), hook);
+
+ if (((m->signature == NULL && signature == NULL) ||
+ (m->signature != NULL && signature != NULL &&
+ !strcmp(m->signature, signature))) &&
+ m->native_ptr == native_ptr && m->script_ptr == script_ptr &&
+ m->plugin == plugin)
+ return m;
+ }
+ }
+
+ return NULL;
+}
+
+
+method_t *find_method(const char *name, const char *signature)
+{
+ method_list_t *l;
+ method_t *m;
+ mrp_list_hook_t *p, *n;
+ const char *base;
+ int plen;
+
+ base = strrchr(name, '.');
+ if (base != NULL) {
+ plen = base - name;
+ base = base + 1;
+ }
+ else {
+ base = name;
+ plen = 0;
+ }
+
+ l = lookup_method_list(base);
+
+ if (l != NULL) {
+ mrp_list_foreach(&l->methods, p, n) {
+ m = mrp_list_entry(p, typeof(*m), hook);
+
+ if (signature != NULL && m->signature != NULL)
+ if (!check_signatures(signature, m->signature))
+ continue;
+
+ if (plen != 0) {
+ if (m->plugin == NULL)
+ continue;
+ if (strncmp(name, m->plugin->instance, plen) ||
+ m->plugin->instance[plen] != '\0')
+ continue;
+ }
+
+ return m;
+ }
+ }
+
+ errno = ENOENT;
+ return NULL;
+}
+
+
+static int export_method(method_t *method)
+{
+ method_list_t *l;
+
+ l = lookup_method_list(method->name);
+
+ if (l == NULL) {
+ l = create_method_list(method->name);
+
+ if (l == NULL)
+ return -1;
+ }
+
+ mrp_ref_plugin(method->plugin);
+ mrp_list_append(&l->methods, &method->hook);
+
+ return 0;
+}
+
+
+static int remove_method(const char *name, const char *signature,
+ void *native_ptr,
+ int (*script_ptr)(mrp_plugin_t *plugin,
+ const char *name,
+ mrp_script_env_t *env),
+ mrp_plugin_t *plugin)
+{
+ method_t *m;
+
+ m = lookup_method(name, signature, native_ptr, script_ptr, plugin);
+
+ if (m != NULL) {
+ mrp_list_delete(&m->hook);
+ mrp_unref_plugin(m->plugin);
+ free_method(m);
+
+ return 0;
+ }
+ else {
+ errno = ENOENT;
+ return -1;
+ }
+}
+
+
+int mrp_export_method(mrp_method_descr_t *method)
+{
+ method_t *m;
+
+ if (lookup_method(method->name, method->signature,
+ method->native_ptr, method->script_ptr,
+ method->plugin) != NULL)
+ errno = EEXIST;
+ else {
+ m = alloc_method(method);
+
+ if (m != NULL) {
+ if (export_method(m) == 0) {
+ mrp_log_info("exported method %s (%s) %s%s.",
+ method->name,
+ method->signature ? method->signature : "-",
+ method->plugin ? "from plugin " : "",
+ method->plugin ? method->plugin->instance : "");
+ return 0;
+ }
+ else
+ free_method(m);
+ }
+ }
+
+ mrp_log_error("Failed to export method %s (%s) %s%s.",
+ method->name, method->signature,
+ method->plugin ? "from plugin " : "",
+ method->plugin ? method->plugin->instance : "");
+
+ return -1;
+}
+
+
+int mrp_remove_method(mrp_method_descr_t *method)
+{
+ return remove_method(method->name, method->signature,
+ method->native_ptr, method->script_ptr,
+ method->plugin);
+}
+
+
+int mrp_import_method(const char *name, const char *signature,
+ void **native_ptr,
+ int (**script_ptr)(mrp_plugin_t *plugin, const char *name,
+ mrp_script_env_t *env),
+ mrp_plugin_t **plugin)
+{
+ method_t *m;
+
+ if ((script_ptr != NULL && plugin == NULL) ||
+ (script_ptr == NULL && plugin != NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ m = find_method(name, signature);
+
+ if (m == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if ((native_ptr != NULL && m->native_ptr == NULL) ||
+ (script_ptr != NULL && m->script_ptr == NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ mrp_ref_plugin(m->plugin);
+
+ if (native_ptr != NULL)
+ *native_ptr = m->native_ptr;
+ if (script_ptr != NULL) {
+ *script_ptr = m->script_ptr;
+ *plugin = m->plugin;
+ }
+
+ return 0;
+}
+
+
+int mrp_release_method(const char *name, const char *signature,
+ void **native_ptr,
+ int (**script_ptr)(mrp_plugin_t *plugin,
+ const char *name,
+ mrp_script_env_t *env))
+{
+ method_t *m;
+
+ m = find_method(name, signature);
+
+ if (m == NULL) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if ((native_ptr != NULL && (*native_ptr != m->native_ptr)) ||
+ (script_ptr != NULL && (*script_ptr != m->script_ptr))) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ mrp_unref_plugin(m->plugin);
+
+ if (native_ptr != NULL)
+ *native_ptr = NULL;
+ if (script_ptr != NULL)
+ *script_ptr = NULL;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CORE_METHOD_H__
+#define __MURPHY_CORE_METHOD_H__
+
+typedef struct mrp_method_descr_s mrp_method_descr_t;
+
+#include <murphy/core/plugin.h>
+#include <murphy/core/scripting.h>
+
+
+/*
+ * exported methods
+ */
+
+#define __MRP_METHOD_FIELDS(...) \
+ __VA_ARGS__ char *name; /* method name */ \
+ __VA_ARGS__ char *signature; /* method signature */ \
+ /* pointers to exported function, native and boilerplate for scripts */ \
+ void *native_ptr; \
+ int (*script_ptr)(mrp_plugin_t *plugin, const char *name, \
+ mrp_script_env_t *env); \
+ mrp_plugin_t *plugin /* exporting plugin (or NULL) */ \
+
+struct mrp_method_descr_s {
+ __MRP_METHOD_FIELDS(const);
+};
+
+
+/*
+ * convenience macros for declaring exported methods
+ */
+
+#define MRP_EXPORTABLE(_return_type, _func, _arglist) \
+ static const char _func##_method_signature[] = \
+ #_return_type" __ "#_arglist; \
+ \
+ static _return_type _func _arglist
+
+/** Declare a method along with a boilerplate to call from scripts. */
+#define MRP_GENERIC_METHOD(_name, _func, _boilerplate) { \
+ .name = _name, \
+ .signature = _func##_method_signature, \
+ .native_ptr = _func, \
+ .script_ptr = _boilerplate, \
+ .plugin = NULL, \
+ }
+
+/** Declare a method that cannot be called from scripts. */
+#define MRP_NATIVE_METHOD(_name, _func) { \
+ .name = _name, \
+ .signature = _func##_method_signature, \
+ .native_ptr = _func, \
+ .script_ptr = NULL, \
+ .plugin = NULL, \
+ }
+
+/** Declare a method that can only be called via a boilerplate for scripts. */
+#define MRP_SCRIPT_METHOD(_name, _boilerplate) { \
+ .name = _name, \
+ .signature = NULL, \
+ .native_ptr = NULL, \
+ .script_ptr = _boilerplate, \
+ .plugin = NULL, \
+ }
+
+
+/*
+ * convenience macros for declaring imported methods
+ */
+
+#define MRP_IMPORTABLE(_return_type, _funcptr, _arglist) \
+ static const char _funcptr##_method_signature[] = \
+ #_return_type" __ "#_arglist; \
+ \
+ static _return_type (*_funcptr) _arglist
+
+#define MRP_IMPORT_METHOD(_name, _funcptr) { \
+ .name = _name, \
+ .signature = _funcptr##_method_signature, \
+ .native_ptr = (void *)&_funcptr, \
+ }
+
+/** Export a method for plugins and/or scripts. */
+int mrp_export_method(mrp_method_descr_t *method);
+
+/** Remove an exported method. */
+int mrp_remove_method(mrp_method_descr_t *method);
+
+/** Import an exported method. */
+int mrp_import_method(const char *name, const char *signature,
+ void **native_ptr,
+ int (**script_ptr)(mrp_plugin_t *plugin, const char *name,
+ mrp_script_env_t *env),
+ mrp_plugin_t **plugin);
+
+/** Release an imported method. */
+int mrp_release_method(const char *name, const char *signature,
+ void **native_ptr,
+ int (**script_ptr)(mrp_plugin_t *plugin, const char *name,
+ mrp_script_env_t *env));
+
+
+
+#endif /* __MURPHY_CORE_METHOD_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-core
+Description: Murphy policy framework, core library.
+Requires: murphy-common lua
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-common -lmurphy-lua-decision -lmurphy-lua-utils @LUA_LIBS@ -lmurphy-core
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <murphy/common/list.h>
+#include <murphy/common/file-utils.h>
+#include <murphy/core/plugin.h>
+
+#define PLUGIN_PREFIX "plugin-"
+#define BUILTIN TRUE
+#define DYNAMIC FALSE
+
+#define __PARANOID_BLACKLIST_CHECK__
+
+static mrp_plugin_descr_t *open_builtin(mrp_context_t *ctx, const char *name);
+static mrp_plugin_descr_t *open_dynamic(mrp_context_t *ctx, const char *name,
+ void **handle);
+static mrp_plugin_t *find_plugin(mrp_context_t *ctx, char *name);
+static mrp_plugin_t *find_plugin_instance(mrp_context_t *ctx,
+ const char *instance);
+static int parse_plugin_args(mrp_plugin_t *plugin,
+ mrp_plugin_arg_t *argv, int argc);
+
+static int export_plugin_methods(mrp_plugin_t *plugin);
+static int remove_plugin_methods(mrp_plugin_t *plugin);
+static int import_plugin_methods(mrp_plugin_t *plugin);
+static int release_plugin_methods(mrp_plugin_t *plugin);
+
+
+/*
+ * list of built-in in plugins
+ *
+ * Descriptors of built-in plugins, ie. plugins that are linked to
+ * the daemon, get collected to this list during startup.
+ */
+
+static MRP_LIST_HOOK(builtin_plugins);
+
+
+/*
+ * plugin-related events
+ */
+
+enum {
+ PLUGIN_EVENT_LOADED = 0, /* plugin has been loaded */
+ PLUGIN_EVENT_STARTED, /* plugin has been started */
+ PLUGIN_EVENT_FAILED, /* plugin failed to start */
+ PLUGIN_EVENT_STOPPING, /* plugin being stopped */
+ PLUGIN_EVENT_STOPPED, /* plugin has been stopped */
+ PLUGIN_EVENT_UNLOADED, /* plugin has been unloaded */
+ PLUGIN_EVENT_MAX,
+};
+
+
+MRP_REGISTER_EVENTS(plugin_events,
+ MRP_EVENT(MRP_PLUGIN_EVENT_LOADED , PLUGIN_EVENT_LOADED ),
+ MRP_EVENT(MRP_PLUGIN_EVENT_STARTED , PLUGIN_EVENT_STARTED ),
+ MRP_EVENT(MRP_PLUGIN_EVENT_FAILED , PLUGIN_EVENT_FAILED ),
+ MRP_EVENT(MRP_PLUGIN_EVENT_STOPPING, PLUGIN_EVENT_STOPPING),
+ MRP_EVENT(MRP_PLUGIN_EVENT_STOPPED , PLUGIN_EVENT_STOPPED ),
+ MRP_EVENT(MRP_PLUGIN_EVENT_UNLOADED, PLUGIN_EVENT_UNLOADED));
+
+
+static inline int is_listed(const char *list, const char *name)
+{
+ const char *b, *n, *e;
+
+ if (list == NULL || name == NULL)
+ return FALSE;
+
+ if ((list[0] == '*' && list[1] == '\0') || (!strcmp(list, "all")))
+ return TRUE;
+
+ b = list;
+ while (b && *b) {
+ while (*b == ' ' || *b == '\t' || *b == ',')
+ b++;
+
+ if ((e = n = strchr(b, ',')) != NULL) {
+ while (e > b && (*e == ' ' || *e == '\t' || *e == ','))
+ e--;
+ }
+
+ if ((e != NULL && e > b && !strncmp(b, name, e - b)) ||
+ (e == NULL && !strcmp (b, name)))
+ return TRUE;
+
+ if ((b[0] == '*' && (b[1] == ',' ||
+ b[1] == ' ' || b[1] == '\t' ||
+ b[1] == '\0')) ||
+ (b[0] == 'a' && b[1] == 'l' && b[2] == 'l' &&
+ (b[1] == ',' ||
+ b[1] == ' ' || b[1] == '\t' ||
+ b[1] == '\0'))) {
+ mrp_log_warning("Wildcard entry in blacklist/whitelist '%s'", list);
+ return TRUE;
+ }
+
+ b = n ? n + 1 : NULL;
+ }
+
+ return FALSE;
+}
+
+
+static inline int is_blacklisted(mrp_context_t *ctx, const char *name,
+ int builtin)
+{
+#define IS_WILDCARD(l) \
+ (((l) != NULL && (l)[0] == '*' && (l)[1] == '\0') || \
+ ((l) != NULL && !strcmp((l), "all")))
+
+ const char *bl, *wl, *type_bl, *type_wl;
+ int b, w, tb, tw, blacklist;
+
+ mrp_debug("checking if %s plugin %s is blacklisted",
+ builtin ? "builtin" : "dynamic", name);
+
+ bl = ctx->blacklist_plugins;
+ wl = ctx->whitelist_plugins;
+ b = is_listed(bl, name);
+ w = is_listed(wl, name);
+
+ if (builtin) {
+ type_bl = ctx->blacklist_builtin;
+ type_wl = ctx->whitelist_builtin;
+ }
+ else {
+ type_bl = ctx->blacklist_dynamic;
+ type_wl = ctx->whitelist_dynamic;
+ }
+
+ tb = is_listed(type_bl, name);
+ tw = is_listed(type_wl, name);
+
+ mrp_debug("%s: b:(%s,%s), w:(%s,%s)", name,
+ b ? "true" : "false", tb ? "true" : "false",
+ w ? "true" : "false", tw ? "true" : "false");
+
+ if (IS_WILDCARD(bl) || IS_WILDCARD(type_bl)) {
+ if (w || tw)
+ blacklist = FALSE;
+ else
+ blacklist = TRUE;
+
+ goto verdict;
+ }
+
+ if (IS_WILDCARD(wl) || IS_WILDCARD(type_wl)) {
+ if (b || tb)
+ blacklist = TRUE;
+ else
+ blacklist = FALSE;
+
+ goto verdict;
+ }
+
+ blacklist = (( is_listed(bl, name) || is_listed(type_bl, name)) &&
+ !(is_listed(wl, name) || is_listed(type_wl, name)));
+
+
+ verdict:
+ mrp_debug("%s: %sblacklisted", name, blacklist ? "" : "not ");
+
+ return blacklist;
+}
+
+
+static int emit_plugin_event(int idx, mrp_plugin_t *plugin)
+{
+ mrp_context_t *ctx = plugin->ctx;
+ mrp_mainloop_t *ml = ctx->ml;
+ uint16_t name = MRP_PLUGIN_TAG_PLUGIN;
+ uint16_t inst = MRP_PLUGIN_TAG_INSTANCE;
+ int flags = MRP_EVENT_SYNCHRONOUS;
+
+ if (ctx->plugin_bus == NULL)
+ ctx->plugin_bus = mrp_event_bus_get(ml, MRP_PLUGIN_BUS);
+
+ if (mrp_event_emit_msg(ctx->plugin_bus, plugin_events[idx].id, flags,
+ MRP_MSG_TAG_STRING(name, plugin->descriptor->name),
+ MRP_MSG_TAG_STRING(inst, plugin->instance)) < 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+int mrp_register_builtin_plugin(mrp_plugin_descr_t *descriptor)
+{
+ mrp_plugin_t *plugin;
+
+ if (descriptor->name && descriptor->init && descriptor->exit) {
+ if ((plugin = mrp_allocz(sizeof(*plugin))) != NULL) {
+ plugin->descriptor = descriptor;
+ mrp_list_append(&builtin_plugins, &plugin->hook);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ else {
+ mrp_log_error("Ignoring static plugin '%s' with an invalid or "
+ "incomplete plugin descriptor.", descriptor->path);
+ return FALSE;
+ }
+}
+
+
+void mrp_block_blacklisted_plugins(mrp_context_t *ctx)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *builtin;
+
+ mrp_list_foreach(&builtin_plugins, p, n) {
+ builtin = mrp_list_entry(p, typeof(*builtin), hook);
+
+ if (is_blacklisted(ctx, builtin->descriptor->name, BUILTIN)) {
+ mrp_log_info("Unlinking blacklisted builtin plugin %s",
+ builtin->descriptor->name);
+ mrp_list_delete(&builtin->hook);
+ }
+ }
+}
+
+
+int mrp_plugin_exists(mrp_context_t *ctx, const char *name)
+{
+ struct stat st;
+ char path[PATH_MAX];
+
+ if (open_builtin(ctx, name))
+ return TRUE;
+ else {
+ if (is_blacklisted(ctx, name, DYNAMIC))
+ return FALSE;
+
+ snprintf(path, sizeof(path), "%s/%s%s.so", ctx->plugin_dir,
+ PLUGIN_PREFIX, name);
+ if (stat(path, &st) == 0)
+ return TRUE;
+ else
+ return FALSE;
+ }
+}
+
+
+int mrp_plugin_loaded(mrp_context_t *ctx, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plugin;
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+
+ if (!strcmp(plugin->instance, name))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int mrp_plugin_running(mrp_context_t *ctx, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plugin;
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+
+ if (!strcmp(plugin->instance, name))
+ return (plugin->state == MRP_PLUGIN_RUNNING);
+ }
+
+ return FALSE;
+}
+
+
+static inline int check_plugin_version(mrp_plugin_descr_t *descr)
+{
+ int major, minor;
+
+ major = MRP_VERSION_MAJOR(descr->mrp_version);
+ minor = MRP_VERSION_MINOR(descr->mrp_version);
+
+ if (major != MRP_PLUGIN_API_MAJOR || minor > MRP_PLUGIN_API_MINOR) {
+ mrp_log_error("Plugin '%s' uses incompatible version (%d.%d vs. %d.%d)",
+ descr->name, major, minor,
+ MRP_PLUGIN_API_MAJOR, MRP_PLUGIN_API_MINOR);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static inline int check_plugin_singleton(mrp_plugin_descr_t *descr)
+{
+ if (descr->singleton && descr->ninstance > 1) {
+ mrp_log_error("Singleton plugin '%s' has already been instantiated.",
+ descr->name);
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+mrp_plugin_t *mrp_load_plugin(mrp_context_t *ctx, const char *name,
+ const char *instance, mrp_plugin_arg_t *args,
+ int narg)
+{
+ mrp_plugin_t *plugin;
+ char path[PATH_MAX];
+ mrp_plugin_descr_t *dynamic, *builtin, *descr;
+ void *handle;
+ mrp_console_group_t *cmds;
+ char grpbuf[PATH_MAX], *cmdgrp;
+
+ if (name == NULL)
+ return NULL;
+
+ if (instance == NULL)
+ instance = name;
+
+ if (ctx->state == MRP_STATE_RUNNING && ctx->disable_runtime_load) {
+ mrp_log_error("Post-startup plugin loading has been disabled.");
+ return NULL;
+ }
+
+ if (find_plugin_instance(ctx, instance) != NULL) {
+ mrp_log_error("Plugin '%s' has already been loaded.", instance);
+ return NULL;
+ }
+
+ plugin = NULL;
+ handle = NULL;
+ snprintf(path, sizeof(path), "%s/%s%s.so", ctx->plugin_dir,
+ PLUGIN_PREFIX, name);
+
+ dynamic = open_dynamic(ctx, name, &handle);
+ builtin = open_builtin(ctx, name);
+
+ if (dynamic != NULL) {
+ if (builtin != NULL)
+ mrp_log_warning("Dynamic plugin '%s' shadows builtin plugin '%s'.",
+ path, builtin->path);
+ descr = dynamic;
+ }
+ else {
+ if (builtin == NULL) {
+ mrp_log_error("Could not find plugin '%s' to load '%s'.", name,
+ instance);
+ return NULL;
+ }
+ descr = builtin;
+ }
+
+ descr->ninstance++;
+
+ if (!check_plugin_version(descr) || !check_plugin_singleton(descr))
+ goto fail;
+
+ if ((plugin = mrp_allocz(sizeof(*plugin))) != NULL) {
+ mrp_list_init(&plugin->hook);
+
+ plugin->instance = mrp_strdup(instance);
+ plugin->path = mrp_strdup(handle ? path : descr->path);
+
+ if (plugin->instance == NULL || plugin->path == NULL) {
+ mrp_log_error("Failed to allocate plugin '%s'.", name);
+ goto fail;
+ }
+
+ if (descr->cmds != NULL) {
+ plugin->cmds = cmds = mrp_allocz(sizeof(*plugin->cmds));
+
+ if (cmds != NULL) {
+ mrp_list_init(&cmds->hook);
+
+ if (instance != name) {
+ snprintf(grpbuf, sizeof(grpbuf), "%s-%s", name, instance);
+ cmdgrp = grpbuf;
+ }
+ else
+ cmdgrp = (char *)name;
+
+ cmds->name = mrp_strdup(cmdgrp);
+
+ if (cmds->name == NULL) {
+ mrp_log_error("Failed to allocate plugin commands.");
+ goto fail;
+ }
+
+ cmds->commands = descr->cmds->commands;
+ cmds->ncommand = descr->cmds->ncommand;
+
+ if (MRP_UNLIKELY(descr->cmds->user_data != NULL))
+ cmds->user_data = descr->cmds->user_data;
+ else
+ cmds->user_data = plugin;
+ }
+ else {
+ mrp_log_error("Failed to allocate plugin commands.");
+ goto fail;
+ }
+ }
+
+
+ plugin->ctx = ctx;
+ plugin->descriptor = descr;
+ plugin->handle = handle;
+
+ mrp_refcnt_init(&plugin->refcnt);
+
+ if (!parse_plugin_args(plugin, args, narg))
+ goto fail;
+
+ if (plugin->cmds != NULL)
+ mrp_console_add_group(plugin->ctx, plugin->cmds);
+
+ if (!export_plugin_methods(plugin))
+ goto fail;
+
+ mrp_list_append(&ctx->plugins, &plugin->hook);
+
+ emit_plugin_event(PLUGIN_EVENT_LOADED, plugin);
+
+ if (ctx->state == MRP_STATE_STARTING || ctx->state == MRP_STATE_RUNNING)
+ mrp_start_plugin(plugin);
+
+ return plugin;
+ }
+ else {
+ mrp_log_error("Could not allocate plugin '%s'.", name);
+ fail:
+ mrp_unload_plugin(plugin);
+
+ return NULL;
+ }
+}
+
+
+static int load_plugin_cb(const char *file, mrp_dirent_type_t type, void *data)
+{
+ mrp_context_t *ctx = (mrp_context_t *)data;
+ char name[PATH_MAX], *start, *end;
+ int len;
+
+ MRP_UNUSED(type);
+
+ if ((start = strstr(file, PLUGIN_PREFIX)) != NULL) {
+ start += sizeof(PLUGIN_PREFIX) - 1;
+ if ((end = strstr(start, ".so")) != NULL) {
+ len = end - start;
+ strncpy(name, start, len);
+ name[len] = '\0';
+ mrp_load_plugin(ctx, name, NULL, NULL, 0);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int mrp_load_all_plugins(mrp_context_t *ctx)
+{
+ mrp_dirent_type_t type;
+ const char *pattern;
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plugin;
+
+ type = MRP_DIRENT_REG;
+ pattern = PLUGIN_PREFIX".*\\.so$";
+ mrp_scan_dir(ctx->plugin_dir, pattern, type, load_plugin_cb, ctx);
+
+ mrp_list_foreach(&builtin_plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+
+ mrp_load_plugin(ctx, plugin->descriptor->name, NULL, NULL, 0);
+ }
+
+ return TRUE;
+}
+
+
+int mrp_request_plugin(mrp_context_t *ctx, const char *name,
+ const char *instance)
+{
+ mrp_plugin_t *plugin;
+
+ if (instance == NULL)
+ instance = name;
+
+ if ((plugin = find_plugin_instance(ctx, instance)) != NULL) {
+ if (instance == name || !strcmp(plugin->descriptor->name, name))
+ return TRUE;
+ }
+
+ return (mrp_load_plugin(ctx, name, instance, NULL, 0) != NULL);
+}
+
+
+int mrp_unload_plugin(mrp_plugin_t *plugin)
+{
+ mrp_plugin_arg_t *pa, *da, *ra;
+ int i, j;
+
+ if (plugin != NULL) {
+ if (plugin->refcnt == 0) {
+ mrp_list_delete(&plugin->hook);
+
+ pa = plugin->args;
+ da = plugin->descriptor->args;
+
+ if (pa != da) {
+ for (i = 0; i < plugin->descriptor->narg; i++) {
+ switch (pa->type) {
+ case MRP_PLUGIN_ARG_TYPE_STRING:
+ if (pa->str != da->str)
+ mrp_free(pa->str);
+ break;
+
+ case MRP_PLUGIN_ARG_TYPE_OBJECT:
+ mrp_json_unref(pa->obj.json);
+ break;
+
+ case MRP_PLUGIN_ARG_TYPE_UNDECL:
+ ra = pa->rest.args;
+ for (j = 0; j < pa->rest.narg; j++) {
+ mrp_free(ra->key);
+ switch (ra->type) {
+ case MRP_PLUGIN_ARG_TYPE_STRING:
+ mrp_free(ra->str);
+ break;
+ case MRP_PLUGIN_ARG_TYPE_OBJECT:
+ mrp_json_unref(ra->obj.json);
+ break;
+ default:
+ break;
+ }
+ ra++;
+ }
+ break;
+ default:
+ break;
+ }
+
+ pa++;
+ da++;
+ }
+ mrp_free(plugin->args);
+ }
+
+ plugin->descriptor->ninstance--;
+
+ emit_plugin_event(PLUGIN_EVENT_UNLOADED, plugin);
+
+ if (plugin->handle != NULL)
+ dlclose(plugin->handle);
+
+ if (plugin->cmds != NULL) {
+ mrp_console_del_group(plugin->ctx, plugin->cmds);
+
+ mrp_free(plugin->cmds->name);
+ mrp_free(plugin->cmds);
+ }
+
+ release_plugin_methods(plugin);
+ remove_plugin_methods(plugin);
+
+ mrp_free(plugin->instance);
+ mrp_free(plugin->path);
+ mrp_free(plugin);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+int mrp_start_plugins(mrp_context_t *ctx)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plugin;
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+
+ if (!import_plugin_methods(plugin)) {
+ if (!plugin->may_fail)
+ return FALSE;
+ else
+ goto unload;
+ }
+
+ if (!mrp_start_plugin(plugin)) {
+ if (!plugin->may_fail)
+ return FALSE;
+ else {
+ unload:
+ mrp_unload_plugin(plugin);
+ }
+ }
+
+ /* XXX TODO: argh, ugly kludge for plugins loading plugins... */
+ if (plugin->hook.next != n)
+ n = plugin->hook.next;
+ }
+
+ return TRUE;
+}
+
+
+int mrp_start_plugin(mrp_plugin_t *plugin)
+{
+ if (plugin != NULL) {
+ if (plugin->state == MRP_PLUGIN_LOADED) {
+ if (!plugin->descriptor->init(plugin)) {
+ mrp_log_error("Failed to start plugin %s (%s).",
+ plugin->instance, plugin->descriptor->name);
+
+ emit_plugin_event(PLUGIN_EVENT_FAILED, plugin);
+ return FALSE;
+ }
+ else {
+ plugin->state = MRP_PLUGIN_RUNNING;
+ emit_plugin_event(PLUGIN_EVENT_STARTED, plugin);
+ }
+ }
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_stop_plugin(mrp_plugin_t *plugin)
+{
+ if (plugin != NULL) {
+ if (plugin->refcnt <= 1) {
+ emit_plugin_event(PLUGIN_EVENT_STOPPING, plugin);
+ plugin->descriptor->exit(plugin);
+ plugin->refcnt = 0;
+ plugin->state = MRP_PLUGIN_STOPPED;
+ emit_plugin_event(PLUGIN_EVENT_STOPPED, plugin);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+static mrp_plugin_t *find_plugin_instance(mrp_context_t *ctx,
+ const char *instance)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plg;
+
+ MRP_UNUSED(find_plugin);
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plg = mrp_list_entry(p, typeof(*plg), hook);
+
+ if (!strcmp(plg->instance, instance))
+ return plg;
+ }
+
+ return NULL;
+}
+
+
+static mrp_plugin_t *find_plugin(mrp_context_t *ctx, char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plg;
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plg = mrp_list_entry(p, typeof(*plg), hook);
+
+ if (!strcmp(plg->descriptor->name, name))
+ return plg;
+ }
+
+ return NULL;
+}
+
+
+static mrp_plugin_descr_t *open_dynamic(mrp_context_t *ctx, const char *name,
+ void **handle)
+{
+ mrp_plugin_descr_t *(*describe)(void);
+ mrp_plugin_descr_t *d;
+ void *h;
+ char path[PATH_MAX];
+
+ snprintf(path, sizeof(path), "%s/%s%s.so", ctx->plugin_dir,
+ PLUGIN_PREFIX, name);
+
+ if (is_blacklisted(ctx, name, DYNAMIC))
+ return NULL;
+
+ if ((h = dlopen(path, RTLD_LAZY | RTLD_LOCAL)) != NULL) {
+ if ((describe = dlsym(h, "mrp_get_plugin_descriptor")) != NULL) {
+ if ((d = describe()) != NULL) {
+ if (d->init != NULL && d->exit != NULL && d->name != NULL) {
+ if (!d->core)
+ *handle = h;
+ else {
+ *handle = dlopen(path,
+ RTLD_LAZY|RTLD_GLOBAL|RTLD_NOLOAD);
+ dlclose(h);
+ }
+
+ return d;
+ }
+ else
+ mrp_log_error("Ignoring plugin '%s' with invalid "
+ "plugin descriptor.", path);
+ }
+ else
+ mrp_log_error("Plugin '%s' provided NULL descriptor.", path);
+ }
+ else
+ mrp_log_error("Plugin '%s' does not provide a descriptor.", path);
+ }
+ else {
+ if (access(path, F_OK) == 0) {
+ char *err = dlerror();
+
+ mrp_log_error("Failed to dlopen plugin '%s' (%s).", path,
+ err ? err : "unknown error");
+ }
+ }
+
+ if (h != NULL)
+ dlclose(h);
+
+ *handle = NULL;
+ return NULL;
+}
+
+
+static mrp_plugin_descr_t *open_builtin(mrp_context_t *ctx, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_plugin_t *plugin;
+
+ mrp_list_foreach(&builtin_plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+
+ if (!strcmp(plugin->descriptor->name, name)) {
+#ifdef __PARANOID_BLACKLIST_CHECK__
+ if (is_blacklisted(ctx, name, BUILTIN)) {
+ mrp_log_warning("Hmm... blacklisted builtin %s still "
+ "reachable, blocking it.", name);
+ return NULL;
+ }
+ else
+#endif
+ return plugin->descriptor;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int parse_plugin_arg(mrp_plugin_arg_t *arg, mrp_plugin_arg_t *parg)
+{
+ char *end;
+ mrp_json_t **json;
+ char *jstr;
+ int jlen;
+
+ switch (parg->type) {
+ case MRP_PLUGIN_ARG_TYPE_STRING:
+ if (arg->str != NULL) {
+ parg->str = arg->str;
+ arg->str = NULL;
+ }
+ return TRUE;
+
+ case MRP_PLUGIN_ARG_TYPE_BOOL:
+ if (arg->str != NULL) {
+ if (!strcasecmp(arg->str, "TRUE"))
+ parg->bln = TRUE;
+ else if (!strcasecmp(arg->str, "FALSE"))
+ parg->bln = FALSE;
+ else
+ return FALSE;
+ }
+ else
+ parg->bln = TRUE;
+ return TRUE;
+
+ case MRP_PLUGIN_ARG_TYPE_UINT32:
+ parg->u32 = (uint32_t)strtoul(arg->str, &end, 0);
+ if (end && !*end)
+ return TRUE;
+ else
+ return FALSE;
+
+ case MRP_PLUGIN_ARG_TYPE_INT32:
+ parg->i32 = (int32_t)strtol(arg->str, &end, 0);
+ if (end && !*end)
+ return TRUE;
+ else
+ return FALSE;
+
+ case MRP_PLUGIN_ARG_TYPE_DOUBLE:
+ parg->dbl = strtod(arg->str, &end);
+ if (end && !*end)
+ return TRUE;
+ else
+ return FALSE;
+
+ case MRP_PLUGIN_ARG_TYPE_OBJECT:
+ jstr = arg->obj.str;
+ jlen = strlen(jstr);
+ json = &parg->obj.json;
+ if (mrp_json_parse_object(&jstr, &jlen, json) < 0 || jlen != 0)
+ return FALSE;
+ else
+ return TRUE;
+
+ default:
+ return FALSE;
+ }
+}
+
+
+static int parse_undeclared_arg(mrp_plugin_arg_t *arg, mrp_plugin_arg_t *pa)
+{
+ mrp_plugin_arg_t *a;
+ char *value, *end;
+ int prfx;
+
+ if (mrp_reallocz(pa->rest.args, pa->rest.narg, pa->rest.narg + 1)) {
+ a = pa->rest.args + pa->rest.narg++;
+ a->key = mrp_strdup(arg->key);
+
+ if (a->key == NULL)
+ return FALSE;
+
+ if (arg->str == NULL) {
+ a->type = MRP_PLUGIN_ARG_TYPE_STRING;
+ a->str = NULL;
+
+ return TRUE;
+ }
+
+ if (!strncmp(arg->str, "string:", 7)) {
+ value = arg->str + 7;
+ string:
+ a->type = MRP_PLUGIN_ARG_TYPE_STRING;
+ a->str = mrp_strdup(value);
+
+ if (a->str != NULL)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else if (!strncmp(arg->str, "bool:", 5)) {
+ a->type = MRP_PLUGIN_ARG_TYPE_BOOL;
+ if (!strcasecmp(arg->str + 5, "TRUE")) a->bln = TRUE;
+ else if (!strcasecmp(arg->str + 5, "FALSE")) a->bln = FALSE;
+ else return FALSE;
+ }
+ else if (!strncmp(arg->str, "int32:", 6)) {
+ a->type = MRP_PLUGIN_ARG_TYPE_INT32;
+ a->i32 = (int32_t)strtol(arg->str + 6, &end, 0);
+
+ if (end && !*end)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else if (!strncmp(arg->str, "uint32:", 7)) {
+ a->type = MRP_PLUGIN_ARG_TYPE_UINT32;
+ a->u32 = (uint32_t)strtoul(arg->str + 7, &end, 0);
+
+ if (end && !*end)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else if (!strncmp(arg->str, "double:", 7)) {
+ a->type = MRP_PLUGIN_ARG_TYPE_DOUBLE;
+ a->dbl = strtod(arg->str + 7, &end);
+
+ if (end && !*end)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else if (!strncmp(arg->str, "object:", prfx=7) ||
+ !strncmp(arg->str, "json:" , prfx=5)) {
+ mrp_json_t **json = &a->obj.json;
+ char *jstr = a->obj.str + prfx;
+ int jlen = strlen(jstr);
+
+ if (mrp_json_parse_object(&jstr, &jlen, json) < 0 || jlen != 0)
+ return FALSE;
+ else {
+ a->type = MRP_PLUGIN_ARG_TYPE_OBJECT;
+ return TRUE;
+ }
+ }
+ else {
+ if (!strcasecmp(arg->str, "TRUE") ||
+ !strcasecmp(arg->str, "FALSE")) {
+ a->type = MRP_PLUGIN_ARG_TYPE_BOOL;
+ a->bln = arg->str[0] == 't' || arg->str[0] == 'T';
+
+ return TRUE;
+ }
+ if (arg->str[0] == '-' || arg->str[0] == '+' ||
+ (arg->str[0] == '0' && arg->str[1] == 'x') ||
+ ('0' <= arg->str[0] && arg->str[0] <= '9')) {
+ a->i32 = strtol(arg->str, &end, 0);
+
+ if (end && !*end) {
+ a->type = MRP_PLUGIN_ARG_TYPE_INT32;
+ return TRUE;
+ }
+ a->dbl = strtod(arg->str, &end);
+
+ if (end && !*end) {
+ a->type = MRP_PLUGIN_ARG_TYPE_DOUBLE;
+ return TRUE;
+ }
+ }
+ else {
+ value = arg->str;
+ goto string;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+static int parse_plugin_args(mrp_plugin_t *plugin,
+ mrp_plugin_arg_t *argv, int argc)
+{
+ mrp_plugin_descr_t *descr;
+ mrp_plugin_arg_t *valid, *args, *pa, *a, *rest;
+ int i, j, cnt;
+
+ if (argv == NULL) {
+ plugin->args = plugin->descriptor->args;
+ return TRUE;
+ }
+
+ descr = plugin->descriptor;
+ valid = descr->args;
+
+ if (valid == NULL && argv != NULL) {
+ mrp_log_error("Plugin '%s' (%s) does not take any arguments.",
+ plugin->instance, descr->name);
+ return FALSE;
+ }
+
+ if ((args = mrp_allocz_array(typeof(*args), descr->narg)) == NULL) {
+ mrp_log_error("Failed to allocate arguments for plugin '%s'.",
+ plugin->instance);
+ return FALSE;
+ }
+
+ memcpy(args, descr->args, descr->narg * sizeof(*args));
+ plugin->args = args;
+
+ for (i = 0, pa = plugin->args; i < descr->narg; i++, pa++) {
+ if (pa->type == MRP_PLUGIN_ARG_TYPE_OBJECT) {
+ mrp_json_t **json = &pa->obj.json;
+ char *jstr = pa->obj.str;
+ int jlen = strlen(jstr);
+
+ if (mrp_json_parse_object(&jstr, &jlen, json) < 0 || jlen != 0)
+ return FALSE;
+ }
+ }
+
+ rest = NULL;
+ j = 0;
+ for (i = 0, a = argv; i < argc; i++, a++) {
+ for (cnt = 0, pa = NULL; pa == NULL && cnt < descr->narg; cnt++) {
+ if (args[j].type != MRP_PLUGIN_ARG_TYPE_UNDECL) {
+ if (!strcmp(a->key, args[j].key))
+ pa = args + j;
+ }
+ else
+ rest = args + j;
+
+ if (++j >= descr->narg)
+ j = 0;
+ }
+
+ if (pa != NULL) {
+ if (!parse_plugin_arg(a, pa)) {
+ mrp_log_error("Invalid argument '%s' for plugin '%s'.",
+ a->key, plugin->instance);
+ return FALSE;
+ }
+ }
+ else if (rest != NULL) {
+ if (!parse_undeclared_arg(a, rest)) {
+ mrp_log_error("Failed to parse argument '%s' for plugin '%s'.",
+ a->key, plugin->instance);
+ return FALSE;
+ }
+ }
+ else {
+ mrp_log_error("Plugin '%s' (%s) does not support argument '%s'",
+ plugin->instance, descr->name, a->key);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+mrp_plugin_arg_t *mrp_plugin_find_undecl_arg(mrp_plugin_arg_t *unspec,
+ const char *key,
+ mrp_plugin_arg_type_t type)
+{
+ mrp_plugin_arg_t *arg;
+ int i;
+
+ for (i = 0, arg = unspec->rest.args; i < unspec->rest.narg; i++, arg++)
+ if (!strcmp(arg->key, key) && (!type || type == arg->type))
+ return arg;
+
+ return NULL;
+}
+
+
+static int export_plugin_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t *methods = plugin->descriptor->exports, *m;
+ int nmethod = plugin->descriptor->nexport, i;
+
+ for (i = 0, m = methods; i < nmethod; i++, m++) {
+ m->plugin = plugin;
+ if (mrp_export_method(m) != 0) {
+ mrp_log_error("Failed to export method %s from plugin %s.",
+ m->name, plugin->instance);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static int remove_plugin_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t *methods = plugin->descriptor->exports, *m;
+ int nmethod = plugin->descriptor->nexport, i;
+ int success = TRUE;
+
+ for (i = 0, m = methods; i < nmethod; i++, m++) {
+ m->plugin = plugin;
+ if (mrp_remove_method(m) != 0) {
+ mrp_log_error("Failed to remove exported method %s of plugin %s.",
+ m->name, plugin->instance);
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
+
+
+static int import_plugin_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t *methods = plugin->descriptor->imports, *m;
+ int nmethod = plugin->descriptor->nimport, i;
+
+ for (i = 0, m = methods; i < nmethod; i++, m++) {
+ if (mrp_import_method(m->name, m->signature,
+ (void **)m->native_ptr, NULL, NULL) != 0) {
+ mrp_log_error("Failed to import method %s (%s) for plugin %s.",
+ m->name, m->signature, plugin->instance);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static int release_plugin_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t *methods = plugin->descriptor->imports, *m;
+ int nmethod = plugin->descriptor->nimport, i;
+ int success = TRUE;
+
+ for (i = 0, m = methods; i < nmethod; i++, m++) {
+ if (mrp_release_method(m->name, m->signature,
+ (void **)m->native_ptr, NULL) != 0) {
+ mrp_log_error("Failed to release imported method %s of plugin %s.",
+ m->name, plugin->instance);
+ success = FALSE;
+ }
+ }
+
+ return success;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_PLUGIN_H__
+#define __MURPHY_PLUGIN_H__
+
+#include <stdbool.h>
+#include <dlfcn.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/refcnt.h>
+#include <murphy/common/json.h>
+#include <murphy/core/context.h>
+#include <murphy/core/console-command.h>
+
+typedef struct mrp_plugin_s mrp_plugin_t;
+
+#include <murphy/core/method.h>
+
+#ifndef MRP_DEFAULT_PLUGIN_DIR
+# define MRP_DEFAULT_PLUGIN_DIR LIBDIR"/murphy/plugins"
+#endif
+
+#define MRP_PLUGIN_DESCRIPTOR "mrp_get_plugin_descriptor"
+
+
+/*
+ * names of plugin-related events we emit
+ */
+
+#define MRP_PLUGIN_BUS "plugin-bus"
+#define MRP_PLUGIN_EVENT_LOADED "plugin-loaded"
+#define MRP_PLUGIN_EVENT_STARTED "plugin-started"
+#define MRP_PLUGIN_EVENT_FAILED "plugin-failed"
+#define MRP_PLUGIN_EVENT_STOPPING "plugin-stopping"
+#define MRP_PLUGIN_EVENT_STOPPED "plugin-stopped"
+#define MRP_PLUGIN_EVENT_UNLOADED "plugin-unloaded"
+
+
+/*
+ * event message data tags
+ */
+
+#define MRP_PLUGIN_TAG_PLUGIN ((uint16_t)1) /* plugin name string */
+#define MRP_PLUGIN_TAG_INSTANCE ((uint16_t)2) /* plugin instance string */
+
+
+/*
+ * plugin arguments
+ */
+
+typedef enum {
+ MRP_PLUGIN_ARG_TYPE_UNKNOWN = 0,
+ MRP_PLUGIN_ARG_TYPE_STRING,
+ MRP_PLUGIN_ARG_TYPE_BOOL,
+ MRP_PLUGIN_ARG_TYPE_UINT32,
+ MRP_PLUGIN_ARG_TYPE_INT32,
+ MRP_PLUGIN_ARG_TYPE_DOUBLE,
+ MRP_PLUGIN_ARG_TYPE_OBJECT,
+ MRP_PLUGIN_ARG_TYPE_UNDECL,
+ /* add more as needed */
+} mrp_plugin_arg_type_t;
+
+typedef struct mrp_plugin_arg_s mrp_plugin_arg_t;
+
+struct mrp_plugin_arg_s {
+ char *key; /* plugin argument name */
+ mrp_plugin_arg_type_t type; /* plugin argument type */
+ union { /* default/supplied value */
+ char *str; /* string values */
+ bool bln; /* boolean values */
+ uint32_t u32; /* 32-bit unsigned values */
+ int32_t i32; /* 32-bit signed values */
+ double dbl; /* double prec. floating pt. values */
+ struct { /* a JSON object */
+ char *str;
+ mrp_json_t *json;
+ } obj;
+ struct { /* other undeclared arguments */
+ mrp_plugin_arg_t *args;
+ int narg;
+ } rest;
+ };
+};
+
+
+/** Macro for declaring a plugin argument table. */
+#define MRP_PLUGIN_ARGUMENTS(table, ...) \
+ static mrp_plugin_arg_t table[] = \
+ __VA_ARGS__ \
+
+
+/** Convenience macros for setting up argument tables with type and defaults. */
+#define MRP_PLUGIN_ARG_STRING(name, defval) \
+ { key: name, type: MRP_PLUGIN_ARG_TYPE_STRING, { str: defval } }
+
+#define MRP_PLUGIN_ARG_BOOL(name, defval) \
+ { key: name, type: MRP_PLUGIN_ARG_TYPE_BOOL , { bln: !!defval } }
+
+#define MRP_PLUGIN_ARG_UINT32(name, defval) \
+ { key: name, type: MRP_PLUGIN_ARG_TYPE_UINT32, { u32: defval } }
+
+#define MRP_PLUGIN_ARG_INT32(name, defval) \
+ { key: name, type: MRP_PLUGIN_ARG_TYPE_INT32 , { i32: defval } }
+
+#define MRP_PLUGIN_ARG_DOUBLE(name, defval) \
+ { key: name, type: MRP_PLUGIN_ARG_TYPE_DOUBLE, { dbl: defval } }
+
+#define MRP_PLUGIN_ARG_OBJECT(name, defval) \
+ { key: name, type: MRP_PLUGIN_ARG_TYPE_OBJECT, \
+ { obj: { str: defval, json: NULL } } }
+
+#define MRP_PLUGIN_ARG_UNDECL(name, defval) \
+ { key: "*", type: MRP_PLUGIN_ARG_TYPE_UNDECL, { str: NULL } }
+
+/** Similar convenience macros for indexed argument access. */
+#define MRP_PLUGIN_ARGIDX_STRING(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_STRING(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_STRING(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_STRING(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_BOOL(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_BOOL(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_UINT32(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_UINT32(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_INT32(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_INT32(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_DOUBLE(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_DOUBLE(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_OBJECT(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_OBJECT(name, defval)
+
+#define MRP_PLUGIN_ARGIDX_UNDECL(idx, name, defval) \
+ [idx] MRP_PLUGIN_ARG_UNDECL(name, defval)
+
+/** Macro for looping through all collected undeclared arguments. */
+#define mrp_plugin_foreach_undecl_arg(_undecl, _arg) \
+ for ((_arg) = (_undecl)->rest.args; \
+ (_arg) - (_undecl)->rest.args < (_undecl)->rest.narg; \
+ (_arg)++)
+
+
+/**
+ * Generic convenience macro for indexed argument access.
+ *
+ * Here is how you can use these macros to declare and access your plugin
+ * arguments:
+ *
+ * #define TEST_HELP "Just a stupid test..."
+ * #define TEST_DESCRIPTION "A test plugin."
+ * #define TEST_AUTHORS "D. Duck <donald.duck@ducksburg.org>"
+ *
+ * enum {
+ * ARG_FOO,
+ * ARG_BAR,
+ * ARG_FOOBAR,
+ * ARG_BARFOO
+ * };
+ *
+ * mrp_plugin_arg_t test_args[] = {
+ * MRP_PLUGIN_ARGIDX(ARG_FOO , STRING, "foo" , "default foo"),
+ * MRP_PLUGIN_ARGIDX(ARG_BAR , BOOL , "bar" , FALSE ),
+ * MRP_PLUGIN_ARGIDX(ARG_FOOBAR, UINT32, "foobar", 1984 ),
+ * MRP_PLUGIN_ARGIDX(ARG_BARFOO, DOUBLE, "barfoo", 3.141 ),
+ * };
+ *
+ * static int test_init(mrp_plugin_t *plugin)
+ * {
+ * mrp_plugin_arg_t *args = plugin->args;
+ *
+ * if (args[ARG_BAR].bln) {
+ * mrp_log_info(" foo: %s", args[ARG_FOO].str);
+ * mrp_log_info("foobar: %u", args[ARG_FOOBAR].u32);
+ * mrp_log_info("barfoo: %f", args[ARG_BARFOO].dbl);
+ * }
+ * else
+ * mrp_log_info("I was not asked to dump my arguments...");
+ * ...
+ * }
+ *
+ * MURPHY_REGISTER_PLUGIN("test", TEST_DESCRIPTION, TEST_AUTHORS, TEST_HELP,
+ * MRP_MULTIPLE, test_init, test_exit, test_args);
+ */
+
+#define MRP_PLUGIN_ARGIDX(idx, type, name, defval) \
+ [idx] MRP_PLUGIN_ARG_##type(name, defval)
+
+
+/*
+ * plugin API version
+ */
+
+#define MRP_PLUGIN_API_MAJOR 0
+#define MRP_PLUGIN_API_MINOR 1
+
+#define MRP_PLUGIN_API_VERSION \
+ MRP_VERSION_INT(MRP_PLUGIN_API_MAJOR, MRP_PLUGIN_API_MINOR, 0)
+
+#define MRP_PLUGIN_API_VERSION_STRING \
+ MRP_VERSION_STRING(MRP_PLUGIN_API_MAJOR, MRP_PLUGIN_API_MINOR, 0)
+
+
+/*
+ * plugin descriptors
+ */
+
+
+typedef int (*mrp_plugin_init_t)(mrp_plugin_t *);
+typedef void (*mrp_plugin_exit_t)(mrp_plugin_t *);
+
+#define MRP_SINGLETON TRUE
+#define MRP_MULTIPLE FALSE
+
+typedef struct {
+ char *name; /* plugin name */
+ char *path; /* plugin path */
+ mrp_plugin_init_t init; /* initialize plugin */
+ mrp_plugin_exit_t exit; /* cleanup plugin */
+ mrp_plugin_arg_t *args; /* table of valid arguments */
+ int narg; /* number of valid arguments */
+ int core : 1; /* is this a core plugin? */
+ int singleton : 1; /* deny multiple instances? */
+ int ninstance; /* number of instances */
+ /* miscallaneous plugin metadata */
+ int version; /* plugin version */
+ int mrp_version; /* murphy API version */
+ const char *description; /* plugin description */
+ const char *authors; /* plugin authors */
+ const char *help; /* plugin help string */
+ mrp_console_group_t *cmds; /* default console commands */
+ mrp_method_descr_t *exports; /* exported methods */
+ int nexport; /* number of exported methods */
+ mrp_method_descr_t *imports; /* imported methods */
+ int nimport; /* number of imported methods */
+} mrp_plugin_descr_t;
+
+
+/*
+ * plugins
+ */
+
+typedef enum {
+ MRP_PLUGIN_LOADED = 0, /* has been loaded */
+ MRP_PLUGIN_RUNNING, /* has been started */
+ MRP_PLUGIN_STOPPED, /* has been stopped */
+} mrp_plugin_state_t;
+
+struct mrp_plugin_s {
+ char *path; /* plugin path */
+ char *instance; /* plugin instance */
+ mrp_list_hook_t hook; /* hook to list of plugins */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_plugin_descr_t *descriptor; /* plugin descriptor */
+ void *handle; /* DSO handle */
+ mrp_plugin_state_t state; /* plugin state */
+ mrp_refcnt_t refcnt; /* reference count */
+ void *data; /* private plugin data */
+ mrp_plugin_arg_t *args; /* plugin arguments */
+ mrp_console_group_t *cmds; /* default console commands */
+ int may_fail : 1; /* load / start may fail */
+};
+
+
+#ifdef __MURPHY_BUILTIN_PLUGIN__
+/* statically linked in plugins */
+# define __MURPHY_REGISTER_PLUGIN(_name, \
+ _version, \
+ _description, \
+ _authors, \
+ _help, \
+ _core, \
+ _single, \
+ _init, \
+ _exit, \
+ _args, \
+ _narg, \
+ _exports, \
+ _nexport, \
+ _imports, \
+ _nimport, \
+ _cmds) \
+ \
+ static void register_plugin(void) __attribute__((constructor)); \
+ \
+ static void register_plugin(void) { \
+ char *path = __FILE__, *base; \
+ static mrp_plugin_descr_t descriptor = { \
+ .name = _name, \
+ .version = _version, \
+ .description = _description, \
+ .authors = _authors, \
+ .mrp_version = MRP_PLUGIN_API_VERSION, \
+ .help = _help, \
+ .init = _init, \
+ .exit = _exit, \
+ .core = _core, \
+ .singleton = _single, \
+ .ninstance = 0, \
+ .args = _args, \
+ .narg = _narg, \
+ .cmds = _cmds, \
+ .exports = _exports, \
+ .nexport = _nexport, \
+ .imports = _imports, \
+ .nimport = _nimport, \
+ }; \
+ \
+ if ((base = strrchr(path, '/')) != NULL) \
+ descriptor.path = base + 1; \
+ else \
+ descriptor.path = (char *)path; \
+ \
+ mrp_register_builtin_plugin(&descriptor); \
+ } \
+ struct mrp_allow_trailing_semicolon
+#else /* dynamically loaded plugins */
+# define __MURPHY_REGISTER_PLUGIN(_name, \
+ _version, \
+ _description, \
+ _authors, \
+ _help, \
+ _core, \
+ _single, \
+ _init, \
+ _exit, \
+ _args, \
+ _narg, \
+ _exports, \
+ _nexport, \
+ _imports, \
+ _nimport, \
+ _cmds) \
+ \
+ mrp_plugin_descr_t *mrp_get_plugin_descriptor(void) { \
+ static mrp_plugin_descr_t descriptor = { \
+ .name = _name, \
+ .version = _version, \
+ .description = _description, \
+ .authors = _authors, \
+ .mrp_version = MRP_PLUGIN_API_VERSION, \
+ .help = _help, \
+ .init = _init, \
+ .exit = _exit, \
+ .core = _core, \
+ .singleton = _single, \
+ .ninstance = 0, \
+ .args = _args, \
+ .narg = _narg, \
+ .cmds = _cmds, \
+ .exports = _exports, \
+ .nexport = _nexport, \
+ .imports = _imports, \
+ .nimport = _nimport, \
+ }; \
+ \
+ return &descriptor; \
+ } \
+ struct mrp_allow_trailing_semicolon
+#endif
+
+
+#define MURPHY_REGISTER_PLUGIN(_n, _v, _d, _a, _h, _s, _i, _e, \
+ _args, _narg, \
+ _exports, _nexport, \
+ _imports, _nimport, \
+ _cmds) \
+ __MURPHY_REGISTER_PLUGIN(_n, _v, _d, _a, _h, FALSE, _s, _i, _e, \
+ _args, _narg, \
+ _exports, _nexport, \
+ _imports, _nimport, \
+ _cmds)
+
+#define MURPHY_REGISTER_CORE_PLUGIN(_n, _v, _d, _a, _h, _s, _i, _e, \
+ _args, _narg, \
+ _exports, _nexport, \
+ _imports, _nimport, \
+ _cmds) \
+ __MURPHY_REGISTER_PLUGIN(_n, _v, _d, _a, _h, TRUE, _s, _i, _e, \
+ _args, _narg, \
+ _exports, _nexport, \
+ _imports, _nimport, \
+ _cmds)
+
+#define MRP_REGISTER_PLUGIN MURPHY_REGISTER_PLUGIN
+#define MRP_REGISTER_CORE_PLUGIN MURPHY_REGISTER_CORE_PLUGIN
+
+
+int mrp_register_builtin_plugin(mrp_plugin_descr_t *descr);
+int mrp_plugin_exists(mrp_context_t *ctx, const char *name);
+mrp_plugin_t *mrp_load_plugin(mrp_context_t *ctx, const char *name,
+ const char *instance, mrp_plugin_arg_t *args,
+ int narg);
+int mrp_load_all_plugins(mrp_context_t *ctx);
+int mrp_unload_plugin(mrp_plugin_t *plugin);
+int mrp_plugin_loaded(mrp_context_t *ctx, const char *name);
+int mrp_start_plugins(mrp_context_t *ctx);
+int mrp_start_plugin(mrp_plugin_t *plugin);
+int mrp_plugin_running(mrp_context_t *ctx, const char *name);
+int mrp_stop_plugin(mrp_plugin_t *plugin);
+int mrp_request_plugin(mrp_context_t *ctx, const char *name,
+ const char *instance);
+void mrp_block_blacklisted_plugins(mrp_context_t *ctx);
+
+mrp_plugin_arg_t *mrp_plugin_find_undecl_arg(mrp_plugin_arg_t *undecl,
+ const char *key,
+ mrp_plugin_arg_type_t type);
+
+
+static inline mrp_plugin_t *mrp_ref_plugin(mrp_plugin_t *plugin)
+{
+ return mrp_ref_obj(plugin, refcnt);
+}
+
+
+static inline int mrp_unref_plugin(mrp_plugin_t *plugin)
+{
+ return mrp_unref_obj(plugin, refcnt);
+}
+
+
+#endif /* __MURPHY_PLUGIN_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+
+#include <murphy/core/scripting.h>
+
+static MRP_LIST_HOOK(interpreters); /* registered interpreters */
+static const char *default_interpreter = "simple"; /* the default interpreter */
+
+
+/*
+ * a context variable
+ */
+
+typedef struct {
+ const char *name; /* variable name */
+ mrp_script_type_t type; /* type if declared */
+ int id; /* variable id */
+} context_var_t;
+
+
+/*
+ * a context frame (a set of context variable values)
+ */
+
+typedef struct context_value_s context_value_t;
+struct context_value_s {
+ int id; /* variable id */
+ mrp_script_value_t value; /* value for this variable */
+ context_value_t *next; /* next value in this frame */
+};
+
+typedef struct context_frame_s context_frame_t;
+struct context_frame_s {
+ context_value_t *values; /* hook to more value */
+ context_frame_t *prev; /* previous frame */
+};
+
+
+/*
+ * table of context variables and context frames
+ */
+
+struct mrp_context_tbl_s {
+ context_var_t *variables; /* known/declared context variables */
+ int nvariable; /* number of variables */
+ mrp_htbl_t *names; /* variable name to id mapping */
+ context_frame_t *frame; /* active frame */
+};
+
+
+
+int mrp_register_interpreter(mrp_interpreter_t *i)
+{
+ MRP_UNUSED(default_interpreter);
+
+ mrp_list_init(&i->hook);
+ mrp_list_append(&interpreters, &i->hook);
+
+ return TRUE;
+}
+
+
+static void unregister_interpreter(mrp_interpreter_t *i)
+{
+ mrp_list_delete(&i->hook);
+ mrp_list_init(&i->hook);
+}
+
+
+int mrp_unregister_interpreter(const char *name)
+{
+ mrp_interpreter_t *i;
+
+ i = mrp_lookup_interpreter(name);
+
+ if (i != NULL) {
+ unregister_interpreter(i);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+
+}
+
+
+mrp_interpreter_t *mrp_lookup_interpreter(const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ mrp_interpreter_t *i;
+
+ mrp_list_foreach(&interpreters, p, n) {
+ i = mrp_list_entry(p, typeof(*i), hook);
+ if (!strcmp(i->name, name))
+ return i;
+ }
+
+ return NULL;
+}
+
+
+mrp_scriptlet_t *mrp_create_script(const char *type, const char *source)
+{
+ mrp_interpreter_t *i;
+ mrp_scriptlet_t *s;
+
+ s = NULL;
+ i = mrp_lookup_interpreter(type);
+
+ if (i != NULL) {
+ s = mrp_allocz(sizeof(*s));
+
+ if (s != NULL) {
+ s->interpreter = i;
+ s->source = mrp_strdup(source);
+
+ if (s->source != NULL)
+ return s;
+ else {
+ mrp_free(s);
+ s = NULL;
+ }
+ }
+ }
+ else
+ errno = ENOENT;
+
+ return NULL;
+}
+
+
+void mrp_destroy_script(mrp_scriptlet_t *script)
+{
+ if (script != NULL) {
+ if (script->interpreter && script->interpreter->cleanup)
+ script->interpreter->cleanup(script);
+
+ mrp_free(script->source);
+ mrp_free(script);
+ }
+}
+
+
+int mrp_compile_script(mrp_scriptlet_t *s)
+{
+ if (s != NULL)
+ return s->interpreter->compile(s);
+ else
+ return 0;
+}
+
+
+int mrp_prepare_script(mrp_scriptlet_t *s)
+{
+ if (s != NULL && s->interpreter->prepare != NULL)
+ return s->interpreter->prepare(s);
+ else
+ return 0;
+}
+
+
+int mrp_execute_script(mrp_scriptlet_t *s, mrp_context_tbl_t *ctbl)
+{
+ if (s != NULL)
+ return s->interpreter->execute(s, ctbl);
+ else
+ return TRUE;
+}
+
+
+char *mrp_print_value(char *buf, size_t size, mrp_script_value_t *value)
+{
+#define HANDLE_TYPE(type, fmt, val) \
+ case MRP_SCRIPT_TYPE_##type: \
+ snprintf(buf, size, fmt, val); \
+ break
+
+ switch (value->type) {
+ HANDLE_TYPE(UNKNOWN, "%s" , "<unknown/invalid type>");
+ HANDLE_TYPE(STRING , "'%s'" , value->str);
+ HANDLE_TYPE(BOOL , "%s" , value->bln ? "true" : "false");
+ HANDLE_TYPE(UINT8 , "%uU8" , value->u8);
+ HANDLE_TYPE(SINT8 , "%dS8" , value->s8);
+ HANDLE_TYPE(UINT16 , "%uU16" , value->u16);
+ HANDLE_TYPE(SINT16 , "%dS16" , value->s16);
+ HANDLE_TYPE(UINT32 , "%uU32" , value->u32);
+ HANDLE_TYPE(SINT32 , "%dS32" , value->s32);
+ HANDLE_TYPE(UINT64 , "%lluU64", (unsigned long long)value->u64);
+ HANDLE_TYPE(SINT64 , "%lldS64", ( signed long long)value->s64);
+ HANDLE_TYPE(DOUBLE , "%f" , value->dbl);
+ default:
+ snprintf(buf, size, "<invalid type 0x%x>", value->type);
+ }
+
+#undef HANDLE_TYPE
+
+ return buf;
+}
+
+
+mrp_context_tbl_t *mrp_create_context_table(void)
+{
+ mrp_context_tbl_t *tbl;
+ mrp_htbl_config_t hcfg;
+
+ tbl = mrp_allocz(sizeof(*tbl));
+
+ if (tbl != NULL) {
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = NULL;
+
+ tbl->frame = NULL;
+ tbl->names = mrp_htbl_create(&hcfg);
+
+ if (tbl->names != NULL)
+ return tbl;
+
+ mrp_free(tbl);
+ }
+
+ return NULL;
+}
+
+
+void mrp_destroy_context_table(mrp_context_tbl_t *tbl)
+{
+ if (tbl != NULL) {
+ while (mrp_pop_context_frame(tbl) == 0)
+ ;
+
+ mrp_htbl_destroy(tbl->names, FALSE);
+ mrp_free(tbl);
+ }
+}
+
+
+static context_var_t *lookup_context_var(mrp_context_tbl_t *tbl,
+ const char *name)
+{
+ int id;
+
+ id = (int)(ptrdiff_t)mrp_htbl_lookup(tbl->names, (void *)name);
+
+ if (0 < id && id <= tbl->nvariable)
+ return tbl->variables + id - 1;
+ else
+ return NULL;
+}
+
+
+int mrp_declare_context_variable(mrp_context_tbl_t *tbl, const char *name,
+ mrp_script_type_t type)
+{
+ context_var_t *var;
+
+ var = lookup_context_var(tbl, name);
+
+ if (var != NULL) {
+ if (!var->type) {
+ var->type = type;
+ return var->id;
+ }
+
+ if (!type || var->type == type)
+ return var->id;
+
+ errno = EEXIST;
+ return -1;
+ }
+ else {
+ size_t o, n;
+
+ o = sizeof(*tbl->variables) * tbl->nvariable;
+ n = sizeof(*tbl->variables) * (tbl->nvariable + 1);
+
+ if (!mrp_reallocz(tbl->variables, o, n))
+ return -1;
+
+ var = tbl->variables + tbl->nvariable++;
+
+ var->name = mrp_strdup(name);
+ var->type = type;
+ var->id = tbl->nvariable; /* this is a 1-based index... */
+
+ if (var->name != NULL) {
+ if (mrp_htbl_insert(tbl->names, (void *)var->name,
+ (void *)(ptrdiff_t)var->id))
+ return var->id;
+ }
+
+ return -1;
+ }
+}
+
+
+int mrp_push_context_frame(mrp_context_tbl_t *tbl)
+{
+ context_frame_t *f;
+
+ f = mrp_allocz(sizeof(*f));
+
+ if (f != NULL) {
+ f->values = NULL;
+ f->prev = tbl->frame;
+ tbl->frame = f;
+
+ mrp_debug("pushed new context frame...");
+
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+int mrp_pop_context_frame(mrp_context_tbl_t *tbl)
+{
+ context_frame_t *f;
+ context_value_t *v, *n;
+
+ f = tbl->frame;
+
+ if (f != NULL) {
+ for (v = f->values; v != NULL; v = n) {
+ n = v->next;
+
+ if (v->value.type == MRP_SCRIPT_TYPE_STRING)
+ mrp_free(v->value.str);
+
+ mrp_debug("popped variable <%d>", v->id);
+ mrp_free(v);
+ }
+
+ tbl->frame = f->prev;
+ mrp_free(f);
+
+ mrp_debug("popped context frame");
+
+ return 0;
+ }
+ else {
+ errno = ENOENT;
+ return -1;
+ }
+}
+
+
+int get_context_id(mrp_context_tbl_t *tbl, const char *name)
+{
+ return (int)(ptrdiff_t)mrp_htbl_lookup(tbl->names, (void *)name);
+}
+
+
+int get_context_value(mrp_context_tbl_t *tbl, int id, mrp_script_value_t *value)
+{
+ context_frame_t *f;
+ context_value_t *v;
+
+ if (0 < id && id <= tbl->nvariable) {
+ for (f = tbl->frame; f != NULL; f = f->prev) {
+ for (v = f->values; v != NULL; v = v->next) {
+ if (v->id == id) {
+ *value = v->value;
+ return 0;
+ }
+ }
+ }
+ }
+
+ value->type = MRP_SCRIPT_TYPE_INVALID;
+ errno = ENOENT;
+
+ return -1;
+}
+
+
+int set_context_value(mrp_context_tbl_t *tbl, int id, mrp_script_value_t *value)
+{
+ context_frame_t *f;
+ context_var_t *var;
+ context_value_t *val;
+ char vbuf[64];
+
+ if (!(0 < id && id <= tbl->nvariable)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ var = tbl->variables + id - 1;
+ if (var->type != MRP_SCRIPT_TYPE_INVALID && var->type != value->type) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ f = tbl->frame;
+ if (f != NULL) {
+ val = mrp_allocz(sizeof(*val));
+
+ if (val != NULL) {
+ val->id = id;
+ val->value = *value;
+
+ if (val->value.type != MRP_SCRIPT_TYPE_STRING ||
+ ((val->value.str = mrp_strdup(val->value.str)) != NULL)) {
+ val->next = f->values;
+ f->values = val;
+
+ mrp_debug("set &%s=%s", var->name,
+ mrp_print_value(vbuf, sizeof(vbuf), value));
+
+ return 0;
+ }
+ else
+ mrp_free(val);
+ }
+ }
+ else
+ errno = ENOSPC;
+
+ return -1;
+}
+
+
+int set_context_values(mrp_context_tbl_t *tbl, int *ids,
+ mrp_script_value_t *values, int nid)
+{
+ int i;
+
+ for (i = 0; i < nid; i++) {
+ if (set_context_value(tbl, ids[i], values + i) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int mrp_get_context_id(mrp_context_tbl_t *tbl, const char *name)
+{
+ int id;
+
+ id = get_context_id(tbl, name);
+
+ if (id <= 0)
+ id = mrp_declare_context_variable(tbl, name, MRP_SCRIPT_TYPE_UNKNOWN);
+
+ return id;
+}
+
+int mrp_get_context_value(mrp_context_tbl_t *tbl, int id,
+ mrp_script_value_t *value)
+{
+ return get_context_value(tbl, id, value);
+
+}
+
+int mrp_set_context_value(mrp_context_tbl_t *tbl, int id,
+ mrp_script_value_t *value)
+{
+ return set_context_value(tbl, id, value);
+}
+
+
+int mrp_get_context_value_by_name(mrp_context_tbl_t *tbl, const char *name,
+ mrp_script_value_t *value)
+{
+ return get_context_value(tbl, get_context_id(tbl, name), value);
+}
+
+
+int mrp_set_context_value_by_name(mrp_context_tbl_t *tbl, const char *name,
+ mrp_script_value_t *value)
+{
+ int id;
+
+ id = get_context_id(tbl, name);
+
+ if (id <= 0) /* auto-declare as an untyped variable */
+ id = mrp_declare_context_variable(tbl, name, MRP_SCRIPT_TYPE_UNKNOWN);
+
+ return set_context_value(tbl, id, value);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CORE_SCRIPTING_H__
+#define __MURPHY_CORE_SCRIPTING_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+#include <murphy/common/log.h>
+
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_interpreter_s mrp_interpreter_t;
+typedef struct mrp_scriptlet_s mrp_scriptlet_t;
+typedef struct mrp_context_tbl_s mrp_context_tbl_t;
+typedef struct mrp_script_value_s mrp_script_value_t;
+
+
+/*
+ * call/execution context passed to exported boilerplate methods
+ *
+ * This context is used to pass positional and keyword arguments
+ * when calling exported scripting boilerplate methods. For instance
+ * the primitive resolver scriptlet interpreter uses this to execute
+ * function calls.
+ */
+
+typedef struct {
+ mrp_script_value_t *args; /* positional arguments */
+ int narg; /* number of arguments */
+ mrp_context_tbl_t *ctbl; /* named arguments */
+} mrp_script_env_t;
+
+
+/*
+ * supported data types to pass to/from scripts (XXX TODO: arrays...)
+ */
+
+#define A(t) MRP_SCRIPT_TYPE_##t
+typedef enum {
+ MRP_SCRIPT_TYPE_UNKNOWN = 0x00,
+ MRP_SCRIPT_TYPE_INVALID = 0x00, /* defined invalid type */
+ MRP_SCRIPT_TYPE_STRING = 0x01, /* string */
+ MRP_SCRIPT_TYPE_BOOL = 0x02, /* boolean */
+ MRP_SCRIPT_TYPE_UINT8 = 0x03, /* signed 8-bit integer */
+ MRP_SCRIPT_TYPE_SINT8 = 0x04, /* unsigned 8-bit integer */
+ MRP_SCRIPT_TYPE_INT8 = A(SINT8), /* alias for SINT8 */
+ MRP_SCRIPT_TYPE_UINT16 = 0x05, /* unsigned 16-bit integer */
+ MRP_SCRIPT_TYPE_SINT16 = 0x06, /* signed 16-bit integer */
+ MRP_SCRIPT_TYPE_INT16 = A(SINT16), /* alias for SINT16 */
+ MRP_SCRIPT_TYPE_UINT32 = 0x07, /* unsigned 32-bit integer */
+ MRP_SCRIPT_TYPE_SINT32 = 0x08, /* signed 32-bit integer */
+ MRP_SCRIPT_TYPE_INT32 = A(SINT32), /* alias for SINT32 */
+ MRP_SCRIPT_TYPE_UINT64 = 0x09, /* unsigned 64-bit integer */
+ MRP_SCRIPT_TYPE_SINT64 = 0x0a, /* signed 64-bit integer */
+ MRP_SCRIPT_TYPE_INT64 = A(SINT64), /* alias for SINT64 */
+ MRP_SCRIPT_TYPE_DOUBLE = 0x0b, /* double-prec. floating point */
+ MRP_SCRIPT_TYPE_ARRAY = 0x80, /* type/marker for arrays */
+} mrp_script_type_t;
+#undef A
+
+#define MRP_SCRIPT_VALUE_UNION union { \
+ char *str; \
+ bool bln; \
+ uint8_t u8; \
+ int8_t s8; \
+ uint16_t u16; \
+ int16_t s16; \
+ uint32_t u32; \
+ int32_t s32; \
+ uint64_t u64; \
+ int64_t s64; \
+ double dbl; \
+ }
+
+typedef MRP_SCRIPT_VALUE_UNION mrp_script_value_u;
+
+struct mrp_script_value_s {
+ mrp_script_type_t type;
+ MRP_SCRIPT_VALUE_UNION;
+};
+
+/** Helper macros for passing values to variadic arglist functions. */
+#define MRP_SCRIPT_STRING(s) MRP_SCRIPT_TYPE_STRING, s
+#define MRP_SCRIPT_BOOL(b) MRP_SCRIPT_TYPE_BOOL , b
+#define MRP_SCRIPT_UINT8(u) MRP_SCRIPT_TYPE_UINT8 , u
+#define MRP_SCRIPT_SINT8(s) MRP_SCRIPT_TYPE_SINT8 , s
+#define MRP_SCRIPT_UINT16(u) MRP_SCRIPT_TYPE_UINT16, u
+#define MRP_SCRIPT_SINT16(s) MRP_SCRIPT_TYPE_SINT16, s
+#define MRP_SCRIPT_UINT32(u) MRP_SCRIPT_TYPE_UINT32, u
+#define MRP_SCRIPT_SINT32(s) MRP_SCRIPT_TYPE_SINT32, s
+#define MRP_SCRIPT_UINT64(u) MRP_SCRIPT_TYPE_UINT64, u
+#define MRP_SCRIPT_DOUBLE(d) MRP_SCRIPT_TYPE_DOUBLE, d
+
+/** Helper macro for initializing/assigning to value arrays. */
+#define __MRP_SCRIPT_VALUE(_t, _m, _v) \
+ (mrp_script_value_t){ .type = MRP_SCRIPT_TYPE_##_t, ._m = _v }
+
+#define MRP_SCRIPT_VALUE_STRING(v) __MRP_SCRIPT_VALUE(STRING, str, v)
+#define MRP_SCRIPT_VALUE_BOOL(v) __MRP_SCRIPT_VALUE(BOOL , bln, v)
+#define MRP_SCRIPT_VALUE_UINT8(v) __MRP_SCRIPT_VALUE(UINT8 , u8 , v)
+#define MRP_SCRIPT_VALUE_SINT8(v) __MRP_SCRIPT_VALUE(SINT8 , s8 , v)
+#define MRP_SCRIPT_VALUE_UINT16(v) __MRP_SCRIPT_VALUE(UINT16, u16, v)
+#define MRP_SCRIPT_VALUE_SINT16(v) __MRP_SCRIPT_VALUE(SINT16, s16, v)
+#define MRP_SCRIPT_VALUE_UINT32(v) __MRP_SCRIPT_VALUE(UINT32, u32, v)
+#define MRP_SCRIPT_VALUE_SINT32(v) __MRP_SCRIPT_VALUE(SINT32, s32, v)
+#define MRP_SCRIPT_VALUE_UINT64(v) __MRP_SCRIPT_VALUE(UINT64, u64, v)
+#define MRP_SCRIPT_VALUE_SINT64(v) __MRP_SCRIPT_VALUE(SINT64, s64, v)
+#define MRP_SCRIPT_VALUE_DOUBLE(v) __MRP_SCRIPT_VALUE(DOUBLE, dbl, v)
+
+/** Print the given value to the given buffer. */
+char *mrp_print_value(char *buf, size_t size, mrp_script_value_t *value);
+
+
+/*
+ * a script interpreter as exposed to the resolver
+ */
+
+struct mrp_interpreter_s {
+ mrp_list_hook_t hook; /* to list of interpreters */
+ const char *name; /* interpreter identifier */
+ void *data; /* opaque global interpreter data */
+ /* interpreter operations */
+ int (*compile)(mrp_scriptlet_t *script);
+ int (*prepare)(mrp_scriptlet_t *script);
+ int (*execute)(mrp_scriptlet_t *script, mrp_context_tbl_t *ctbl);
+ void (*cleanup)(mrp_scriptlet_t *script);
+};
+
+/** Macro to automatically register an interpreter on startup. */
+#define MRP_REGISTER_INTERPRETER(_type, _compile, _prepare, _execute, \
+ _cleanup) \
+ static void auto_register_interpreter(void) \
+ __attribute__((constructor)); \
+ \
+ static void auto_register_interpreter(void) { \
+ static mrp_interpreter_t interpreter = { \
+ .name = _type, \
+ .compile = _compile, \
+ .prepare = _prepare, \
+ .execute = _execute, \
+ .cleanup = _cleanup \
+ }; \
+ \
+ if (!mrp_register_interpreter(&interpreter)) \
+ mrp_log_error("Failed to register interpreter '%s'.", \
+ _type); \
+ else \
+ mrp_log_info("Registered interpreter '%s'.", _type); \
+ } \
+ struct mrp_allow_trailing_semicolon
+
+
+/** Register a new scriptlet interpreter. */
+int mrp_register_interpreter(mrp_interpreter_t *i);
+
+/** Unregister a scriptlet interpreter. */
+int mrp_unregister_interpreter(const char *type);
+
+/** Find a scriptlet interpreter by type. */
+mrp_interpreter_t *mrp_lookup_interpreter(const char *type);
+
+
+/*
+ * a resolver target update script
+ */
+
+struct mrp_scriptlet_s {
+ char *source; /* scriptlet code */
+ mrp_interpreter_t *interpreter; /* interpreter handling this */
+ void *data; /* opaque interpreter data */
+ void *compiled; /* compiled scriptlet */
+};
+
+/** Create a scriptlet of the given type and source. */
+mrp_scriptlet_t *mrp_create_script(const char *type, const char *source);
+
+/** Destroy the given scriptlet, freeing all of its resources. */
+void mrp_destroy_script(mrp_scriptlet_t *script);
+
+/** Compile the given scriptlet. */
+int mrp_compile_script(mrp_scriptlet_t *s);
+
+/** Prepare the given scriptlet for execution. */
+int mrp_prepare_script(mrp_scriptlet_t *s);
+
+/** Execute the given scriptlet with the given context variables. */
+int mrp_execute_script(mrp_scriptlet_t *s, mrp_context_tbl_t *ctbl);
+
+
+
+/*
+ * Context variable (keyword argument) handling.
+ * XXX TODO: Uhmm... this needs to be rethought/redone. :-(
+ */
+
+mrp_context_tbl_t *mrp_create_context_table(void);
+void mrp_destroy_context_table(mrp_context_tbl_t *tbl);
+int mrp_declare_context_variable(mrp_context_tbl_t *tbl, const char *name,
+ mrp_script_type_t type);
+int mrp_push_context_frame(mrp_context_tbl_t *tbl);
+int mrp_pop_context_frame(mrp_context_tbl_t *tbl);
+int mrp_get_context_id(mrp_context_tbl_t *tbl, const char *name);
+int mrp_get_context_value(mrp_context_tbl_t *tbl, int id,
+ mrp_script_value_t *value);
+int mrp_set_context_value(mrp_context_tbl_t *tbl, int id,
+ mrp_script_value_t *value);
+int mrp_get_context_value_by_name(mrp_context_tbl_t *tbl, const char *name,
+ mrp_script_value_t *value);
+int mrp_set_context_value_by_name(mrp_context_tbl_t *tbl, const char *name,
+ mrp_script_value_t *value);
+
+
+
+
+MRP_CDECL_END
+
+
+
+
+#endif /* __MURPHY_CORE_SCRIPTING_H__ */
--- /dev/null
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir) $(JSON_CFLAGS)
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common/log.h>
+#include <murphy/core/context.h>
+#include <murphy/core/plugin.h>
+#include <murphy/daemon/config.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 1024
+#endif
+#define MAX_ARGS 64
+
+static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
+ int saved_argc, char **saved_argv, char **envp);
+
+/*
+ * command line processing
+ */
+
+static void print_usage(mrp_context_t *ctx, const char *argv0, int exit_code,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [-V [valgrind-path] [valgrind-options]]\n\n"
+ "The possible options are:\n"
+ " -c, --config-file=PATH main configuration file to use\n"
+ " The default configuration file is '%s'.\n"
+ " -C, --config-dir=PATH configuration directory to use\n"
+ " If omitted, defaults to '%s'.\n"
+ " -P, --plugin-dir=PATH load plugins from DIR\n"
+ " The default plugin directory is '%s'.\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable given debug configuration\n"
+ " -D, --list-debug list known debug sites\n"
+ " -f, --foreground don't daemonize\n"
+ " -h, --help show help on usage\n"
+ " -q, --query-plugins show detailed information about\n"
+ " all the available plugins\n"
+ " -B, --blacklist-plugins <list> disable list of plugins\n"
+ " -I, --blacklist-builtin <list> disable list of builtin plugins\n"
+ " -E, --blacklist-dynamic <list> disable list of dynamic plugins\n"
+ " -w, --whitelist-plugins <list> disable list of plugins\n"
+ " -i, --whitelist-builtin <list> disable list of builtin plugins\n"
+ " -e, --whitelist-dynamic <list> disable list of dynamic plugins\n"
+ " -R, --no-poststart-load "
+ "disable post-startup plugin loading\n"
+ " -p, --disable-console disable Murphy debug console\n"
+ " -V, --valgrind run through valgrind\n",
+ argv0, ctx->config_file, ctx->config_dir, ctx->plugin_dir);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void print_plugin_help(mrp_context_t *ctx, int detailed)
+{
+#define PRNT(fmt, arg) snprintf(defval, sizeof(defval), fmt, arg)
+
+ mrp_plugin_t *plugin;
+ mrp_plugin_descr_t *descr;
+ mrp_plugin_arg_t *arg;
+ mrp_list_hook_t *p, *n;
+ char *type, defval[64];
+ int i;
+
+ mrp_load_all_plugins(ctx);
+
+ printf("\nAvailable plugins:\n\n");
+
+ mrp_list_foreach(&ctx->plugins, p, n) {
+ plugin = mrp_list_entry(p, typeof(*plugin), hook);
+ descr = plugin->descriptor;
+
+ printf("- %splugin %s:", plugin->handle ? "" : "Builtin ", descr->name);
+ if (detailed) {
+ printf(" (%s, version %d.%d.%d)\n", plugin->path,
+ MRP_VERSION_MAJOR(descr->version),
+ MRP_VERSION_MINOR(descr->version),
+ MRP_VERSION_MICRO(descr->version));
+ printf(" Authors: %s\n", descr->authors);
+ }
+ else
+ printf("\n");
+
+ if (detailed)
+ printf(" Description:\n %s\n", descr->description);
+
+ if (descr->args != NULL) {
+ printf(" Arguments:\n");
+
+ for (i = 0, arg = descr->args; i < descr->narg; i++, arg++) {
+ printf(" %s: ", arg->key);
+ switch (arg->type) {
+ case MRP_PLUGIN_ARG_TYPE_STRING:
+ type = "string";
+ PRNT("%s", arg->str ? arg->str : "<none>");
+ break;
+ case MRP_PLUGIN_ARG_TYPE_BOOL:
+ type = "boolean";
+ PRNT("%s", arg->bln ? "TRUE" : "FALSE");
+ break;
+ case MRP_PLUGIN_ARG_TYPE_UINT32:
+ type = "unsigned 32-bit integer";
+ PRNT("%u", arg->u32);
+ break;
+ case MRP_PLUGIN_ARG_TYPE_INT32:
+ type = "signed 32-bit integer";
+ PRNT("%d", arg->i32);
+ break;
+ case MRP_PLUGIN_ARG_TYPE_DOUBLE:
+ type = "double-precision floating point";
+ PRNT("%f", arg->dbl);
+ break;
+ default:
+ type = "<unknown argument type>";
+ PRNT("%s", "<unknown>");
+ }
+
+ printf("%s, default value=%s\n", type, defval);
+ }
+ }
+
+ if (descr->help != NULL && descr->help[0])
+ printf(" Help:\n %s\n", descr->help);
+
+ printf("\n");
+ }
+
+ printf("\n");
+
+#if 0
+ printf("Note that you can disable any plugin from the command line by\n");
+ printf("using the '-a name:%s' option.\n", MURPHY_PLUGIN_ARG_DISABLED);
+#endif
+}
+
+
+static void config_set_defaults(mrp_context_t *ctx, char *argv0)
+{
+ static char cfg_file[PATH_MAX], cfg_dir[PATH_MAX], plugin_dir[PATH_MAX];
+ char *e;
+ int l;
+
+ if ((e = strstr(argv0, "/src/murphyd")) != NULL ||
+ (e = strstr(argv0, "/src/.libs/lt-murphyd")) != NULL) {
+ mrp_log_mask_t saved = mrp_log_set_mask(MRP_LOG_MASK_WARNING);
+ mrp_log_warning("***");
+ mrp_log_warning("*** Looks like we are run from the source tree.");
+ mrp_log_warning("*** Runtime defaults will be set accordingly...");
+ mrp_log_warning("***");
+ mrp_log_set_mask(saved);
+
+ l = e - argv0;
+ snprintf(cfg_dir, sizeof(cfg_dir), "%*.*s/src/daemon", l, l, argv0);
+ snprintf(cfg_file, sizeof(cfg_file), "%s/murphy-lua.conf", cfg_dir);
+ snprintf(plugin_dir, sizeof(plugin_dir), "%*.*s/src/.libs",
+ l, l, argv0);
+
+ ctx->config_file = cfg_file;
+ ctx->config_dir = cfg_dir;
+ ctx->plugin_dir = plugin_dir;
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_INFO);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+ ctx->foreground = TRUE;
+ }
+ else {
+ ctx->config_file = MRP_DEFAULT_CONFIG_FILE;
+ ctx->config_dir = MRP_DEFAULT_CONFIG_DIR;
+ ctx->plugin_dir = MRP_DEFAULT_PLUGIN_DIR;
+ ctx->log_mask = MRP_LOG_MASK_ERROR;
+ ctx->log_target = MRP_LOG_TO_STDERR;
+ }
+}
+
+
+void mrp_parse_cmdline(mrp_context_t *ctx, int argc, char **argv, char **envp)
+{
+# define OPTIONS "c:C:l:t:fP:a:vd:hHqB:I:E:w:i:e:RpV"
+ struct option options[] = {
+ { "config-file" , required_argument, NULL, 'c' },
+ { "config-dir" , required_argument, NULL, 'C' },
+ { "plugin-dir" , required_argument, NULL, 'P' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target" , required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "foreground" , no_argument , NULL, 'f' },
+ { "help" , no_argument , NULL, 'h' },
+ { "more-help" , no_argument , NULL, 'H' },
+ { "query-plugins" , no_argument , NULL, 'q' },
+ { "blacklist" , required_argument, NULL, 'B' },
+ { "blacklist-plugins", required_argument, NULL, 'B' },
+ { "blacklist-builtin", required_argument, NULL, 'I' },
+ { "blacklist-dynamic", required_argument, NULL, 'E' },
+ { "whitelist" , required_argument, NULL, 'w' },
+ { "whitelist-plugins", required_argument, NULL, 'w' },
+ { "whitelist-builtin", required_argument, NULL, 'i' },
+ { "whitelist-dynamic", required_argument, NULL, 'e' },
+ { "no-poststart-load", no_argument , NULL, 'R' },
+ { "disable-console" , no_argument , NULL, 'p' },
+ { "valgrind" , optional_argument, NULL, 'V' },
+ { NULL, 0, NULL, 0 }
+ };
+
+# define SAVE_ARG(a) do { \
+ if (saved_argc >= MAX_ARGS) \
+ print_usage(ctx, argv[0], EINVAL, \
+ "too many command line arguments"); \
+ else \
+ saved_argv[saved_argc++] = a; \
+ } while (0)
+# define SAVE_OPT(o) SAVE_ARG(o)
+# define SAVE_OPTARG(o, a) SAVE_ARG(o); SAVE_ARG(a)
+ char *saved_argv[MAX_ARGS];
+ int saved_argc;
+
+ int opt, help;
+
+ config_set_defaults(ctx, argv[0]);
+ mrp_log_set_mask(ctx->log_mask);
+ mrp_log_set_target(ctx->log_target);
+
+ saved_argc = 0;
+ saved_argv[saved_argc++] = argv[0];
+
+ help = FALSE;
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'c':
+ SAVE_OPTARG("-c", optarg);
+ ctx->config_file = optarg;
+ break;
+
+ case 'C':
+ SAVE_OPTARG("-C", optarg);
+ ctx->config_dir = optarg;
+ break;
+
+ case 'P':
+ SAVE_OPTARG("-P", optarg);
+ ctx->plugin_dir = optarg;
+ break;
+
+ case 'v':
+ SAVE_OPT("-v");
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ mrp_log_set_mask(ctx->log_mask);
+ break;
+
+ case 'l':
+ SAVE_OPTARG("-l", optarg);
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(ctx, argv[0], EINVAL,
+ "invalid log level '%s'", optarg);
+ else
+ mrp_log_set_mask(ctx->log_mask);
+ break;
+
+ case 't':
+ SAVE_OPTARG("-t", optarg);
+ ctx->log_target = optarg;
+ break;
+
+ case 'd':
+ SAVE_OPTARG("-d", optarg);
+ ctx->log_mask |= MRP_LOG_MASK_DEBUG;
+ mrp_debug_set_config(optarg);
+ mrp_debug_enable(TRUE);
+ break;
+
+ case 'f':
+ SAVE_OPT("-f");
+ ctx->foreground = TRUE;
+ break;
+
+ case 'h':
+ SAVE_OPT("-h");
+ help++;
+ break;
+
+ case 'H':
+ SAVE_OPT("-H");
+ help += 2;
+ break;
+
+ case 'q':
+ SAVE_OPT("-q");
+ print_plugin_help(ctx, TRUE);
+ break;
+
+ case 'B':
+ if (ctx->blacklist_plugins != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "blacklist option given multiple times");
+ SAVE_OPTARG("-B", optarg);
+ ctx->blacklist_plugins = optarg;
+ break;
+ case 'I':
+ if (ctx->blacklist_builtin != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "builtin blacklist option given multiple times");
+ SAVE_OPTARG("-I", optarg);
+ ctx->blacklist_builtin = optarg;
+ break;
+ case 'E':
+ if (ctx->blacklist_dynamic != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "dynamic blacklist option given multiple times");
+ SAVE_OPTARG("-E", optarg);
+ ctx->blacklist_dynamic = optarg;
+ break;
+ case 'w':
+ if (ctx->whitelist_plugins != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "whitelist option given multiple times");
+ SAVE_OPTARG("-w", optarg);
+ ctx->whitelist_plugins = optarg;
+ break;
+ case 'i':
+ if (ctx->whitelist_builtin != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "builtin whitelist option given multiple times");
+ SAVE_OPTARG("-i", optarg);
+ ctx->whitelist_builtin = optarg;
+ break;
+ case 'e':
+ if (ctx->whitelist_dynamic != NULL)
+ print_usage(ctx, argv[0], EINVAL,
+ "dynamic whitelist option given multiple times");
+ SAVE_OPTARG("-e", optarg);
+ ctx->whitelist_dynamic = optarg;
+ break;
+
+ case 'R':
+ SAVE_OPT("-R");
+ ctx->disable_runtime_load = true;
+ break;
+
+ case 'p':
+ SAVE_OPT("-p");
+ ctx->disable_console = TRUE;
+ break;
+ case 'V':
+ valgrind(optarg, argc, argv, optind, saved_argc, saved_argv, envp);
+ break;
+
+ default:
+ print_usage(ctx, argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ if (help) {
+ print_usage(ctx, argv[0], -1, "");
+ if (help > 1)
+ print_plugin_help(ctx, FALSE);
+ exit(0);
+ }
+
+}
+
+
+
+/*
+ * configuration file processing
+ */
+
+typedef struct {
+ char buf[MRP_CFG_MAXLINE]; /* input buffer */
+ char *token; /* current token */
+ char *in; /* filling pointer */
+ char *out; /* consuming pointer */
+ char *next; /* next token buffer position */
+ int fd; /* input file */
+ int error; /* whether has encounted and error */
+ char *file; /* file being processed */
+ int line; /* line number */
+ int next_newline;
+ int was_newline;
+} input_t;
+
+
+#define COMMON_ACTION_FIELDS \
+ action_type_t type; /* action to execute */ \
+ mrp_list_hook_t hook /* to command sequence */
+
+typedef enum { /* action types */
+ ACTION_UNKNOWN = 0,
+ ACTION_LOAD, /* load a plugin */
+ ACTION_TRYLOAD, /* load a plugin, ignore errors */
+ ACTION_IF, /* if-else branch */
+ ACTION_SETCFG, /* set a config variable */
+ ACTION_INFO, /* emit an info message */
+ ACTION_WARNING, /* emit a warning message */
+ ACTION_ERROR, /* emit and error message and exit */
+ ACTION_MAX,
+} action_type_t;
+
+typedef enum { /* branch operators */
+ BR_UNKNOWN = 0,
+ BR_PLUGIN_EXISTS, /* test if a plugin exists */
+} branch_op_t;
+
+typedef struct { /* a generic action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+} any_action_t;
+
+typedef struct { /* a command-type of action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ char **args; /* arguments for the action */
+ int narg; /* number of arguments */
+} cmd_action_t;
+
+typedef struct { /* a command-type of action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ char *name; /* plugin to load */
+ char *instance; /* load as this instance */
+ mrp_plugin_arg_t *args; /* plugin arguments */
+ int narg; /* number of arguments */
+} load_action_t;
+
+typedef struct { /* a branch test action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ branch_op_t op; /* branch operator */
+ char *arg1; /* argument for the operator */
+ char *arg2; /* argument for the operator */
+ mrp_list_hook_t pos; /* postitive branch */
+ mrp_list_hook_t neg; /* negative branch */
+} branch_action_t;
+
+typedef struct { /* a branch test action */
+ COMMON_ACTION_FIELDS; /* type, hook */
+ char *message; /* message to show */
+} message_action_t;
+
+typedef enum {
+ CFGVAR_UNKNOWN = 0,
+ CFGVAR_RESOLVER_RULES, /* resolver ruleset file */
+} cfgvar_t;
+
+typedef struct {
+ COMMON_ACTION_FIELDS;
+ cfgvar_t id; /* confguration variable */
+ char *value; /* value for variable */
+} setcfg_action_t;
+
+typedef struct {
+ const char *keyword;
+ any_action_t *(*parse)(input_t *in, char **args, int narg);
+ int (*exec)(mrp_context_t *ctx, any_action_t *action);
+ void (*free)(any_action_t *a);
+} action_descr_t;
+
+
+
+static any_action_t *parse_action(input_t *in, char **args, int narg);
+static any_action_t *parse_load(input_t *in, char **argv, int argc);
+static any_action_t *parse_if_else(input_t *in, char **argv, int argc);
+static any_action_t *parse_setcfg(input_t *in, char **argv, int argc);
+static any_action_t *parse_message(input_t *in, char **argv, int argc);
+static int exec_action(mrp_context_t *ctx, any_action_t *action);
+static int exec_load(mrp_context_t *ctx, any_action_t *action);
+static int exec_if_else(mrp_context_t *ctx, any_action_t *action);
+static int exec_setcfg(mrp_context_t *ctx, any_action_t *action);
+static int exec_message(mrp_context_t *ctx, any_action_t *action);
+static void free_action(any_action_t *action);
+static void free_if_else(any_action_t *action);
+static void free_load(any_action_t *action);
+static void free_setcfg(any_action_t *action);
+static void free_message(any_action_t *action);
+
+static char *get_next_token(input_t *in);
+static int get_next_line(input_t *in, char **args, size_t size);
+static char *replace_tokens(input_t *in, char *first, char *last,
+ char *token, int size);
+
+#define A(type, keyword, parse, exec, free) \
+ [ACTION_##type] = { MRP_KEYWORD_##keyword, parse, exec, free }
+
+static action_descr_t actions[] = {
+ [ACTION_UNKNOWN] = { NULL, NULL, NULL, NULL },
+
+ A(LOAD , LOAD , parse_load , exec_load , free_load),
+ A(TRYLOAD, TRYLOAD, parse_load , exec_load , free_load),
+ A(IF , IF , parse_if_else, exec_if_else, free_if_else),
+ A(SETCFG , SETCFG , parse_setcfg , exec_setcfg , free_setcfg),
+ A(INFO , INFO , parse_message, exec_message, free_message),
+ A(WARNING, WARNING, parse_message, exec_message, free_message),
+ A(ERROR , ERROR , parse_message, exec_message, free_message),
+
+ [ACTION_MAX] = { NULL, NULL, NULL, NULL }
+};
+
+#undef A
+
+
+
+mrp_cfgfile_t *mrp_parse_cfgfile(const char *path)
+{
+ mrp_cfgfile_t *cfg = NULL;
+ input_t input;
+ char *args[MRP_CFG_MAXARGS];
+ int narg;
+ any_action_t *a;
+
+ memset(&input, 0, sizeof(input));
+ input.token = input.buf;
+ input.in = input.buf;
+ input.out = input.buf;
+ input.next = input.buf;
+ input.fd = open(path, O_RDONLY);
+ input.file = (char *)path;
+ input.line = 1;
+
+ if (input.fd < 0) {
+ mrp_log_error("Failed to open configuration file '%s' (%d: %s).",
+ path, errno, strerror(errno));
+ goto fail;
+ }
+
+ cfg = mrp_allocz(sizeof(*cfg));
+
+ if (cfg == NULL) {
+ mrp_log_error("Failed to allocate configuration file buffer.");
+ goto fail;
+ }
+
+ mrp_list_init(&cfg->actions);
+
+ while ((narg = get_next_line(&input, args, MRP_ARRAY_SIZE(args))) > 0) {
+ a = parse_action(&input, args, narg);
+
+ if (a != NULL)
+ mrp_list_append(&cfg->actions, &a->hook);
+ else
+ goto fail;
+ }
+
+ if (narg == 0)
+ return cfg;
+
+ fail:
+ if (input.fd >= 0)
+ close(input.fd);
+ if (cfg)
+ mrp_free_cfgfile(cfg);
+
+ return NULL;
+}
+
+
+void mrp_free_cfgfile(mrp_cfgfile_t *cfg)
+{
+ mrp_list_hook_t *p, *n;
+ any_action_t *a;
+
+ mrp_list_foreach(&cfg->actions, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_action(a);
+ }
+
+ mrp_free(cfg);
+}
+
+
+int mrp_exec_cfgfile(mrp_context_t *ctx, mrp_cfgfile_t *cfg)
+{
+ mrp_list_hook_t *p, *n;
+ any_action_t *a;
+
+ mrp_list_foreach(&cfg->actions, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ if (!exec_action(ctx, a))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static any_action_t *parse_action(input_t *in, char **args, int narg)
+{
+ action_descr_t *ad = actions + 1;
+
+ while (ad->keyword != NULL) {
+ if (!strcmp(args[0], ad->keyword))
+ return ad->parse(in, args, narg);
+ ad++;
+ }
+
+ mrp_log_error("Unknown command '%s' in file '%s'.", args[0], in->file);
+ return NULL;
+}
+
+
+static void free_action(any_action_t *action)
+{
+ mrp_list_delete(&action->hook);
+
+ if (ACTION_UNKNOWN < action->type && action->type < ACTION_MAX)
+ actions[action->type].free(action);
+ else {
+ mrp_log_error("Unknown configuration action of type 0x%x.",
+ action->type);
+ mrp_free(action);
+ }
+}
+
+
+static int exec_action(mrp_context_t *ctx, any_action_t *action)
+{
+ if (ACTION_UNKNOWN < action->type && action->type < ACTION_MAX)
+ return actions[action->type].exec(ctx, action);
+ else {
+ mrp_log_error("Unknown configuration action of type 0x%x.",
+ action->type);
+ return FALSE;
+ }
+}
+
+
+static any_action_t *parse_load(input_t *in, char **argv, int argc)
+{
+ load_action_t *action;
+ action_type_t type;
+ mrp_plugin_arg_t *args, *a;
+ int narg, i, start;
+ char *k, *v;
+
+ MRP_UNUSED(in);
+
+ if (!strcmp(argv[0], MRP_KEYWORD_LOAD))
+ type = ACTION_LOAD;
+ else
+ type = ACTION_TRYLOAD;
+
+ if (argc < 2 || (action = mrp_allocz(sizeof(*action))) == NULL) {
+ mrp_log_error("Failed to allocate load config action.");
+ return NULL;
+ }
+
+ mrp_list_init(&action->hook);
+ action->type = type;
+ action->name = mrp_strdup(argv[1]);
+
+ if (action->name == NULL) {
+ mrp_log_error("Failed to allocate load config action.");
+ mrp_free(action);
+ return NULL;
+ }
+
+ args = NULL;
+
+ if (argc > 3 && !strcmp(argv[2], MRP_KEYWORD_AS)) {
+ /* [try-]load-plugin name as instance [args...] */
+ action->instance = mrp_strdup(argv[3]);
+ start = 4;
+
+ if (action->instance == NULL) {
+ mrp_log_error("Failed to allocate load config action.");
+ mrp_free(action->name);
+ mrp_free(action);
+ goto fail;
+ }
+ }
+ else {
+ /* [try-]load-plugin name [args...] */
+ start = 2;
+ }
+
+ narg = 0;
+ if (start < argc) {
+ if ((args = mrp_allocz_array(typeof(*args), argc - 1)) != NULL) {
+ for (i = start, a = args; i < argc; i++, a++) {
+ if (*argv[i] == MRP_START_COMMENT)
+ break;
+
+ mrp_debug("argument #%d: '%s'", i - start, argv[i]);
+
+ k = argv[i];
+ v = strchr(k, '=');
+
+ if (v != NULL)
+ *v++ = '\0';
+ else {
+ if (i + 2 < argc) {
+ if (argv[i+1][0] == '=' && argv[i+1][1] == '\0') {
+ v = argv[i + 2];
+ i += 2;
+ }
+ }
+ else {
+ mrp_log_error("Invalid plugin load argument '%s'.", k);
+ goto fail;
+ }
+ }
+
+ a->type = MRP_PLUGIN_ARG_TYPE_STRING;
+ a->key = mrp_strdup(k);
+ a->str = v ? mrp_strdup(v) : NULL;
+ narg++;
+
+ if (a->key == NULL || (a->str == NULL && v != NULL)) {
+ mrp_log_error("Failed to allocate plugin arg %s%s%s.",
+ k, v ? "=" : "", v ? v : "");
+ goto fail;
+ }
+ }
+ }
+ }
+
+ action->args = args;
+ action->narg = narg;
+
+ return (any_action_t *)action;
+
+
+ fail:
+ if (args != NULL) {
+ for (i = 1; i < argc && args[i].key != NULL; i++) {
+ mrp_free(args[i].key);
+ mrp_free(args[i].str);
+ }
+ mrp_free(args);
+ }
+
+ return NULL;
+}
+
+
+static void free_load(any_action_t *action)
+{
+ load_action_t *load = (load_action_t *)action;
+ int i;
+
+ if (load != NULL) {
+ mrp_free(load->name);
+
+ for (i = 0; i < load->narg; i++) {
+ mrp_free(load->args[i].key);
+ mrp_free(load->args[i].str);
+ }
+
+ mrp_free(load->args);
+ }
+}
+
+
+static int exec_load(mrp_context_t *ctx, any_action_t *action)
+{
+ load_action_t *load = (load_action_t *)action;
+ mrp_plugin_t *plugin;
+
+ plugin = mrp_load_plugin(ctx, load->name, load->instance,
+ load->args, load->narg);
+
+ if (plugin != NULL) {
+ plugin->may_fail = (load->type == ACTION_TRYLOAD);
+
+ return TRUE;
+ }
+ else
+ return (load->type == ACTION_TRYLOAD);
+}
+
+
+static any_action_t *parse_if_else(input_t *in, char **argv, int argc)
+{
+ branch_action_t *branch;
+ mrp_list_hook_t *actions;
+ any_action_t *a;
+ char *args[MRP_CFG_MAXARGS], *op, *name;
+ int start, narg, pos;
+
+ if (argc < 2) {
+ mrp_log_error("%s:%d: invalid use of if-conditional.",
+ in->file, in->line - 1);
+ return NULL;
+ }
+
+ start = in->line - 1;
+ op = argv[1];
+ name = argv[2];
+
+ if (strcmp(op, MRP_KEYWORD_EXISTS)) {
+ mrp_log_error("%s:%d: unknown operator '%s' in if-conditional.",
+ in->file, in->line - 1, op);
+ }
+
+ branch = mrp_allocz(sizeof(*branch));
+
+ if (branch != NULL) {
+ mrp_list_init(&branch->hook);
+ mrp_list_init(&branch->pos);
+ mrp_list_init(&branch->neg);
+
+ branch->type = ACTION_IF;
+ branch->op = BR_PLUGIN_EXISTS;
+ branch->arg1 = mrp_strdup(name);
+
+ if (branch->arg1 == NULL) {
+ mrp_log_error("Failed to allocate configuration if-conditional.");
+ goto fail;
+ }
+
+ pos = TRUE;
+ actions = &branch->pos;
+ while ((narg = get_next_line(in, args, sizeof(args))) > 0) {
+ if (narg == 1) {
+ if (!strcmp(args[0], MRP_KEYWORD_END))
+ return (any_action_t *)branch;
+
+ if (!strcmp(args[0], MRP_KEYWORD_ELSE)) {
+ if (pos) {
+ actions = &branch->neg;
+ pos = FALSE;
+ }
+ else {
+ mrp_log_error("%s:%d: extra else without if.",
+ in->file, in->line - 1);
+ goto fail;
+ }
+ }
+ }
+ else {
+ a = parse_action(in, args, narg);
+
+ if (a != NULL)
+ mrp_list_append(actions, &a->hook);
+ else
+ goto fail;
+ }
+ }
+ }
+ else {
+ mrp_log_error("Failed to allocate configuration if-conditional.");
+ return NULL;
+ }
+
+ mrp_log_error("%s:%d: unterminated if-conditional (missing 'end')",
+ in->file, start);
+
+ fail:
+ free_action((any_action_t *)branch);
+ return NULL;
+}
+
+
+static void free_if_else(any_action_t *action)
+{
+ branch_action_t *branch = (branch_action_t *)action;
+ any_action_t *a;
+ mrp_list_hook_t *p, *n;
+
+ if (branch != NULL) {
+ mrp_free(branch->arg1);
+ mrp_free(branch->arg2);
+
+ mrp_list_foreach(&branch->pos, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_action(a);
+ }
+
+ mrp_list_foreach(&branch->neg, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+ free_action(a);
+ }
+
+ mrp_free(branch);
+ }
+}
+
+
+static int exec_if_else(mrp_context_t *ctx, any_action_t *action)
+{
+ branch_action_t *branch = (branch_action_t *)action;
+ mrp_list_hook_t *p, *n, *actions;
+ any_action_t *a;
+
+ if (branch->op != BR_PLUGIN_EXISTS || branch->arg1 == NULL)
+ return FALSE;
+
+ if (mrp_plugin_exists(ctx, branch->arg1))
+ actions = &branch->pos;
+ else
+ actions = &branch->neg;
+
+ mrp_list_foreach(actions, p, n) {
+ a = mrp_list_entry(p, typeof(*a), hook);
+
+ if (!exec_action(ctx, a))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static any_action_t *parse_setcfg(input_t *in, char **argv, int argc)
+{
+ setcfg_action_t *action;
+ struct {
+ const char *name;
+ cfgvar_t id;
+ } *var, vartbl[] = {
+ { MRP_CFGVAR_RESOLVER, CFGVAR_RESOLVER_RULES },
+ { NULL , 0 },
+ };
+
+ if (argc < 3) {
+ mrp_log_error("%s:%d: configuration directive %s requires two "
+ "arguments, %d given.", in->file, in->line,
+ MRP_KEYWORD_SETCFG, argc - 1);
+ return NULL;
+ }
+
+ for (var = vartbl; var->name != NULL; var++)
+ if (!strcmp(var->name, argv[1]))
+ break;
+
+ if (var->name == NULL) {
+ mrp_log_error("%s:%d: unknown configuration variable '%s'.",
+ in->file, in->line, argv[1]);
+ return NULL;
+ }
+
+ if ((action = mrp_allocz(sizeof(*action))) == NULL) {
+ mrp_log_error("Failed to allocate %s %s configuration action.",
+ MRP_KEYWORD_SETCFG, argv[1]);
+ return NULL;
+ }
+
+ mrp_list_init(&action->hook);
+ action->type = ACTION_SETCFG;
+ action->id = var->id;
+ action->value = mrp_strdup(argv[2]);
+
+ if (action->value == NULL) {
+ mrp_log_error("Failed to allocate %s %s configuration action.",
+ MRP_KEYWORD_SETCFG, argv[1]);
+ mrp_free(action);
+ return NULL;
+ }
+
+ return (any_action_t *)action;
+}
+
+
+static int exec_setcfg(mrp_context_t *ctx, any_action_t *action)
+{
+ setcfg_action_t *setcfg = (setcfg_action_t *)action;
+
+ switch (setcfg->id) {
+ case CFGVAR_RESOLVER_RULES:
+ if (ctx->resolver_ruleset == NULL) {
+ ctx->resolver_ruleset = setcfg->value;
+ setcfg->value = NULL;
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Multiple resolver rulesets specified (%s, %s).",
+ ctx->resolver_ruleset, setcfg->value);
+ return FALSE;
+ }
+ break;
+ default:
+ mrp_log_error("Invalid configuration setting.");
+ }
+
+ return FALSE;
+}
+
+
+static void free_setcfg(any_action_t *action)
+{
+ setcfg_action_t *setcfg = (setcfg_action_t *)action;
+
+ if (setcfg != NULL) {
+ mrp_free(setcfg->value);
+ mrp_free(setcfg);
+ }
+}
+
+
+static any_action_t *parse_message(input_t *in, char **argv, int argc)
+{
+ message_action_t *msg;
+ action_type_t type;
+ char buf[4096], *p;
+ const char *t;
+ int i, l, n;
+
+ MRP_UNUSED(in);
+
+ if (argc < 2) {
+ mrp_log_error("%s requires at least one argument.", argv[0]);
+ return NULL;
+ }
+
+ if (!strcmp(argv[0], MRP_KEYWORD_ERROR))
+ type = ACTION_ERROR;
+ else if (!strcmp(argv[0], MRP_KEYWORD_WARNING))
+ type = ACTION_WARNING;
+ else if (!strcmp(argv[0], MRP_KEYWORD_INFO))
+ type = ACTION_INFO;
+ else
+ return NULL;
+
+ p = buf;
+ n = sizeof(buf);
+ if ((msg = mrp_allocz(sizeof(*msg))) != NULL) {
+ for (i = 1, t=""; i < argc && n > 0; i++, t=" ") {
+ l = snprintf(p, n, "%s%s", t, argv[i]);
+ p += l;
+ n -= l;
+ }
+
+ msg->type = type;
+ msg->message = mrp_strdup(buf);
+
+ if (msg->message == NULL) {
+ mrp_log_error("Failed to allocate %s config action.", argv[0]);
+ mrp_free(msg);
+ msg = NULL;
+ }
+ }
+
+ return (any_action_t *)msg;
+}
+
+
+static int exec_message(mrp_context_t *ctx, any_action_t *action)
+{
+ message_action_t *msg = (message_action_t *)action;
+
+ MRP_UNUSED(ctx);
+
+ switch (action->type) {
+ case ACTION_ERROR: mrp_log_error("%s", msg->message); exit(1);
+ case ACTION_WARNING: mrp_log_warning("%s", msg->message); return TRUE;
+ case ACTION_INFO: mrp_log_info("%s", msg->message); return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
+static void free_message(any_action_t *action)
+{
+ message_action_t *msg = (message_action_t *)action;
+
+ if (msg != NULL) {
+ mrp_free(msg->message);
+ mrp_free(msg);
+ }
+}
+
+
+static int get_next_line(input_t *in, char **args, size_t size)
+{
+#define BLOCK_START(s) \
+ ((s[0] == '{' || s[0] == '[') && (s[1] == '\0' || s[1] == '\n'))
+#define BLOCK_END(s) \
+ ((s[0] == '}' || s[0] == ']') && (s[1] == '\0' || s[1] == '\n'))
+
+ char *token, *p;
+ char block[2], json[MRP_CFG_MAXLINE];
+ int narg, nest, beg;
+ int i, n, l, tot;
+
+ narg = 0;
+ nest = 0;
+ beg = -1;
+ tot = 0;
+ block[0] = block[1] = '\0';
+ while ((token = get_next_token(in)) != NULL && narg < (int)size) {
+ if (in->error)
+ return -1;
+
+ mrp_debug("read input token '%s'", token);
+
+ if (token[0] != '\n') {
+ if (BLOCK_START(token)) {
+ if (!nest) {
+ mrp_debug("collecting JSON argument");
+
+ block[0] = token[0];
+ block[1] = (block[0] == '{' ? '}' : ']');
+ nest = 1;
+ beg = narg;
+ tot = 1;
+ }
+ else {
+ if (token[0] == block[0])
+ nest++;
+ }
+ }
+
+ args[narg++] = token;
+
+ if (beg >= 0) { /* if collecting, update length */
+ tot += strlen(token) + 1;
+ if (strchr(token, ' ') || strchr(token, '\t'))
+ tot += 2; /* will need quoting */
+ }
+
+ if (BLOCK_END(token) && nest > 0) {
+ if (token[0] == block[1])
+ nest--;
+
+ if (nest == 0) {
+ mrp_debug("finished collecting JSON argument");
+
+ if (tot > (int)sizeof(json) - 1) {
+ mrp_log_error("Maximum token length exceeded.");
+ return -1;
+ }
+
+ p = json;
+ l = tot;
+ for (i = beg; i < narg; i++) {
+ if (strchr(args[i], ' ') || strchr(args[i], '\t'))
+ n = snprintf(p, l, "'%s'", args[i]);
+ else
+ n = snprintf(p, l, "%s", args[i]);
+ if (n >= l)
+ return -1;
+ p += n;
+ l -= n;
+ }
+
+ mrp_debug("collected JSON token: '%s'", json);
+
+ args[beg] = replace_tokens(in, args[beg], args[narg-1],
+ json, (int)(p - json));
+
+ if (args[beg] == NULL) {
+ mrp_log_error("Failed to replace block of tokens.");
+ return -1;
+ }
+ else
+ narg = beg + 1;
+
+ block[0] = '\0';
+ block[1] = '\0';
+ beg = -1;
+ }
+ }
+ }
+ else {
+ if (narg && *args[0] != MRP_START_COMMENT && *args[0] != '\n')
+ return narg;
+ else
+ narg = 0;
+ }
+ }
+
+ if (in->error)
+ return -1;
+
+ if (narg >= (int)size) {
+ mrp_log_error("Too many tokens on line %d of %s.",
+ in->line - 1, in->file);
+ return -1;
+ }
+ else {
+ if (*args[0] != MRP_START_COMMENT && *args[0] != '\n')
+ return narg;
+ else
+ return 0;
+ }
+}
+
+
+static inline void skip_whitespace(input_t *in)
+{
+ while ((*in->out == ' ' || *in->out == '\t') && in->out < in->in)
+ in->out++;
+}
+
+
+static inline void skip_rest_of_line(input_t *in)
+{
+ while (*in->out != '\n' && in->out < in->in)
+ in->out++;
+}
+
+
+static char *replace_tokens(input_t *in, char *first, char *last,
+ char *token, int size)
+{
+ char *beg = first;
+ char *end = last + strlen(last) + 1;
+
+ if (!(in->buf < beg && end < in->out))
+ return NULL;
+
+ if ((end - beg) < size)
+ return NULL;
+
+ strcpy(first, token);
+
+ return first;
+}
+
+
+static char *get_next_token(input_t *in)
+{
+ ssize_t len;
+ int diff, size;
+ int quote, quote_line;
+ char *p, *q;
+
+ /*
+ * Newline:
+ *
+ * If the previous token was terminated by a newline,
+ * take care of properly returning and administering
+ * the newline token here.
+ */
+
+ if (in->next_newline) {
+ in->next_newline = FALSE;
+ in->was_newline = TRUE;
+ in->line++;
+
+ return "\n";
+ }
+
+
+ /*
+ * if we just finished a line, discard all old data/tokens
+ */
+
+ if (*in->token == '\n' || in->was_newline) {
+ diff = in->out - in->buf;
+ size = in->in - in->out;
+ memmove(in->buf, in->out, size);
+ in->out -= diff;
+ in->in -= diff;
+ in->next = in->buf;
+ *in->in = '\0';
+ }
+
+ /*
+ * refill the buffer if we're empty or just flushed all tokens
+ */
+
+ if (in->token == in->buf && in->fd != -1) {
+ size = sizeof(in->buf) - 1 - (in->in - in->buf);
+ len = read(in->fd, in->in, size);
+
+ if (len < size) {
+ close(in->fd);
+ in->fd = -1;
+ }
+
+ if (len < 0) {
+ mrp_log_error("Failed to read from config file (%d: %s).",
+ errno, strerror(errno));
+ in->error = TRUE;
+ close(in->fd);
+ in->fd = -1;
+
+ return NULL;
+ }
+
+ in->in += len;
+ *in->in = '\0';
+ }
+
+ if (in->out >= in->in)
+ return NULL;
+
+ skip_whitespace(in);
+
+ quote = FALSE;
+ quote_line = 0;
+
+ p = in->out;
+ q = in->next;
+ in->token = q;
+
+ while (p < in->in) {
+ /*printf("[%c]\n", *p == '\n' ? '.' : *p);*/
+ switch (*p) {
+ /*
+ * Quoting:
+ *
+ * If we're not within a quote, mark a quote started.
+ * Otherwise if quote matches, close quoting. Otherwise
+ * copy the quoted quote verbatim.
+ */
+ case '\'':
+ case '\"':
+ in->was_newline = FALSE;
+ if (!quote) {
+ quote = *p++;
+ quote_line = in->line;
+ }
+ else {
+ if (*p == quote) {
+ quote = FALSE;
+ quote_line = 0;
+ p++;
+ *q++ = '\0';
+
+ in->out = p;
+ in->next = q;
+
+ return in->token;
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ break;
+
+ /*
+ * Whitespace:
+ *
+ * If we're quoting, copy verbatim. Otherwise mark the end
+ * of the token.
+ */
+ case ' ':
+ case '\t':
+ if (quote)
+ *q++ = *p++;
+ else {
+ p++;
+ *q++ = '\0';
+
+ in->out = p;
+ in->next = q;
+
+ return in->token;
+ }
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Escaping:
+ *
+ * If the last character in the input, copy verbatim.
+ * Otherwise if it escapes a '\n', skip both. Otherwise
+ * copy the escaped character verbatim.
+ */
+ case '\\':
+ if (p < in->in - 1) {
+ p++;
+ if (*p != '\n')
+ *q++ = *p++;
+ else {
+ p++;
+ in->line++;
+ in->out = p;
+ skip_whitespace(in);
+ p = in->out;
+ }
+ }
+ else
+ *q++ = *p++;
+ in->was_newline = FALSE;
+ break;
+
+ /*
+ * Newline:
+ *
+ * We don't allow newlines to be quoted. Otherwise
+ * if the token is not the newline itself, we mark
+ * the next token to be newline and return the token
+ * it terminated.
+ */
+ case '\n':
+ if (quote) {
+ mrp_log_error("%s:%d: Unterminated quote (%c) started "
+ "on line %d.", in->file, in->line, quote,
+ quote_line);
+ in->error = TRUE;
+
+ return NULL;
+ }
+ else {
+ *q = '\0';
+ p++;
+
+ in->out = p;
+ in->next = q;
+
+ if (in->token == q) {
+ in->line++;
+ in->was_newline = TRUE;
+ return "\n";
+ }
+ else {
+ in->next_newline = TRUE;
+ return in->token;
+ }
+ }
+ break;
+
+ /*
+ * Comments:
+ *
+ * Attempt to allow and filter out partial-line comments.
+ *
+ * This has not been thoroughly thought through. Probably
+ * there are broken border-cases. The whole tokenizing loop
+ * has not been written so that it could grow the buffer and
+ * refill it even if even we run out of input and we're not
+ * sure whehter a full token has been consumed... beware.
+ * To be sure, we bail out here if it looks like we exhausted
+ * the input buffer while skipping a comment. This needs to
+ * be thought through properly.
+ */
+ case MRP_START_COMMENT:
+ skip_rest_of_line(in);
+ if (in->out == in->in) {
+ mrp_log_error("%s:%d Exhausted input buffer while skipping "
+ "a comment.", in->file, in->line);
+ in->error = TRUE;
+ return NULL;
+ }
+ else {
+ p = in->out;
+ in->line++;
+ }
+ break;
+
+ default:
+ *q++ = *p++;
+ in->was_newline = FALSE;
+ }
+ }
+
+ if (in->fd == -1) {
+ *q = '\0';
+ in->out = p;
+ in->in = q;
+
+ return in->token;
+ }
+ else {
+ mrp_log_error("Input line %d of file %s exceeds allowed length.",
+ in->line, in->file);
+ return NULL;
+ }
+}
+
+
+/*
+ * bridging to valgrind
+ */
+
+static void valgrind(const char *vg_path, int argc, char **argv, int vg_offs,
+ int saved_argc, char **saved_argv, char **envp)
+{
+#define VG_ARG(a) vg_argv[vg_argc++] = a
+ char *vg_argv[MAX_ARGS + 1];
+ int vg_argc, normal_offs, i;
+
+ vg_argc = 0;
+
+ /* set valgrind binary */
+ VG_ARG(vg_path ? (char *)vg_path : "/usr/bin/valgrind");
+
+ /* add valgrind arguments */
+ for (i = vg_offs; i < argc; i++)
+ VG_ARG(argv[i]);
+
+ /* save offset to normal argument list for fallback */
+ normal_offs = vg_argc;
+
+ /* add our binary and our arguments */
+ for (i = 0; i < saved_argc; i++)
+ vg_argv[vg_argc++] = saved_argv[i];
+
+ /* terminate argument list */
+ VG_ARG(NULL);
+
+ /* try executing through valgrind */
+ mrp_log_warning("Executing through valgrind (%s)...", vg_argv[0]);
+ execve(vg_argv[0], vg_argv, envp);
+
+ /* try falling back to normal execution */
+ mrp_log_error("Executing through valgrind failed (error %d: %s), "
+ "retrying without...", errno, strerror(errno));
+ execve(vg_argv[normal_offs], vg_argv + normal_offs, envp);
+
+ /* can't do either, so just give up */
+ mrp_log_error("Fallback to normal execution failed (error %d: %s).",
+ errno, strerror(errno));
+ exit(1);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CONFIG_H__
+#define __MURPHY_CONFIG_H__
+
+#include <murphy/common/list.h>
+#include <murphy/core/context.h>
+
+#ifndef MRP_DEFAULT_CONFIG_DIR
+# define MRP_DEFAULT_CONFIG_DIR SYSCONFDIR"/murphy"
+#endif
+
+#ifndef MRP_DEFAULT_CONFIG_FILE
+# define MRP_DEFAULT_CONFIG_FILE MRP_DEFAULT_CONFIG_DIR"/murphy.conf"
+#endif
+
+/*
+ * command line processing
+ */
+
+/** Parse the command line and update context accordingly. */
+void mrp_parse_cmdline(mrp_context_t *ctx, int argc, char **argv, char **envp);
+
+
+/*
+ * configuration file processing
+ */
+
+#define MRP_CFG_MAXLINE (16 * 1024) /* input line length limit */
+#define MRP_CFG_MAXARGS 64 /* command argument limit */
+
+/* configuration keywords */
+#define MRP_KEYWORD_LOAD "load-plugin"
+#define MRP_KEYWORD_TRYLOAD "try-load-plugin"
+#define MRP_KEYWORD_AS "as"
+#define MRP_KEYWORD_IF "if"
+#define MRP_KEYWORD_ELSE "else"
+#define MRP_KEYWORD_END "end"
+#define MRP_KEYWORD_EXISTS "plugin-exists"
+#define MRP_KEYWORD_SETCFG "set"
+#define MRP_KEYWORD_ERROR "error"
+#define MRP_KEYWORD_WARNING "warning"
+#define MRP_KEYWORD_INFO "info"
+#define MRP_START_COMMENT '#'
+
+/* known configuration variables for 'set' command */
+#define MRP_CFGVAR_RESOLVER "resolver-ruleset"
+
+typedef struct {
+ mrp_list_hook_t actions;
+} mrp_cfgfile_t;
+
+
+/** Parse the given configuration file. */
+mrp_cfgfile_t *mrp_parse_cfgfile(const char *path);
+
+/** Execute the commands of the given parsed configuration file. */
+int mrp_exec_cfgfile(mrp_context_t *ctx, mrp_cfgfile_t *cfg);
+
+/** Free the given parsed configuration file. */
+void mrp_free_cfgfile(mrp_cfgfile_t *cfg);
+
+#endif /* __MURPHY_CONFIG_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <signal.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/utils.h>
+#include <murphy/core/context.h>
+#include <murphy/core/plugin.h>
+#include <murphy/resolver/resolver.h>
+#include <murphy/daemon/config.h>
+#include <murphy/daemon/daemon.h>
+
+
+/*
+ * daemon-related events
+ */
+
+enum {
+ DAEMON_EVENT_LOADING = 0, /* daemon loading configuration */
+ DAEMON_EVENT_STARTING, /* daemon starting plugins */
+ DAEMON_EVENT_RUNNING, /* daemon entering mainloop */
+ DAEMON_EVENT_STOPPING /* daemon shutting down */
+};
+
+
+MRP_REGISTER_EVENTS(daemon_events,
+ MRP_EVENT(MRP_DAEMON_LOADING , DAEMON_EVENT_LOADING ),
+ MRP_EVENT(MRP_DAEMON_STARTING, DAEMON_EVENT_STARTING),
+ MRP_EVENT(MRP_DAEMON_RUNNING , DAEMON_EVENT_RUNNING ),
+ MRP_EVENT(MRP_DAEMON_STOPPING, DAEMON_EVENT_STOPPING));
+
+
+static int emit_daemon_event(mrp_context_t *ctx, int idx)
+{
+ mrp_event_bus_t *bus = ctx->daemon_bus;
+ uint32_t id = daemon_events[idx].id;
+ int flags = MRP_EVENT_SYNCHRONOUS;
+
+ return mrp_event_emit_msg(bus, id, flags, MRP_MSG_END);
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ mrp_context_t *ctx = (mrp_context_t *)user_data;
+
+ MRP_UNUSED(ctx);
+
+ switch (signum) {
+ case SIGINT:
+ mrp_log_info("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+
+ case SIGTERM:
+ mrp_log_info("Got SIGTERM, stopping...");
+ mrp_mainloop_quit(ml, 0);
+ break;
+ }
+}
+
+
+static mrp_context_t *create_context(void)
+{
+ mrp_context_t *ctx;
+
+ ctx = mrp_context_create();
+
+ if (ctx != NULL) {
+ ctx->daemon_bus = mrp_event_bus_get(ctx->ml, MRP_DAEMON_BUS);
+ return ctx;
+ }
+ else
+ mrp_log_error("Failed to create murphy main context.");
+
+ exit(1);
+}
+
+
+static void setup_signals(mrp_context_t *ctx)
+{
+ mrp_add_sighandler(ctx->ml, SIGINT , signal_handler, ctx);
+ mrp_add_sighandler(ctx->ml, SIGTERM, signal_handler, ctx);
+}
+
+
+static void parse_cmdline(mrp_context_t *ctx, int argc, char **argv, char **env)
+{
+ mrp_parse_cmdline(ctx, argc, argv, env);
+}
+
+
+static void load_configuration(mrp_context_t *ctx)
+{
+ mrp_cfgfile_t *cfg;
+
+ mrp_context_setstate(ctx, MRP_STATE_LOADING);
+ emit_daemon_event(ctx, DAEMON_EVENT_LOADING);
+
+ cfg = mrp_parse_cfgfile(ctx->config_file);
+
+ if (cfg != NULL) {
+ mrp_log_info("Blacklisted plugins of any type: %s",
+ ctx->blacklist_plugins ? ctx->blacklist_plugins:"<none>");
+ mrp_log_info("Blacklisted builtin plugins: %s",
+ ctx->blacklist_builtin ? ctx->blacklist_builtin:"<none>");
+ mrp_log_info("Blacklisted dynamic plugins: %s",
+ ctx->blacklist_dynamic ? ctx->blacklist_dynamic:"<none>");
+ mrp_log_info("Whitelisted plugins of any type: %s",
+ ctx->whitelist_plugins ? ctx->whitelist_plugins:"<none>");
+ mrp_log_info("Whitelisted builtin plugins: %s",
+ ctx->whitelist_builtin ? ctx->whitelist_builtin:"<none>");
+ mrp_log_info("Whitelisted dynamic plugins: %s",
+ ctx->whitelist_dynamic ? ctx->whitelist_dynamic:"<none>");
+
+ mrp_block_blacklisted_plugins(ctx);
+
+ if (!mrp_exec_cfgfile(ctx, cfg)) {
+ mrp_log_error("Failed to execute configuration.");
+ exit(1);
+ }
+ }
+ else {
+ mrp_log_error("Failed to parse configuration file '%s'.",
+ ctx->config_file);
+ exit(1);
+ }
+}
+
+
+static void create_ruleset(mrp_context_t *ctx)
+{
+ ctx->r = mrp_resolver_create(ctx);
+}
+
+
+static void load_ruleset(mrp_context_t *ctx)
+{
+ if (ctx->resolver_ruleset != NULL) {
+ if (mrp_resolver_parse(ctx->r, ctx, ctx->resolver_ruleset))
+ mrp_log_info("Loaded resolver ruleset '%s'.",
+ ctx->resolver_ruleset);
+ else {
+ mrp_log_error("Failed to load resolver ruleset '%s'.",
+ ctx->resolver_ruleset);
+ exit(1);
+ }
+ }
+}
+
+
+static void start_plugins(mrp_context_t *ctx)
+{
+ mrp_context_setstate(ctx, MRP_STATE_STARTING);
+ emit_daemon_event(ctx, DAEMON_EVENT_STARTING);
+
+ if (mrp_start_plugins(ctx))
+ mrp_log_info("Successfully started all loaded plugins.");
+ else {
+ mrp_log_error("Some plugins failed to start.");
+ exit(1);
+ }
+}
+
+
+static void setup_logging(mrp_context_t *ctx)
+{
+ const char *target;
+
+ target = mrp_log_parse_target(ctx->log_target);
+
+ if (!target)
+ mrp_log_error("invalid log target '%s'", ctx->log_target);
+ else
+ mrp_log_set_target(target);
+}
+
+static void daemonize(mrp_context_t *ctx)
+{
+ if (!ctx->foreground) {
+ mrp_log_info("Switching to daemon mode.");
+
+ if (!mrp_daemonize("/", "/dev/null", "/dev/null")) {
+ mrp_log_error("Failed to daemonize.");
+ exit(1);
+ }
+ }
+}
+
+
+static void prepare_ruleset(mrp_context_t *ctx)
+{
+ if (ctx->r != NULL) {
+ if (mrp_resolver_prepare(ctx->r))
+ mrp_log_info("Ruleset prepared for resolution.");
+ else {
+ mrp_log_error("Failed to prepare ruleset for execution.");
+ exit(1);
+ }
+ if (!mrp_resolver_enable_autoupdate(ctx->r, "autoupdate")) {
+ mrp_log_error("Failed to enable resolver autoupdate.");
+ exit(1);
+ }
+ }
+}
+
+
+static void run_mainloop(mrp_context_t *ctx)
+{
+ mrp_context_setstate(ctx, MRP_STATE_RUNNING);
+ emit_daemon_event(ctx, DAEMON_EVENT_RUNNING);
+ mrp_mainloop_run(ctx->ml);
+}
+
+
+static void stop_plugins(mrp_context_t *ctx)
+{
+ MRP_UNUSED(ctx);
+
+ mrp_context_setstate(ctx, MRP_STATE_STOPPING);
+ emit_daemon_event(ctx, DAEMON_EVENT_STOPPING);
+}
+
+
+static void cleanup_context(mrp_context_t *ctx)
+{
+ mrp_log_info("Shutting down...");
+ mrp_context_destroy(ctx);
+}
+
+
+static void set_linebuffered(FILE *stream)
+{
+ fflush(stream);
+ setvbuf(stream, NULL, _IOLBF, 0);
+}
+
+
+static void set_nonbuffered(FILE *stream)
+{
+ fflush(stream);
+ setvbuf(stream, NULL, _IONBF, 0);
+}
+
+
+int main(int argc, char *argv[], char *envp[])
+{
+ mrp_context_t *ctx;
+
+ ctx = create_context();
+
+ setup_signals(ctx);
+ create_ruleset(ctx);
+ parse_cmdline(ctx, argc, argv, envp);
+ load_configuration(ctx);
+ start_plugins(ctx);
+ load_ruleset(ctx);
+ prepare_ruleset(ctx);
+ setup_logging(ctx);
+ daemonize(ctx);
+ set_linebuffered(stdout);
+ set_nonbuffered(stderr);
+ run_mainloop(ctx);
+ stop_plugins(ctx);
+
+ cleanup_context(ctx);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DAEMON_H__
+#define __MURPHY_DAEMON_H__
+
+/*
+ * names of daemon-related events we emit
+ */
+
+#define MRP_DAEMON_BUS "daemon-bus" /* bus for daemon events */
+#define MRP_DAEMON_LOADING "daemon-loading" /* loading configuration */
+#define MRP_DAEMON_STARTING "daemon-starting" /* starting up (plugins) */
+#define MRP_DAEMON_RUNNING "daemon-running" /* about to run mainloop */
+#define MRP_DAEMON_STOPPING "daemon-stopping" /* shutting down */
+
+#endif /* __MURPHY_DAEMON_H__ */
--- /dev/null
+#set resolver-ruleset 'src/resolver/test-input'
+load-plugin lua config="src/daemon/murphy.lua"
--- /dev/null
+set resolver-ruleset '/u/src/work/murphy/src/resolver/test-input'
+
+# try-load-plugin console
+try-load-plugin console # address="tcp4:127.0.0.1:3000"
+ # address="udp4:127.0.0.1:3000"
+ # address="unxs:@/murphyd"
+ # address="dbus:[session]@murphy.org/console
+
+# load two instances of the test plugin
+if plugin-exists test
+ load-plugin test string2='this is now string 2' boolean2=TRUE \
+ int32=-981 string1="and this is string 1" \
+ double=2.73 \
+ object='{ "foo": "f o o", "bar": "b a r", "two": 2, \
+ "array": [ 1, 2, 3, 4, "five", "six", ] }'
+ load-plugin test as test5 # foo=foo foobar=foobar
+ info "Successfully loaded two instances of test..."
+end
+
+# load the dbus and glib plugins if they exist
+if plugin-exists dbus
+ load-plugin dbus
+end
+
+# try loading the glib plugin, ignoring any errors
+try-load-plugin glib
+
+# load the murphy DB plugin if it exists
+if plugin-exists murphydb
+ load-plugin murpydb
+#else
+# error "Could not find mandatory plugin murphydb, giving up..."
+end
+
+# load the native resource plugin if it exists
+if plugin-exists resource-native
+ load-plugin resource-native
+else
+ info "Could not find resource-native plugin"
+end
+
+#if plugin-exists resource-dbus
+# try-load-plugin resource-dbus
+#end
+
+if plugin-exists domain-control
+ load-plugin domain-control
+else
+ info "No domain-control plugin found..."
+end
+
--- /dev/null
+m = murphy.get()
+
+-- try loading console plugin
+m:try_load_plugin('console')
+
+--[[
+m:try_load_plugin('console', 'dbusconsole' , {
+ address = 'dbus:[session]@org.Murphy/console'
+})
+--]]
+
+m:try_load_plugin('console', 'webconsole', {
+ address = 'wsck:127.0.0.1:3000/murphy',
+ httpdir = 'src/plugins/console',
+-- sslcert = 'src/plugins/console/console.crt',
+-- sslpkey = 'src/plugins/console/console.key'
+ })
+
+m:try_load_plugin('systemd')
+
+-- load a test plugin
+if m:plugin_exists('test.disabled') then
+ m:load_plugin('test', {
+ string2 = 'this is now string2',
+ boolean2 = true,
+ int32 = -981,
+ double = 2.73,
+ object = {
+ foo = 1,
+ bar = 'bar',
+ foobar = 3.141,
+ barfoo = 'bar foo',
+ array = { 'one', 'two', 'three',
+ { 1, 'two', 3, 'four' } },
+ yees = true,
+ noou = false
+ }
+ })
+-- m:load_plugin('test', 'test2')
+-- m:info("Successfully loaded two instances of test...")
+end
+
+-- load the dbus plugin if it exists
+-- if m:plugin_exists('dbus') then
+-- m:load_plugin('dbus')
+-- end
+
+-- load glib plugin, ignoring any errors
+m:try_load_plugin('glib')
+
+-- load the native resource plugin
+if m:plugin_exists('resource-native') then
+ m:load_plugin('resource-native')
+ m:info("native resource plugin loaded")
+else
+ m:info("No native resource plugin found...")
+end
+
+-- load the dbus resource plugin
+if m:plugin_exists('resource-dbus') then
+ m:try_load_plugin('resource-dbus', {
+ dbus_bus = "system",
+ dbus_service = "org.Murphy",
+ dbus_track = true,
+ default_zone = "driver",
+ default_class = "implicit"
+ })
+ m:info("dbus resource plugin loaded")
+else
+ m:info("No dbus resource plugin found...")
+end
+
+-- load the WRT resource plugin
+if m:plugin_exists('resource-wrt') then
+ m:try_load_plugin('resource-wrt', {
+ address = "wsck:127.0.0.1:4000/murphy",
+ httpdir = "src/plugins/resource-wrt",
+-- sslcert = 'src/plugins/resource-wrt/resource.crt',
+-- sslpkey = 'src/plugins/resource-wrt/resource.key'
+ })
+else
+ m:info("No WRT resource plugin found...")
+end
+
+-- load the domain control plugin if it exists
+if m:plugin_exists('domain-control') then
+ m:load_plugin('domain-control')
+else
+ m:info("No domain-control plugin found...")
+end
+
+-- load the domain control plugin if it exists
+if m:plugin_exists('domain-control') then
+ m:try_load_plugin('domain-control', 'wrt-export', {
+ external_address = '',
+ internal_address = '',
+ wrt_address = "wsck:127.0.0.1:5000/murphy",
+ httpdir = "src/plugins/domain-control"
+ })
+else
+ m:info("No domain-control plugin found...")
+end
+
+
+-- define application classes
+application_class { name="interrupt", priority=99, modal=true , share=false, order="fifo" }
+application_class { name="navigator", priority=4 , modal=false, share=true , order="fifo" }
+application_class { name="phone" , priority=3 , modal=false, share=true , order="lifo" }
+application_class { name="game" , priority=2 , modal=false, share=true , order="lifo" }
+application_class { name="player" , priority=1 , modal=false, share=true , order="lifo" }
+application_class { name="implicit" , priority=0 , modal=false, share=true , order="lifo" }
+
+-- define zone attributes
+zone.attributes {
+ type = {mdb.string, "common", "rw"},
+ location = {mdb.string, "anywhere", "rw"}
+}
+
+-- define zones
+zone {
+ name = "driver",
+ attributes = {
+ type = "common",
+ location = "front-left"
+ }
+}
+
+zone {
+ name = "passanger1",
+ attributes = {
+ type = "private",
+ location = "front-right"
+ }
+}
+
+zone {
+ name = "passanger2",
+ attributes = {
+ type = "private",
+ location = "back-left"
+ }
+}
+
+zone {
+ name = "passanger3",
+ attributes = {
+ type = "private",
+ location = "back-right"
+ }
+}
+
+zone {
+ name = "passanger4",
+ attributes = {
+ type = "private",
+ location = "back-left"
+ }
+}
+
+
+-- define resource classes
+resource.class {
+ name = "audio_playback",
+ shareable = true,
+ attributes = {
+ role = { mdb.string, "music", "rw" },
+ pid = { mdb.string, "<unknown>", "rw" },
+ policy = { mdb.string, "relaxed", "rw" }
+ }
+}
+
+resource.class {
+ name = "audio_recording",
+ shareable = false,
+ attributes = {
+ role = { mdb.string, "music", "rw" },
+ pid = { mdb.string, "<unknown>", "rw" },
+ policy = { mdb.string, "relaxed", "rw" }
+ }
+}
+
+resource.class {
+ name = "video_playback",
+ shareable = false,
+}
+
+resource.class {
+ name = "video_recording",
+ shareable = false
+}
+
+-- test for creating selections
+mdb.select {
+ name = "audio_owner",
+ table = "audio_playback_owner",
+ columns = {"application_class"},
+ condition = "zone_name = 'driver'",
+}
+
+element.lua {
+ name = "speed2volume",
+ inputs = { owner = mdb.select.audio_owner, param = 5 },
+ outputs = { mdb.table { name = "speedvol",
+ index = {"zone", "device"},
+ columns = {{"zone", mdb.string, 16},
+ {"device", mdb.string, 16},
+ {"value", mdb.floating}},
+ create = true
+ }
+ },
+ update = function(self)
+ if (self.inputs.owner.single_value) then
+ print("*** element "..self.name.." update "..
+ self.inputs.owner.single_value)
+ else
+ print("*** element "..self.name.." update <nil>")
+ end
+ end
+}
+
+
+--
+
+json = m:JSON({ a = 'foo', b = 'bar', foobar = { 1, 2, 3, 5, 6 } })
+
+print(tostring(json))
+
+
+function connect_cb(self, peer, data)
+ print('incoming connection from ' .. peer .. ' on ' .. tostring(self))
+ accepted = self:accept()
+ print('accepted: ' .. tostring(accepted))
+end
+
+function closed_cb(self, error, data)
+ print('connection closed by peer')
+end
+
+function recv_cb(self, msg, data)
+ print('got message ' .. tostring(msg))
+end
+
+t = m:Transport({ connect = connect_cb,
+ closed = closed_cb,
+ recv = recv_cb,
+ data = 'foo',
+ address = 'wsck:127.0.0.1:18081/ico_syc_protocol' })
+
+print(tostring(t))
+
+t:listen()
+
+print(tostring(t))
+
+print(t.accept)
+print(t.recv)
+
+function test_rs_create(iterations)
+ resourceHandler = function (rset) print(rset) end
+
+ for i = 1, iterations do
+ r = m:ResourceSet({
+ zone = "driver",
+ callback = resourceHandler,
+ application_class = "player"
+ })
+
+ if r then
+
+ r:addResource({
+ resource_name = "audio_playback",
+ mandatory = true
+ })
+
+ r.resources.audio_playback.attributes.pid = tostring(i)
+
+ print("pid: " .. r.resources.audio_playback.attributes.pid)
+ r:acquire()
+ r:release()
+ end
+ r = nil
+ end
+end
--- /dev/null
+-- -*- mode: lua -*-
+
+if not loaded('system-monitor') then
+ return
+end
+
+cg = m:CGroupOpen({ type = 'cpuacct',
+ name = 'murphy-test',
+ mode = 'readonly',
+ foo = 3,
+ bar = 'foobar',
+ foobar = 9.81 })
+
+print('cg: ' .. tostring(cg))
+print('cg.mode: ' .. tostring(cg.mode))
+print('cg.foo: ' .. tostring(cg.foo))
+print('cg.bar: ' .. tostring(cg.bar))
+print('cg.foobar: ' .. tostring(cg.foobar))
+
+--status = cg:add_process(4214)
+--cg.tasks = 4214
+print('cg.tasks: ' .. cg.tasks)
+
+
+cg1 = m:CGroupOpen({ type = 'cpu',
+ name = 'test',
+ mode = 'readwrite,create',
+ Shares = 2048 })
+
+print('cg1: ' .. tostring(cg1))
+print('cg1.mode: ' .. cg1.mode)
+print('cg1.shares: ' .. cg1.Shares)
+print('cg1.tasks: ' .. cg1.tasks)
--- /dev/null
+-- -*- mode: lua -*-
+
+-- plugin optionality constants
+OPTIONAL = 1 -- mark a plugin optional
+IFEXISTS = 2 -- mark a plugin mandatory if present
+MANDATORY = 3 -- mark a plugin mandatory
+
+-- function to try loading an optional plugin, ignoring errors
+function try_load(plugin, ...)
+ local a = {...}
+ m:info('* Trying to load (optional) plugin ' .. plugin)
+ if #a == 0 then return m:try_load_plugin(plugin )
+ elseif #a == 1 then return m:try_load_plugin(plugin, a[1] )
+ else return m:try_load_plugin(plugin, a[1], a[2])
+ end
+end
+
+-- function to load a plugin if it exists
+function load_if_exists(plugin, ...)
+ local a = {...}
+ if m:plugin_exists(plugin) then
+ m:info('* Loading (existing) plugin ' .. plugin)
+ else
+ return true
+ end
+ if #a == 0 then return m:load_plugin(plugin )
+ elseif #a == 1 then return m:load_plugin(plugin, a[1] )
+ else return m:load_plugin(plugin, a[1], a[2])
+ end
+end
+
+-- function to load a mandatory plugin
+function load(plugin, ...)
+ local a = {...}
+ m:info('* Loading (mandatory) plugin ' .. plugin)
+ if #a == 0 then return m:load_plugin(plugin )
+ elseif #a == 1 then return m:load_plugin(plugin, a[1] )
+ else return m:load_plugin(plugin, a[1], a[2])
+ end
+end
+
+-- function to check if a plugin has been successfully loaded (and running)
+function loaded(plugin)
+ return m:plugin_loaded(plugin)
+end
+
+-- function to include a file
+function include(file, necessity)
+ if necessity < MANDATORY then
+ m:info('* Trying to include (optional) ' .. file)
+ m:try_include(file)
+ else
+ m:info('* Including (mandatory) ' .. file)
+ m:include(file)
+ end
+end
--- /dev/null
+-- -*- mode: lua -*-
+
+-- load the oridnary console
+load('console')
+
+-- load a web console
+try_load('console',
+ 'webconsole', {
+ address = 'wsck:127.0.0.1:3000/murphy',
+ httpdir = 'src/plugins/console',
+ --[[
+ sslcert = 'src/plugins/console/console.crt',
+ sslpkey = 'src/plugins/console/console.key'
+ --]] })
--- /dev/null
+-- -*- mode: lua -*-
+
+-- load the domain control plugin if it exists
+load_if_exists('domain-control')
+
+-- load a domain-control instance for exposing data to the WRT
+try_load('domain-control',
+ 'wrt-export', {
+ external_address = '',
+ internal_address = '',
+ wrt_address = "wsck:127.0.0.1:5000/murphy",
+ httpdir = "src/plugins/domain-control" })
--- /dev/null
+-- -*- mode: lua -*-
+
+-- try loading the GLIB plugin
+try_load('glib')
--- /dev/null
+-- -*- mode: lua -*-
+
+--
+-- pull in the common configuration support bits
+--
+m = murphy.get()
+m:include_once('common.cfg')
+
+
+--
+-- configuration files and rulesets to pull in
+--
+config = {
+ { 'console' , OPTIONAL },
+ { 'systemd' , OPTIONAL },
+ { 'glib' , OPTIONAL },
+ { 'resource' , MANDATORY },
+ { 'domain-control' , MANDATORY },
+ { 'system-controller', OPTIONAL },
+ { 'system-monitor' , OPTIONAL },
+}
+
+ruleset = {
+ { 'speed-volume' , MANDATORY },
+ { 'system-monitor' , OPTIONAL },
+ { 'cgroup-test' , OPTIONAL },
+ { 'timer-test' , OPTIONAL },
+
+}
+
+
+--
+-- pull in the given configuration and ruleset files
+--
+
+for idx,cfg in ipairs(config) do
+ include(cfg[1] .. '.cfg', cfg[2])
+end
+
+for idx,rules in ipairs(ruleset) do
+ include(rules[1] .. '.rules', rules[2])
+end
--- /dev/null
+load-plugin lua config="src/daemon/sample-config/main.cfg"
--- /dev/null
+-- -*- mode: lua -*-
+
+-- load the native resource plugin
+load_if_exists('resource-native')
+
+-- load the D-Bus resource plugin if we have it and D-Bus
+try_load('resource-dbus', {
+ dbus_bus = "session",
+ dbus_service = "org.Murphy",
+ dbus_track = true,
+ default_zone = "driver",
+ default_class = "implicit"
+})
+
+-- try loading the WRT resource plugin
+try_load('resource-wrt', {
+ address = "wsck:127.0.0.1:4000/murphy",
+ httpdir = "src/plugins/resource-wrt",
+ --[[
+ sslcert = 'src/plugins/resource-wrt/resource.crt',
+ sslpkey = 'src/plugins/resource-wrt/resource.key',
+ --]]
+})
+
+-- load the IVI resource manager if it is available
+load_if_exists('ivi-resource-manager')
+
+--
+-- define application classes
+--
+application_class { name="interrupt", priority=99,
+ modal=true , share=false, order="fifo" }
+application_class { name="navigator", priority=4 ,
+ modal=false, share=true , order="fifo" }
+application_class { name="phone" , priority=3 ,
+ modal=false, share=true , order="lifo" }
+application_class { name="game" , priority=2 ,
+ modal=false, share=true , order="lifo" }
+application_class { name="player" , priority=1 ,
+ modal=false, share=true , order="lifo" }
+application_class { name="implicit" , priority=0 ,
+ modal=false, share=true , order="lifo" }
+
+--
+-- define zones
+--
+zone.attributes {
+ type = {mdb.string, "common", "rw"},
+ location = {mdb.string, "anywhere", "rw"}
+}
+
+zone {
+ name = "driver",
+ attributes = {
+ type = "common",
+ location = "front-left"
+ }
+}
+
+zone {
+ name = "passanger1",
+ attributes = {
+ type = "private",
+ location = "front-right"
+ }
+}
+
+zone {
+ name = "passanger2",
+ attributes = {
+ type = "private",
+ location = "back-left"
+ }
+}
+
+zone {
+ name = "passanger3",
+ attributes = {
+ type = "private",
+ location = "back-right"
+ }
+}
+
+zone {
+ name = "passanger4",
+ attributes = {
+ type = "private",
+ location = "back-left"
+ }
+}
+
+
+--
+-- define resource classes
+--
+if not m:plugin_loaded('ivi-resource-manager') then
+ resource.class {
+ name = "audio_playback",
+ shareable = true,
+ attributes = {
+ role = { mdb.string, "music", "rw" },
+ pid = { mdb.string, "<unknown>", "rw" },
+ policy = { mdb.string, "relaxed", "rw" }
+ }
+ }
+end
+
+resource.class {
+ name = "audio_recording",
+ shareable = false,
+ attributes = {
+ role = { mdb.string, "music", "rw" },
+ pid = { mdb.string, "<unknown>", "rw" },
+ policy = { mdb.string, "relaxed", "rw" }
+ }
+}
+
+resource.class {
+ name = "video_playback",
+ shareable = false,
+}
+
+resource.class {
+ name = "video_recording",
+ shareable = false,
+}
+
+resource.class {
+ name = "speech_synthesis",
+ shareable = true,
+}
+
+resource.class {
+ name = "speech_recognition",
+ shareable = true,
+}
--- /dev/null
+-- test for creating selections
+mdb.select {
+ name = "audio_owner",
+ table = "audio_playback_owner",
+ columns = {"application_class"},
+ condition = "zone_name = 'driver'",
+}
+
+element.lua {
+ name = "speed2volume",
+ inputs = { owner = mdb.select.audio_owner, param = 5 },
+ outputs = { mdb.table { name = "speedvol",
+ index = {"zone", "device"},
+ columns = {{"zone", mdb.string, 16},
+ {"device", mdb.string, 16},
+ {"value", mdb.floating}},
+ create = true
+ }
+ },
+ update = function(self)
+ if (self.inputs.owner.single_value) then
+ print("*** element "..self.name.." update "..
+ self.inputs.owner.single_value)
+ else
+ print("*** element "..self.name.." update <nil>")
+ end
+ end
+}
--- /dev/null
+-- -*- mode: lua -*-
+
+load_if_exists('system-controller')
--- /dev/null
+-- -*- mode: lua -*-
+
+-- try loading the system-monitor plugin
+load_if_exists('system-monitor')
--- /dev/null
+-- -*- mode: lua -*-
+
+if not loaded('system-monitor') then
+ return
+end
+
+-- get and configure system-monitor
+sm = m:get_system_monitor()
+
+sm.polling = 1000 -- poll 1 / second
+
+--
+-- monitor overall CPU load (load of the virtual combined CPU)
+--
+sm:CpuWatch({
+ cpu = 'cpu', -- virtual 'combined' CPU
+ sample = 'load', -- monitor 'load'
+ limits = { -- load threshold %'s
+ [1] = { label = 'idle' , limit = 5 },
+ [2] = { label = 'low' , limit = 20 },
+ [3] = { label = 'moderate', limit = 40 },
+ [4] = { label = 'medium' , limit = 50 },
+ [5] = { label = 'high' , limit = 80 },
+ [6] = { label = 'critical' }
+ },
+ window = 15000, -- use an EWMA of 15 secs
+ notify = function (w, prev, curr) -- threshold change callback
+ print('CPU load change: ' .. prev .. ' -> ' .. curr)
+ end
+ })
+
+
+--
+-- monitor length of the writeback queue
+--
+sm:MemWatch({
+ sample = 'Writeback',
+ limits = { -- pressure thresholds
+ [1] = { label = 'none' , limit = 1024 },
+ [2] = { label = 'low' , limit = 8192 },
+ [3] = { label = 'medium' , limit = '1M' },
+ [4] = { label = 'high' , limit = '4M' },
+ [5] = { label = 'critical', limit = '16M' }
+ },
+ window = 0, -- don't average/integrate
+ notify = function (w, prev, curr) -- threshold change callback
+ print(w.sample .. ' change: ' .. prev .. ' -> ' .. curr)
+ end
+ })
+
+--
+-- monitor the amount of dirty memory
+--
+sm:MemWatch({
+ sample = 'Dirty',
+ limits = { -- pressure thresholds
+ [1] = { label = 'none' , limit = 1024 },
+ [2] = { label = 'low' , limit = 8192 },
+ [3] = { label = 'medium' , limit = '1M' },
+ [4] = { label = 'high' , limit = '4M' },
+ [5] = { label = 'critical', limit = '16M' }
+ },
+ window = 10000, -- use an EWMA of 10 secs
+ notify = function (w, prev, curr) -- threshold change callback
+ print(w.sample .. ' change: ' .. prev .. ' -> ' .. curr)
+ end
+ })
--- /dev/null
+-- -*- mode: lua -*-
+
+-- try loading the systemd plugin (for systemd-aware logging)
+try_load('systemd')
--- /dev/null
+timer = m:Timer({
+ interval = 1000,
+ callback = function (t)
+ print('timer-test<'..t.data..'> #' .. tostring(t.count))
+ print(' foo = ' .. t.foo .. ', bar = ' .. t.bar)
+
+ t.count = t.count + 1
+
+ if t.count == 5 then
+ t.interval = 500
+ else
+ if t.count == 10 then
+ t.interval = 250
+ else
+ if t.count == 20 then
+ t:stop()
+ end
+ end
+ end
+ end,
+ oneshot = false,
+ data = 'timer-data',
+ count = 0,
+ foo = 'bar',
+ bar = 'foo'
+})
+
+
+def = m:Deferred({
+ disabled = true,
+ data = 'test-deferred',
+ callback = function (d)
+ print('deferred <'..d.data..'> callback #'.. tostring(d.count))
+ print(' xyzzy = ' .. d.xyzzy .. ', what = ' .. d.what)
+
+ if d.count == 10 then
+ d.disabled = true
+ else
+ if d.count == 20 then
+ d:disable()
+ d.count = 1
+ end
+ end
+
+ d.count = d.count + 1
+ end,
+ count = 1,
+ xyzzy = 'blah',
+ what = 'ever...'
+
+})
+
+
+sh = m:SigHandler({
+ signal = 17,
+ data = 'test-sighandler',
+ callback = function (sh)
+ print('SigHandler <'..sh.data..'> callback #'.. tostring(sh.count))
+ print(' xyzzy = ' .. sh.attr1 .. ', what = ' .. sh.attr2)
+ sh.count = sh.count + 1
+ end,
+ count = 1,
+ attr1 = 'sh-attr-1',
+ attr2 = 'sh-attr-2'
+})
--- /dev/null
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
--- /dev/null
+if HAVE_CHECK
+TESTDIR = tests
+else
+TESTDIR =
+endif
+
+SUBDIRS = mdb mqi mql include $(TESTDIR)
+
+pkgconfigdir = $(libdir)/pkgconfig
+nodist_pkgconfig_DATA = murphy-db.pc
+
+murphy-db.pc: murphy-db.pc.in
+ sed -e 's![@]prefix[@]!$(prefix)!g' \
+ -e 's![@]exec_prefix[@]!$(exec_prefix)!g' \
+ -e 's![@]includedir[@]!$(includedir)!g' \
+ -e 's![@]libdir[@]!$(libdir)!g' \
+ -e 's![@]PACKAGE_VERSION[@]!$(PACKAGE_VERSION)!g' \
+ $< > $@
+
+clean-local:
+ rm -f *~ murphy-db.pc
--- /dev/null
+nobase_include_HEADERS = murphy-db/mqi.h \
+ murphy-db/mqi-types.h \
+ murphy-db/mql.h \
+ murphy-db/mql-statement.h \
+ murphy-db/mql-result.h \
+ murphy-db/mql-trigger.h
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_ASSERT_H__
+#define __MDB_ASSERT_H__
+
+
+#define MDB_ASSERT(cond, errcode, retval) \
+ do { \
+ if (!(cond)) { \
+ errno = errcode; \
+ return retval; \
+ } \
+ } while(0)
+
+#define MDB_CHECKARG(cond, retval) MDB_ASSERT(cond, EINVAL, retval)
+#define MDB_PREREQUISITE(cond, retval) MDB_ASSERT(cond, EIO, retval)
+
+
+#endif /* __MDB_ASSERT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_HANDLE_H__
+#define __MDB_HANDLE_H__
+
+#include <stdint.h>
+
+#define MDB_HANDLE_INVALID (~((mdb_handle_t)0))
+
+#define MDB_HANDLE_MAP_CREATE mdb_handle_map_create
+#define MDB_HANDLE_MAP_DESTROY mdb_handle_map_destroy
+
+typedef uint32_t mdb_handle_t;
+typedef struct mdb_handle_map_s mdb_handle_map_t;
+
+mdb_handle_map_t *mdb_handle_map_create(void);
+int mdb_handle_map_destroy(mdb_handle_map_t *);
+
+mdb_handle_t mdb_handle_add(mdb_handle_map_t *, void *);
+void *mdb_handle_delete(mdb_handle_map_t *, mdb_handle_t);
+void *mdb_handle_get_data(mdb_handle_map_t *, mdb_handle_t);
+int mdb_handle_print(mdb_handle_map_t *, char *, int);
+
+
+#endif /* __MDB_HANDLE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_HASH_H__
+#define __MDB_HASH_H__
+
+#include <murphy-db/mqi-types.h>
+#include <murphy-db/list.h>
+
+
+#define MDB_HASH_TABLE_CREATE(type, max_entries) \
+ mdb_hash_table_create(max_entries, \
+ mdb_hash_function_##type, \
+ mqi_data_compare_##type, \
+ mqi_data_print_##type)
+
+#define MDB_HASH_TABLE_DESTROY(h) \
+ mdb_hash_table_destroy(h)
+
+#define MDB_HASH_TABLE_FOR_EACH_WITH_KEY(htbl, data, key, cursor) \
+ for (cursor = NULL; \
+ (data = mdb_hash_table_iterate(htbl, (void **)&key, &cursor)); )
+#define MDB_HASH_TABLE_FOR_EACH_WITH_KEY_SAFE(htbl, data, key, cursor) \
+ MDB_HASH_TABLE_FOR_EACH_WITH_KEY(htbl, data, key, cursor)
+
+#define MDB_HASH_TABLE_FOR_EACH(htbl, data, cursor) \
+ for (cursor = NULL; (data = mdb_hash_table_iterate(htbl, NULL, &cursor));)
+#define MDB_HASH_TABLE_FOR_EACH_SAFE(htbl, data, cursor) \
+ MDB_HASH_TABLE_FOR_EACH(htbl, data, key, cursor)
+
+typedef struct mdb_hash_s mdb_hash_t;
+
+typedef int (*mdb_hash_function_t)(int, int, int, void *);
+typedef int (*mdb_hash_compare_t)(int, void *, void *);
+typedef int (*mdb_hash_print_t)(void *, char *, int);
+
+
+mdb_hash_t *mdb_hash_table_create(int, mdb_hash_function_t, mdb_hash_compare_t,
+ mdb_hash_print_t);
+int mdb_hash_table_destroy(mdb_hash_t *);
+int mdb_hash_table_reset(mdb_hash_t *);
+void *mdb_hash_table_iterate(mdb_hash_t *, void **, void **);
+int mdb_hash_table_print(mdb_hash_t *, char *, int);
+
+int mdb_hash_add(mdb_hash_t *, int, void *, void *);
+void *mdb_hash_delete(mdb_hash_t *, int, void *);
+void *mdb_hash_get_data(mdb_hash_t *, int, void *);
+
+int mdb_hash_function_integer(int, int, int, void *);
+int mdb_hash_function_unsignd(int, int, int, void *);
+int mdb_hash_function_string(int, int, int, void *);
+int mdb_hash_function_pointer(int, int, int, void *);
+int mdb_hash_function_varchar(int, int, int, void *);
+int mdb_hash_function_blob(int, int, int, void *);
+
+
+#endif /* __MDB_HASH_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_LIST_H__
+#define __MDB_LIST_H__
+
+#include <murphy-db/mqi-types.h>
+
+#define MDB_LIST_RELOCATE(structure, member, ptr) \
+ ((structure *)((char *)ptr - MQI_OFFSET(structure, member)))
+
+
+#define MDB_DLIST_HEAD(name) mdb_dlist_t name = { &(name), &(name) }
+
+#define MDB_DLIST_INIT(self) \
+ do { \
+ (&(self))->prev = &(self); \
+ (&(self))->next = &(self); \
+ } while(0)
+
+#define MDB_DLIST_EMPTY(name) ((&(name))->next == &(name))
+
+#define MDB_DLIST_FOR_EACH(structure, member, pos, head) \
+ for (pos = MDB_LIST_RELOCATE(structure, member, (head)->next); \
+ &pos->member != (head); \
+ pos = MDB_LIST_RELOCATE(structure, member, pos->member.next))
+
+#define MDB_DLIST_FOR_EACH_SAFE(structure, member, pos, n, head) \
+ for (pos = MDB_LIST_RELOCATE(structure, member, (head)->next), \
+ n = MDB_LIST_RELOCATE(structure, member, pos->member.next); \
+ &pos->member != (head); \
+ pos = n, \
+ n = MDB_LIST_RELOCATE(structure, member, pos->member.next))
+
+#define MDB_DLIST_FOR_EACH_NOHEAD(structure, member, pos, start) \
+ for (pos = start; \
+ &(pos)->member != &(start)->member; \
+ pos = MDB_LIST_RELOCATE(structure, member, pos->member.next))
+
+#define MDB_DLIST_FOR_EACH_NOHEAD_SAFE(structure, member, pos,n, start) \
+ for (pos = start, \
+ n = MDB_LIST_RELOCATE(structure, member, pos->member.next); \
+ &pos->member != &(start)->member; \
+ pos = n, \
+ n = MDB_LIST_RELOCATE(structure, member, pos->member.next))
+
+#define MDB_DLIST_INSERT_BEFORE(structure, member, new, before) \
+ do { \
+ mdb_dlist_t *after = (before)->prev; \
+ after->next = &(new)->member; \
+ (new)->member.next = before; \
+ (before)->prev = &(new)->member; \
+ (new)->member.prev = after; \
+ } while(0)
+#define MDB_DLIST_APPEND(structure, member, new, head) \
+ MDB_DLIST_INSERT_BEFORE(structure, member, new, head)
+
+#define MDB_DLIST_INSERT_AFTER(structure, member, new, after) \
+ do { \
+ mdb_dlist_t *before = (after)->next; \
+ (after)->next = &((new)->member); \
+ (new)->member.next = before; \
+ before->prev = &((new)->member); \
+ (new)->member.prev = after; \
+ } while(0)
+#define MDB_DLIST_PREPEND(structure, member, new, head) \
+ MDB_DLIST_INSERT_AFTER(structure, member, new, head)
+
+
+#define MDB_DLIST_UNLINK(structure, member, elem) \
+ do { \
+ mdb_dlist_t *after = (elem)->member.prev; \
+ mdb_dlist_t *before = (elem)->member.next; \
+ after->next = before; \
+ before->prev = after; \
+ (elem)->member.prev = (elem)->member.next = &(elem)->member; \
+ } while(0)
+
+
+typedef struct mdb_dlist_s {
+ struct mdb_dlist_s *prev;
+ struct mdb_dlist_s *next;
+} mdb_dlist_t;
+
+
+
+
+#endif /* __MDB_LIST_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_MDB_H__
+#define __MDB_MDB_H__
+
+#include <murphy-db/mqi-types.h>
+
+typedef struct mdb_table_s mdb_table_t;
+
+
+int mdb_trigger_add_column_callback(mdb_table_t *, int, mqi_trigger_cb_t,
+ void *, mqi_column_desc_t *);
+int mdb_trigger_delete_column_callback(mdb_table_t *, int,
+ mqi_trigger_cb_t, void *);
+int mdb_trigger_add_row_callback(mdb_table_t *, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+int mdb_trigger_delete_row_callback(mdb_table_t *, mqi_trigger_cb_t, void *);
+int mdb_trigger_add_table_callback(mqi_trigger_cb_t, void *);
+int mdb_trigger_delete_table_callback(mqi_trigger_cb_t, void *);
+int mdb_trigger_add_transaction_callback(mqi_trigger_cb_t, void *);
+int mdb_trigger_delete_transaction_callback(mqi_trigger_cb_t, void *);
+
+uint32_t mdb_transaction_begin(void);
+int mdb_transaction_commit(uint32_t);
+int mdb_transaction_rollback(uint32_t);
+uint32_t mdb_transaction_get_depth(void);
+
+
+mdb_table_t *mdb_table_create(char *, char **, mqi_column_def_t *);
+int mdb_table_register_handle(mdb_table_t *, mqi_handle_t);
+int mdb_table_drop(mdb_table_t *);
+int mdb_table_create_index(mdb_table_t *, char **);
+int mdb_table_describe(mdb_table_t *, mqi_column_def_t *, int);
+int mdb_table_insert(mdb_table_t *, int, mqi_column_desc_t *, void **);
+int mdb_table_select(mdb_table_t *, mqi_cond_entry_t *,
+ mqi_column_desc_t *, void *, int, int);
+int mdb_table_select_by_index(mdb_table_t *, mqi_variable_t *,
+ mqi_column_desc_t *, void *);
+int mdb_table_update(mdb_table_t *, mqi_cond_entry_t *,
+ mqi_column_desc_t *, void *);
+int mdb_table_delete(mdb_table_t *, mqi_cond_entry_t *);
+
+
+mdb_table_t *mdb_table_find(char *);
+int mdb_table_get_column_index(mdb_table_t *, char *);
+int mdb_table_get_size(mdb_table_t *);
+char *mdb_table_get_column_name(mdb_table_t *, int);
+mqi_data_type_t mdb_table_get_column_type(mdb_table_t *, int);
+int mdb_table_get_column_size(mdb_table_t *, int);
+uint32_t mdb_table_get_stamp(mdb_table_t *);
+int mdb_table_print_rows(mdb_table_t *, char *, int);
+
+
+#endif /* __MDB_MDB_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQI_TYPES_H__
+#define __MQI_TYPES_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/** macro to tag a variable unused */
+#define MQI_UNUSED(var) ((void)var)
+
+/** maximum number of rows a query can produce */
+#define MQI_QUERY_RESULT_MAX 8192
+/** the maximum number columns a table can have */
+#define MQI_COLUMN_MAX ((int)(sizeof(mqi_bitfld_t) * 8))
+/** maximum length of a condition table (i.e. array of mqi_cond_entry_t) */
+#define MQI_COND_MAX 64
+#define MQL_PARAMETER_MAX 16
+/** maximum depth for nested transactions */
+#define MQI_TXDEPTH_MAX 16
+
+/**
+ * mqi_handle_t value for nonexisting handle. Zero is a valid handle
+ * thus casting a zero to mqi_handle_t will produce a valid handle
+ * (remeber this when using static mqi_handle_t).
+ */
+#define MQI_HANDLE_INVALID (~((mqi_handle_t)0))
+
+/**
+ * Stamp for a non-existing table or a table without any inserts ever.
+ */
+#define MQI_STAMP_NONE ((uint32_t)0)
+
+
+#define MQI_DIMENSION(array) \
+ (sizeof(array) / sizeof(array[0]))
+
+#define MQI_OFFSET(structure, member) \
+ ((int)((char *)((&((structure *)0)->member)) - (char *)0))
+
+#define MQI_BIT(b) (((mqi_bitfld_t)1) << (b))
+
+#define MQL_BIND_INDEX_BITS 8
+#define MQL_BIND_INDEX_MAX (1 << MQL_BIND_INDEX_BITS)
+#define MQL_BIND_INDEX_MASK (MQL_BIND_INDEX_MAX - 1)
+
+#define MQL_BINDABLE (1 << (MQL_BIND_INDEX_BITS + 0))
+#define MQL_BIND_INDEX(v) ((v) & MQL_BIND_INDEX_MASK)
+
+#define MQI_COLUMN_KEY (1UL << 0)
+#define MQI_COLUMN_AUTOINCR (1UL << 1)
+
+enum mqi_data_type_e {
+ mqi_error = -1, /* not a data type; used to return error conditions */
+ mqi_unknown = 0,
+ mqi_varchar,
+ mqi_string = mqi_varchar,
+ mqi_integer,
+ mqi_unsignd,
+ mqi_floating,
+ mqi_blob,
+};
+
+enum mqi_operator_e {
+ mqi_done = 0,
+ mqi_end = mqi_done,
+ mqi_begin, /* expression start */
+ mqi_and,
+ mqi_or,
+ mqi_less,
+ mqi_leq,
+ mqi_eq,
+ mqi_geq,
+ mqi_gt,
+ mqi_not,
+ mqi_operator_max
+};
+
+enum mqi_cond_entry_type_e {
+ mqi_operator,
+ mqi_variable,
+ mqi_column
+};
+
+enum mqi_event_type_e {
+ mqi_event_unknown = 0,
+ mqi_column_changed,
+ mqi_row_inserted,
+ mqi_row_deleted,
+ mqi_table_created,
+ mqi_table_dropped,
+ mqi_transaction_start,
+ mqi_transaction_end
+};
+
+
+
+typedef uint32_t mqi_handle_t;
+typedef uint32_t mqi_bitfld_t;
+
+typedef enum mqi_data_type_e mqi_data_type_t;
+typedef struct mqi_column_def_s mqi_column_def_t;
+typedef struct mqi_column_desc_s mqi_column_desc_t;
+
+typedef enum mqi_operator_e mqi_operator_t;
+typedef struct mqi_variable_s mqi_variable_t;
+typedef enum mqi_cond_entry_type_e mqi_cond_entry_type_t;
+typedef struct mqi_cond_entry_s mqi_cond_entry_t;
+
+typedef enum mqi_event_type_e mqi_event_type_t;
+typedef union mqi_event_u mqi_event_t;
+
+typedef struct mqi_change_table_s mqi_change_table_t;
+typedef struct mqi_change_select_s mqi_change_select_t;
+typedef struct mqi_change_coldsc_s mqi_change_coldsc_t;
+typedef union mqi_change_data_u mqi_change_data_t;
+typedef struct mqi_change_value_s mqi_change_value_t;
+
+typedef struct mqi_column_event_s mqi_column_event_t;
+typedef struct mqi_row_event_s mqi_row_event_t;
+typedef struct mqi_table_event_s mqi_table_event_t;
+typedef struct mqi_transact_event_s mqi_transact_event_t;
+
+typedef void (*mqi_trigger_cb_t)(mqi_event_t *, void *);
+
+
+
+struct mqi_column_def_s {
+ const char *name;
+ mqi_data_type_t type;
+ int length;
+ uint32_t flags;
+};
+
+struct mqi_column_desc_s {
+ int cindex; /* column index */
+ int offset; /* offset within the data struct */
+};
+
+struct mqi_variable_s {
+ mqi_data_type_t type;
+ uint32_t flags;
+ union {
+ char **varchar;
+ int32_t *integer;
+ uint32_t *unsignd;
+ double *floating;
+ void **blob;
+ void *generic;
+ } v;
+};
+
+
+struct mqi_cond_entry_s {
+ mqi_cond_entry_type_t type;
+ union {
+ mqi_operator_t operator_;
+ mqi_variable_t variable;
+ int column; /* column index actually */
+ } u;
+};
+
+
+struct mqi_change_table_s {
+ mqi_handle_t handle;
+ const char *name;
+};
+
+struct mqi_change_select_s {
+ int length;
+ void *data;
+};
+
+struct mqi_change_coldsc_s {
+ int index;
+ const char *name;
+};
+
+union mqi_change_data_u {
+ char *varchar;
+ char *string;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+ void *generic;
+};
+
+struct mqi_change_value_s {
+ mqi_data_type_t type;
+ mqi_change_data_t old;
+ mqi_change_data_t new_;
+};
+
+
+struct mqi_column_event_s {
+ mqi_event_type_t event;
+ mqi_change_table_t table;
+ mqi_change_coldsc_t column;
+ mqi_change_value_t value;
+ mqi_change_select_t select;
+};
+
+struct mqi_row_event_s {
+ mqi_event_type_t event;
+ mqi_change_table_t table;
+ mqi_change_select_t select;
+};
+
+struct mqi_table_event_s {
+ mqi_event_type_t event;
+ mqi_change_table_t table;
+};
+
+struct mqi_transact_event_s {
+ mqi_event_type_t event;
+ uint32_t depth;
+};
+
+
+union mqi_event_u {
+ mqi_event_type_t event;
+ mqi_column_event_t column;
+ mqi_row_event_t row;
+ mqi_table_event_t table;
+ mqi_transact_event_t transact;
+};
+
+
+const char *mqi_data_type_str(mqi_data_type_t);
+
+int mqi_data_compare_integer(int, void *, void *);
+int mqi_data_compare_unsignd(int, void *, void *);
+int mqi_data_compare_string(int, void *, void *);
+int mqi_data_compare_pointer(int, void *, void *);
+int mqi_data_compare_varchar(int, void *, void *);
+int mqi_data_compare_blob(int, void *, void *);
+
+int mqi_data_print_integer(void *, char *, int);
+int mqi_data_print_unsignd(void *, char *, int);
+int mqi_data_print_string(void *, char *, int);
+int mqi_data_print_pointer(void *, char *, int);
+int mqi_data_print_varchar(void *, char *, int);
+int mqi_data_print_blob(void *, char *, int);
+
+
+#endif /* __MQI_TYPES_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQI_MQI_H__
+#define __MQI_MQI_H__
+
+#include <murphy-db/mqi-types.h>
+
+#define MQI_ALL NULL
+#define MQI_NO_INDEX NULL
+
+/* table flags */
+#define MQI_PERSISTENT (1 << 0)
+#define MQI_TEMPORARY (1 << 1)
+#define MQI_ANY (MQI_PERSISTENT | MQI_TEMPORARY)
+#define MQI_TABLE_TYPE_MASK (MQI_PERSISTENT | MQI_TEMPORARY)
+
+
+#define MQI_COLUMN_DEFINITION(name, type...) \
+ {name, type, 0}
+
+#define MQI_COLUMN_SELECTOR(column_index, result_structure, result_member) \
+ {column_index, MQI_OFFSET(result_structure, result_member)}
+
+#define MQI_VARCHAR(s) mqi_varchar, s
+#define MQI_INTEGER mqi_integer, 0
+#define MQI_UNSIGNED mqi_unsignd, 0
+#define MQI_BLOB(s) mqi_blob, s
+
+#define MQI_COLUMN(column_index) \
+ {.type=mqi_column, .u.column=column_index}
+
+#define MQI_VALUE(typ, val) \
+ {.type=mqi_##typ, .v.typ=val}
+
+#define MQI_VARIABLE(typ, val) \
+ {.type=mqi_variable, .u.variable=MQI_VALUE(typ, val)}
+
+#define MQI_OPERATOR(op) \
+ {.type=mqi_operator, .u.operator_=mqi_##op}
+
+
+#define MQI_EXPRESSION(seq) MQI_OPERATOR(begin), seq, MQI_OPERATOR(end),
+
+
+#define MQI_STRING_VAL(val) MQI_VALUE(varchar, (char **)&val),
+#define MQI_INTEGER_VAL(val) MQI_VALUE(integer, (int32_t *)&val),
+#define MQI_UNSIGNED_VAL(val) MQI_VALUE(unsignd, (uint32_t *)&val),
+#define MQI_BLOB_VAL(val) MQI_VALUE(blob, (void **)&val),
+
+#define MQI_STRING_VAR(val) MQI_VARIABLE(varchar, (char **)&val)
+#define MQI_INTEGER_VAR(val) MQI_VARIABLE(integer, (int32_t *)&val)
+#define MQI_UNSIGNED_VAR(val) MQI_VARIABLE(unsignd, (uint32_t *)&val)
+#define MQI_BLOB_VAR(val) MQI_VARIABLE(blob, (void **)&val)
+
+
+#define MQI_AND MQI_OPERATOR(and),
+#define MQI_OR MQI_OPERATOR(or),
+
+#define MQI_LESS(a,b) a, MQI_OPERATOR(less), b,
+#define MQI_LESS_OR_EQUAL(a,b) a, MQI_OPERATOR(leq), b,
+#define MQI_EQUAL(a,b) a, MQI_OPERATOR(eq), b,
+#define MQI_GREATER_OR_EQUAL(a,b) a, MQI_OPERATOR(geq), b,
+#define MQI_GREATER(a,b) a, MQI_OPERATOR(gt), b,
+
+#define MQI_NOT(val) MQI_OPERATOR(not), val,
+
+#define MQI_COLUMN_DEFINITION_LIST(name, columns...) \
+ static mqi_column_def_t name[] = { \
+ columns, \
+ {NULL, mqi_unknown, 0, 0} \
+ }
+
+#define MQI_INDEX_COLUMN(column_name) column_name,
+
+#define MQI_INDEX_DEFINITION(name, column_names...) \
+ static char *name[] = { \
+ column_names \
+ NULL \
+ }
+
+#define MQI_INDEX_VALUE(name, varlist...) \
+ static mqi_variable_t name[] = {varlist}
+
+#define MQI_COLUMN_SELECTION_LIST(name, columns...) \
+ static mqi_column_desc_t name[] = { \
+ columns, \
+ {-1, 1} \
+ }
+#define MQI_WHERE_CLAUSE(name, seq...) \
+ static mqi_cond_entry_t name[] = { \
+ seq \
+ MQI_OPERATOR(end) \
+ }
+
+#define MQI_BEGIN \
+ mqi_begin_transaction()
+
+#define MQI_COMMIT(id) \
+ mqi_commit_transaction(id)
+
+#define MQI_ROLLBACK(id) \
+ mqi_rollback_transaction(id)
+
+#define MQI_CREATE_TABLE(name, type, column_defs, index_def) \
+ mqi_create_table(name, type, index_def, column_defs)
+
+#define MQI_DESCRIBE(table, coldefs) \
+ mqi_describe(table, coldefs, MQI_DIMENSION(coldefs))
+
+#define MQI_INSERT_INTO(table, column_descs, data) \
+ mqi_insert_into(table, 0, column_descs, (void **)data)
+
+#define MQI_REPLACE(table, column_descs, data) \
+ mqi_insert_into(table, 1, column_descs, (void **)data)
+
+#define MQI_SELECT(columns, table, where, result) \
+ mqi_select(table, where, columns, result, \
+ sizeof(result[0]), MQI_DIMENSION(result))
+
+#define MQI_SELECT_BY_INDEX(columns, table, idxvars, result) \
+ mqi_select_by_index(table, idxvars, columns, result)
+
+#define MQI_UPDATE(table, column_descs, data, where) \
+ mqi_update(table, where, column_descs, data)
+
+#define MQI_DELETE(table, where) \
+ mqi_delete_from(table, where)
+
+
+
+int mqi_open(void);
+int mqi_close(void);
+
+int mqi_show_tables(uint32_t, char **, int);
+
+int mqi_create_transaction_trigger(mqi_trigger_cb_t, void *);
+int mqi_create_table_trigger(mqi_trigger_cb_t, void *);
+int mqi_create_row_trigger(mqi_handle_t, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+int mqi_create_column_trigger(mqi_handle_t, int, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+int mqi_drop_transaction_trigger(mqi_trigger_cb_t, void *);
+int mqi_drop_table_trigger(mqi_trigger_cb_t, void *);
+int mqi_drop_row_trigger(mqi_handle_t, mqi_trigger_cb_t,void *);
+int mqi_drop_column_trigger(mqi_handle_t, int, mqi_trigger_cb_t, void *);
+mqi_handle_t mqi_begin_transaction(void);
+int mqi_commit_transaction(mqi_handle_t);
+int mqi_rollback_transaction(mqi_handle_t);
+mqi_handle_t mqi_get_transaction_handle(void);
+uint32_t mqi_get_transaction_depth(void);
+mqi_handle_t mqi_create_table(char *, uint32_t, char **, mqi_column_def_t *);
+int mqi_create_index(mqi_handle_t, char **);
+int mqi_drop_table(mqi_handle_t);
+int mqi_describe(mqi_handle_t, mqi_column_def_t *, int);
+int mqi_insert_into(mqi_handle_t, int, mqi_column_desc_t *, void **);
+int mqi_delete_from(mqi_handle_t, mqi_cond_entry_t *);
+int mqi_update(mqi_handle_t, mqi_cond_entry_t *, mqi_column_desc_t *, void *);
+int mqi_select(mqi_handle_t, mqi_cond_entry_t *, mqi_column_desc_t *,
+ void *, int, int);
+int mqi_select_by_index(mqi_handle_t, mqi_variable_t *,
+ mqi_column_desc_t *, void *);
+
+mqi_handle_t mqi_get_table_handle(char *);
+int mqi_get_column_index(mqi_handle_t, char *);
+int mqi_get_table_size(mqi_handle_t);
+char *mqi_get_column_name(mqi_handle_t, int);
+mqi_data_type_t mqi_get_column_type(mqi_handle_t, int);
+int mqi_get_column_size(mqi_handle_t, int);
+uint32_t mqi_get_table_stamp(mqi_handle_t);
+int mqi_print_rows(mqi_handle_t, char *, int);
+
+
+#endif /* __MQI_MQI_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQL_RESULT_H__
+#define __MQL_RESULT_H__
+
+#include <murphy-db/mqi-types.h>
+
+
+/** types of mql_result_t structure */
+enum mql_result_type_e {
+ mql_result_error = -1, /**< error code + error message */
+ mql_result_unknown = 0,
+ mql_result_dontcare = mql_result_unknown, /**< will default */
+ mql_result_event,
+ mql_result_columns, /**< column description of a table */
+ mql_result_rows, /**< select'ed rows */
+ mql_result_string, /**< zero terminated ASCII string */
+ mql_result_list, /**< array of basic types, (integer, string, etc) */
+};
+
+typedef enum mql_result_type_e mql_result_type_t;
+typedef struct mql_result_s mql_result_t;
+
+
+/**
+ * @brief generic return type of MQL opertaions.
+ *
+ * mql_result_type_t is the generic return type of mql_exec_string()
+ * and mql_exec_statement(). It is either the return status of the
+ * MQL operation or the resulting data. For instance, executing an
+ * insert statement will return a status (ie. mql_result_error type),
+ * while the execution of a select statement will return the selected
+ * rows (ie. mql_result_rows or mql_result_string depending on what
+ * type was requested by mql_exec_string() or mql_exec_statement())
+ *
+ * To access the opaque data use the mql_result_xxx() functions
+ */
+struct mql_result_s {
+ mql_result_type_t type; /**< type of the result */
+ uint8_t data[0]; /**< opaque result data */
+};
+
+
+
+int mql_result_is_success(mql_result_t *);
+
+int mql_result_error_get_code(mql_result_t *);
+const char *mql_result_error_get_message(mql_result_t *);
+
+int mql_result_columns_get_column_count(mql_result_t *);
+const char *mql_result_columns_get_name(mql_result_t *, int);
+mqi_data_type_t mql_result_columns_get_type(mql_result_t *, int);
+int mql_result_columns_get_length(mql_result_t *, int);
+
+int mql_result_rows_get_row_column_count(mql_result_t *);
+mqi_data_type_t mql_result_rows_get_row_column_type(mql_result_t *, int);
+int mql_result_rows_get_row_column_index(mql_result_t *, int);
+int mql_result_rows_get_row_count(mql_result_t *);
+const char *mql_result_rows_get_string(mql_result_t*, int,int, char*,int);
+int32_t mql_result_rows_get_integer(mql_result_t *, int,int);
+uint32_t mql_result_rows_get_unsigned(mql_result_t *, int,int);
+double mql_result_rows_get_floating(mql_result_t *, int,int);
+
+const char *mql_result_string_get(mql_result_t *);
+
+int mql_result_list_get_length(mql_result_t *);
+const char *mql_result_list_get_string(mql_result_t *, int, char *, int);
+int32_t mql_result_list_get_integer(mql_result_t *, int);
+int32_t mql_result_list_get_unsigned(mql_result_t *, int);
+double mql_result_list_get_floating(mql_result_t *, int);
+
+mqi_event_type_t mql_result_event_get_type(mql_result_t *);
+mql_result_t *mql_result_event_get_changed_rows(mql_result_t *);
+
+void mql_result_free(mql_result_t *);
+
+
+#endif /* __MQL_RESULT_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQL_STATEMENT_H__
+#define __MQL_STATEMENT_H__
+
+#include <murphy-db/mql-result.h>
+
+
+typedef enum {
+ mql_statement_unknown = 0,
+ mql_statement_show_tables,
+ mql_statement_describe,
+ mql_statement_create_table,
+ mql_statement_create_index,
+ mql_statement_drop_table,
+ mql_statement_drop_index,
+ mql_statement_begin,
+ mql_statement_commit,
+ mql_statement_rollback,
+ mql_statement_insert,
+ mql_statement_update,
+ mql_statement_delete,
+ mql_statement_select,
+ /* do not add anything after this */
+ mql_statement_last
+} mql_statement_type_t;
+
+typedef struct mql_statement_s {
+ mql_statement_type_t type;
+ uint8_t data[0];
+} mql_statement_t;
+
+
+mql_result_t *mql_exec_statement(mql_result_type_t, mql_statement_t *);
+int mql_bind_value(mql_statement_t *, int, mqi_data_type_t, ...);
+void mql_statement_free(mql_statement_t *);
+
+
+#endif /* __MQL_STATEMENT_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQL_TRIGGER_H__
+#define __MQL_TRIGGER_H__
+
+#include <murphy-db/mql-result.h>
+
+
+typedef void (*mql_trigger_cb_t)(mql_result_t *, void *);
+
+int mql_register_callback(const char *, mql_result_type_t,
+ mql_trigger_cb_t, void *);
+int mql_unregister_callback(const char *);
+
+
+#endif /* __MQL_TRIGGER_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQL_MQL_H__
+#define __MQL_MQL_H__
+
+#include <murphy-db/mql-statement.h>
+#include <murphy-db/mql-trigger.h>
+
+/**
+ * @brief execute a series of MQL statements stored in a file
+ *
+ * This function is to execute a series of MQL statements stored in a file.
+ * The MQL statements supposed to separated with ';'. No ';' shall follow
+ * the last statement. In case of an error the the execution of the statements
+ * will stop, errno is set and the function will return -1. Error messages
+ * will be written to stderr. Currently there is no programmatic way to figure
+ * out what statement failed and how many statement were sucessfully executed.
+ *
+ * @param [in] file is the path of the file, eg. "~/mql/create_table.mql"
+ *
+ * @return If the execution failed mql_exec_file() returns -1 and errno is
+ * set. It returns 0 if all the statements in the file were
+ * successfully executed.
+ */
+int mql_exec_file(const char *file);
+
+/**
+ * @brief execute an MQL statement
+ *
+ * This function is to execute a single MQL statement. The result of the
+ * operation is returned in a data structure. The returned result can be
+ * either the type of the requested result or an error result. It is
+ * recommended that the returned result will be checked first by calling
+ * mql_result_is_success() function.
+ *
+ * To process a result the relevant mql_result_xxx() functions are to use.
+ * Values returned by the mql_result_xxx() functions are valid till the
+ * next MQL commit or rollback. For instance accessing the returned strings
+ * after a commit might lead to segfaults.
+ *
+ * The returned result should be freed by mql_result_free().
+ *
+ * @param [in] result_type specifies the expected type of the returned
+ * result. However, if the execution failed for
+ * some reason the returned result will have
+ * mql_result_error type.
+ *
+ * @param [in] statement is the string of the MQL statement to execute
+ *
+ * @code
+ * #include <stdio.h>
+ * #include <murphy-db/mql.h>
+ *
+ * const char *statement = "SELECT * FROM persons WHERE name = 'murphy'";
+ * mql_result_t *r = mql_exec_string(mql_result_type_string, statement);
+ *
+ * if (mql_result_is_success(r))
+ * printf("the result of the query:\n%s\n", r->mql_result_string_get(r));
+ * @endcode
+ */
+mql_result_t *mql_exec_string(mql_result_type_t result_type,
+ const char *statement);
+
+/**
+ * @brief precompile an MQL statement
+ *
+ * @param [in] statement is the string of the MQL statement to execute
+ *
+ * For performance optimisation purposes the execution of MQL statements
+ * can be done in a precompilation and an execution phase. This allows
+ * a single first phase for frequently executed MQL statements followed
+ * by a series of second phase.
+ *
+ * Precompilation is the parsing of the ASCII MQL statement, making all the
+ * necessary lookups, generating the data structures what the the underlying
+ * MQI interface needs for the execution and packing all these information
+ * into a dynamically allocated memory block.
+ *
+ * mql_bind_value() can be used to assign values for parameters of the
+ * preecompiled statement, if any.
+ *
+ * A precompiled statement can be executed by mql_exec_statement().
+ *
+ * Precompiled statements should be freed by mql_statement_free() when they
+ * are not needed any more.
+ *
+ * @return mql_precompile() returns a pointer to the precompile statement in
+ * case the precompilation succeeded or NULL if the precompilation
+ * failed. In the later case errno is set to give a clue what went
+ * wrong.
+ *
+ * Note that a successfull precompilation do not garantie the
+ * successfull execution of the precompiled statement. For instance
+ * a successfully precompiled of a SELECT statement will fail
+ * if the table, it tries to operate on, was meanwhile deleted.
+ * @code
+ * #include <stdio.h>
+ * #include <string.h>
+ * #include <errno.h>
+ * #include <murphy-db/mql.h>
+ *
+ * const char *group[] = {"joe", "jack", "murphy", NULL};
+ * const char *query = "SELECT name, email FROM persons WHERE name = %s";
+ * mql_statement_t *stmnt;
+ * mql_result_t *r;
+ * char *person;
+ * int i;
+ *
+ * if ((stmnt = mql_precompilation(query)) == NULL)
+ * printf("precompilation failed (%d): %s\n", errno, strerror(errno));
+ * else {
+ * for (i = 0; (person = group[i]) != NULL; i++) {
+ * if (mql_bind_value(stmnt, 0,mqi_varchar, person) < 0)
+ * printf("bindig failed (%d): %s\n", errno, strerror(errno));
+ * else {
+ * r = mql_exec_statement(mql_result_string, stmnt);
+ * if (!mql_result_is_success(r))
+ * printf("exec failed %d: %s\n",
+ * mql_result_error_get_code(r),
+ * mql_result_error_get_message(r));
+ * else
+ * printf("query %d\n%s\n", i, mql_result_string_get(r));
+ * }
+ * }
+ * }
+ *
+ * mql_statement_free(stmnt);
+ * @endcode
+ */
+mql_statement_t *mql_precompile(const char *statement);
+
+
+#endif /* __MQL_MQL_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_SEQUENCE_H__
+#define __MDB_SEQUENCE_H__
+
+#include <murphy-db/mqi-types.h>
+
+
+#define MDB_SEQUENCE_TABLE_CREATE(type, alloc) \
+ mdb_sequence_table_create(alloc, \
+ mqi_data_compare_##type, \
+ mqi_data_print_##type)
+
+#define MDB_SEQUENCE_FOR_EACH(seq, data, cursor) \
+ for (cursor = NULL; (data = mdb_sequence_iterate(seq, &cursor)); )
+
+#define MDB_SEQUENCE_FOR_EACH_SAFE(seq, data, cursor) \
+ MDB_SEQUENCE_FOR_EACH(seq, data, cursor)
+
+
+typedef struct mdb_sequence_s mdb_sequence_t;
+
+typedef int (*mdb_sequence_compare_t)(int, void *, void *);
+typedef int (*mdb_sequence_print_t)(void *, char *, int);
+
+
+mdb_sequence_t *mdb_sequence_table_create(int, mdb_sequence_compare_t,
+ mdb_sequence_print_t);
+int mdb_sequence_table_destroy(mdb_sequence_t *);
+int mdb_sequence_table_get_size(mdb_sequence_t *);
+int mdb_sequence_table_reset(mdb_sequence_t *);
+int mdb_sequence_table_print(mdb_sequence_t *, char *, int);
+
+int mdb_sequence_add(mdb_sequence_t *, int, void *, void *);
+void *mdb_sequence_delete(mdb_sequence_t *, int, void *);
+void *mdb_sequence_iterate(mdb_sequence_t *, void **);
+void mdb_sequence_cursor_destroy(mdb_sequence_t *, void **);
+
+
+
+#endif /* __MDB_SEQUENCE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+pkglib_LTLIBRARIES = libmdb.la
+
+LINKER_SCRIPT = linker-script.mdb
+QUIET_GEN = $(Q:@=@echo ' GEN '$@;)
+
+libmdb_la_CFLAGS = -I../include
+
+libmdb_ladir = \
+ $(includedir)/murphy-db
+
+libmdb_la_HEADERS = \
+ ../include/murphy-db/assert.h \
+ ../include/murphy-db/list.h \
+ ../include/murphy-db/handle.h \
+ ../include/murphy-db/hash.h \
+ ../include/murphy-db/sequence.h \
+ ../include/murphy-db/mqi-types.h \
+ ../include/murphy-db/mdb.h
+
+libmdb_la_SOURCES = \
+ $(libmdb_la_HEADERS) \
+ list.h handle.c hash.c sequence.c mqi-types.c \
+ column.h column.c \
+ cond.h cond.c \
+ index.h index.c \
+ log.h log.c \
+ row.h row.c \
+ table.h table.c \
+ transaction.h transaction.c \
+ trigger.h trigger.c
+
+libmdb_la_LDFLAGS = \
+ -Wl,-version-script=$(LINKER_SCRIPT)
+# -version-info @MURPHYDB_VERSION_INFO@
+
+libmdb_la_DEPENDENCIES = $(LINKER_SCRIPT)
+
+# linker script generation
+$(LINKER_SCRIPT): $(libmdb_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmdb_la_CFLAGS)" -p "^(mdb_)|(mqi_)" -o $@ $^
+
+clean-$(LINKER_SCRIPT):
+ -rm -f $(LINKER_SCRIPT)
+
+# cleanup
+clean-local:: # clean-$(LINKER_SCRIPT)
+ -rm -f *~
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/list.h>
+#include <murphy-db/handle.h>
+#include <murphy-db/hash.h>
+#include <murphy-db/sequence.h>
+#include "column.h"
+#include "index.h"
+#include "table.h"
+
+static int print_blob(uint8_t *, int data, char *, int);
+
+int mdb_column_write(mdb_column_t *dst_desc, void *dst_data,
+ mqi_column_desc_t *src_desc, void *src_data)
+{
+ int lgh;
+ void *dst, *src;
+ static char *empty = "";
+
+ if (dst_desc && dst_data && src_desc && src_desc->offset >= 0 && src_data){
+ dst = dst_data + dst_desc->offset;
+ src = src_data + src_desc->offset;
+ lgh = dst_desc->length;
+
+ switch (dst_desc->type) {
+
+ case mqi_varchar:
+ if(__builtin_expect(*((char**)src) == NULL, 0))
+ src = ∅
+
+ if (!**(char **)src && !*(char *)dst)
+ goto identical;
+
+ if (!strncmp(*(char **)src, dst, strlen(*(char **)src) + 1))
+ goto identical;
+
+ memset(dst, 0, lgh);
+ strncpy((char *)dst, *(const char **)src, lgh-1);
+ break;
+
+ case mqi_integer:
+ if (*(int32_t *)dst == *(int32_t *)src)
+ goto identical;
+ else
+ *(int32_t *)dst = *(int32_t *)src;
+ break;
+
+ case mqi_unsignd:
+ if (*(uint32_t *)dst == *(uint32_t *)src)
+ goto identical;
+ else
+ *(uint32_t *)dst = *(uint32_t *)src;
+ break;
+
+ case mqi_floating:
+ if (*(double *)dst == *(double *)src)
+ goto identical;
+ else
+ *(double *)dst = *(double *)src;
+ break;
+
+ case mqi_blob:
+ if (!memcmp(dst, src, lgh))
+ goto identical;
+ else
+ memcpy(dst, src, lgh);
+ break;
+
+ default:
+ /* we do not knopw what this is,
+ so we silently ignore it */
+ goto identical;
+ }
+ }
+
+ return 1;
+
+ identical:
+ return 0;
+}
+
+
+void mdb_column_read(mqi_column_desc_t *dst_desc, void *dst_data,
+ mdb_column_t *src_desc, void *src_data)
+{
+ int lgh;
+ void *dst, *src;
+
+ if (dst_desc && dst_data && src_desc && src_data) {
+ dst = dst_data + dst_desc->offset;
+ src = src_data + src_desc->offset;
+ lgh = src_desc->length;
+
+ switch (src_desc->type) {
+
+ case mqi_varchar:
+ *(char **)dst = (char *)src;
+ break;
+
+ case mqi_integer:
+ *(int32_t *)dst = *(int32_t *)src;
+ break;
+
+ case mqi_unsignd:
+ *(uint32_t *)dst = *(uint32_t *)src;
+ break;
+
+ case mqi_floating:
+ *(double *)dst = *(double *)src;
+ break;
+
+ case mqi_blob:
+ memcpy(dst, src, lgh);
+ break;
+
+ default:
+ /* we do not know what this is,
+ so we silently ignore it */
+ break;
+ }
+ }
+}
+
+
+int mdb_column_print_header(mdb_column_t *cdesc, char *buf, int len)
+{
+ int r;
+ int l;
+
+ if (!cdesc || !buf || len < 1)
+ r = 0;
+ else {
+ switch (cdesc->type) {
+ case mqi_varchar: l = cdesc->length; break;
+ case mqi_integer: l = 11; break;
+ case mqi_unsignd: l = 11; break;
+ case mqi_blob: l = cdesc->length > 0 ? (cdesc->length * 3) - 1 : 0;
+ break;
+ default: l = 0; break;
+ }
+
+ r = (l > 0) ? snprintf(buf,len, "%*s", l, cdesc->name) : 0;
+ }
+
+ return r;
+}
+
+
+int mdb_column_print(mdb_column_t *cdesc, void *data, char *buf, int len)
+{
+ int r;
+ int l;
+ void *d;
+
+ if (!cdesc || !data || !buf || len < 1)
+ r = 0;
+ else {
+ d = data + cdesc->offset;
+ l = cdesc->length;
+
+ switch (cdesc->type) {
+ case mqi_varchar: r = snprintf(buf,len, "%*s", l, (char *)d); break;
+ case mqi_integer: r = snprintf(buf,len, "%11d", *(int32_t *)d); break;
+ case mqi_unsignd: r = snprintf(buf,len, " %10u",*(uint32_t*)d); break;
+ case mqi_blob: r = print_blob(d,cdesc->length, buf,len); break;
+ default: r = 0; break;
+ }
+ }
+
+ return r;
+}
+
+static int print_blob(uint8_t *data, int data_len, char *buf, int buflen)
+{
+ MQI_UNUSED(data);
+ MQI_UNUSED(data_len);
+ MQI_UNUSED(buf);
+ MQI_UNUSED(buflen);
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_COLUMN_H__
+#define __MDB_COLUMN_H__
+
+#include <stdint.h>
+
+#define MDB_COLUMN_LENGTH_MAX 1024
+
+
+#include <murphy-db/mqi-types.h>
+
+typedef struct {
+ char *name;
+ mqi_data_type_t type;
+ int length;
+ int offset;
+ uint32_t flags;
+} mdb_column_t;
+
+int mdb_column_write(mdb_column_t *, void *, mqi_column_desc_t *, void *);
+void mdb_column_read(mqi_column_desc_t *, void *, mdb_column_t *, void *);
+int mdb_column_print_header(mdb_column_t *, char *, int);
+int mdb_column_print(mdb_column_t *, void *, char *, int);
+
+#endif /* __MDB_COLUMN_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/list.h>
+#include <murphy-db/handle.h>
+#include <murphy-db/hash.h>
+#include <murphy-db/sequence.h>
+#include "column.h"
+#include "index.h"
+#include "table.h"
+#include "cond.h"
+
+
+typedef struct {
+ mqi_data_type_t type;
+ union {
+ char *varchar;
+ int32_t integer;
+ uint32_t unsignd;
+ void *blob;
+ void *data;
+ } v;
+} cond_data_t;
+
+#define PRECEDENCE_DATA 256
+
+typedef struct {
+ int precedence; /* 256 => data, precedence otherwise */
+ union {
+ cond_data_t data;
+ mqi_operator_t operator;
+ };
+} cond_stack_t;
+
+static int cond_get_data(cond_stack_t*,mqi_cond_entry_t*,mdb_column_t*,void*);
+static int cond_eval(cond_stack_t *, cond_stack_t *, int);
+static int cond_relop(mqi_operator_t, cond_stack_t *, cond_stack_t *);
+static int cond_binary_logicop(mqi_operator_t, cond_stack_t *,
+ cond_stack_t *);
+static int cond_unary_logicop(mqi_operator_t, cond_stack_t *);
+
+int mdb_cond_evaluate(mdb_table_t *tbl, mqi_cond_entry_t **cond_ptr,void *data)
+{
+ static int precedence[mqi_operator_max] = {
+ [ mqi_done ] = 0,
+ [ mqi_begin ] = 1,
+ [ mqi_and ] = 2,
+ [ mqi_or ] = 3,
+ [ mqi_less ] = 4,
+ [ mqi_leq ] = 4,
+ [ mqi_eq ] = 4,
+ [ mqi_geq ] = 4,
+ [ mqi_gt ] = 4,
+ [ mqi_not ] = 5
+ };
+
+ mqi_cond_entry_t *cond = *cond_ptr;
+ cond_stack_t stack[256] = {
+ [0] = { precedence[mqi_begin], { .operator = mqi_begin } }
+ };
+ cond_stack_t *sp = stack + 1;
+ cond_stack_t *lastop = stack;
+ int result;
+ int pr;
+
+ MDB_CHECKARG(cond && data, -1);
+
+ for (;;) {
+ switch (cond->type) {
+
+ case mqi_operator:
+ pr = precedence[cond->u.operator_];
+ sp += cond_eval(sp, lastop, pr);
+
+ switch (cond->u.operator_) {
+
+ case mqi_begin:
+ cond++;
+ result = mdb_cond_evaluate(tbl, &cond, data);
+ cond++;
+
+ sp->data.v.integer = result >= 0 ? result : 0;
+ sp->precedence = PRECEDENCE_DATA;
+ sp->data.type = mqi_integer;
+ sp++;
+ break;
+
+ case mqi_end:
+ *cond_ptr = cond+1;
+ sp--;
+ if (sp->precedence < PRECEDENCE_DATA ||
+ sp->data.type != mqi_integer)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ return sp->data.v.integer ? 1 : 0;
+
+ case mqi_and:
+ case mqi_or:
+ case mqi_less:
+ case mqi_leq:
+ case mqi_eq:
+ case mqi_geq:
+ case mqi_gt:
+ case mqi_not:
+ lastop = sp++;
+ lastop->precedence = pr;
+ lastop->operator = cond->u.operator_;
+ cond++;
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case mqi_variable:
+ case mqi_column:
+ sp += cond_get_data(sp, cond, tbl->columns, data);
+ cond++;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ } /* for ;; */
+}
+
+static int cond_get_data(cond_stack_t *sp,
+ mqi_cond_entry_t *cond,
+ mdb_column_t *columns,
+ void *data)
+{
+ mqi_column_desc_t sp_desc[2];
+ cond_data_t *sd;
+ mdb_column_t *col_desc;
+ mqi_variable_t *var;
+ int ok;
+
+ switch (cond->type) {
+
+ case mqi_variable:
+ sd = &sp->data;
+ var = &cond->u.variable;
+
+ if (!var->v.generic)
+ ok = 0;
+ else {
+ switch ((sp->data.type = var->type)) {
+ case mqi_varchar: sd->v.varchar = *var->v.varchar; ok = 1; break;
+ case mqi_integer: sd->v.integer = *var->v.integer; ok = 1; break;
+ case mqi_unsignd: sd->v.unsignd = *var->v.unsignd; ok = 1; break;
+ case mqi_blob: sd->v.blob = *var->v.blob; ok = 1; break;
+ default: ok = 0; break;
+ }
+ }
+ break;
+
+ case mqi_column: {
+ col_desc = columns + cond->u.column;
+ sp_desc[0].cindex = cond->u.column;
+ sp_desc[0].offset = 0;
+ sp_desc[1].cindex = -1;
+ sp_desc[1].offset = -1;
+ mdb_column_read(sp_desc, &sp->data.v.data, col_desc, data);
+ sp->data.type = col_desc->type;
+ ok = 1;
+ }
+ break;
+
+ default:
+ ok = 0;
+ break;
+ }
+
+ sp->precedence = PRECEDENCE_DATA * ok;
+
+ return ok;
+}
+
+static int cond_eval(cond_stack_t *sp,cond_stack_t *lastop,int new_precedence)
+{
+ cond_stack_t *result;
+ cond_stack_t *newsp = sp;
+ int value;
+ int stack_advance = 0;
+
+ while (new_precedence < lastop->precedence) {
+ switch (lastop->operator) {
+
+ case mqi_begin:
+ /* stack: (0)begin, (1)operand => (0)result */
+ newsp = (result = lastop) + 1;
+ value = (lastop+1)->data.v.integer;
+ new_precedence = INT_MAX;
+ goto store_on_stack;
+
+ case mqi_and:
+ case mqi_or:
+ /* stack: (-1)operand1, (0)operator, (1)operand2 => (-1)result */
+ result = (newsp = lastop) - 1;
+ value = cond_binary_logicop(lastop->operator, lastop-1,lastop+1);
+ goto find_new_lastop_and_store_on_stack;
+
+ case mqi_less:
+ case mqi_leq:
+ case mqi_eq:
+ case mqi_geq:
+ case mqi_gt:
+ /* stack: (-1)operand1, (0)operator, (1)operand2 => (-1)result */
+ result = (newsp = lastop) - 1;
+ value = cond_relop(lastop->operator, lastop-1,lastop+1);
+ goto find_new_lastop_and_store_on_stack;
+
+ case mqi_not:
+ /* stack: (0)operator, (1)operand => (0)result */
+ newsp = (result = lastop) + 1;
+ value = cond_unary_logicop(lastop->operator, lastop+1);
+ goto find_new_lastop_and_store_on_stack;
+
+ find_new_lastop_and_store_on_stack:
+ for (lastop--; lastop->precedence >= PRECEDENCE_DATA; lastop--)
+ ;
+ /* intentional fall over */
+
+ store_on_stack:
+ result->precedence = PRECEDENCE_DATA;
+ result->data.type = mqi_integer;
+ result->data.v.integer = value;
+ /* intentional fall over */
+
+ default:
+ stack_advance = newsp - sp;
+ break;
+ }
+ }
+
+ return stack_advance;
+}
+
+
+static int cond_relop(mqi_operator_t op, cond_stack_t *v1, cond_stack_t *v2)
+{
+ cond_data_t *d1 = &v1->data;
+ cond_data_t *d2 = &v2->data;
+ int cmp;
+
+ if (v1->precedence >= PRECEDENCE_DATA &&
+ v2->precedence >= PRECEDENCE_DATA &&
+ d1->type == d2->type)
+ {
+ switch (d1->type) {
+ case mqi_varchar:
+ if (!d1->v.varchar && !d2->v.varchar)
+ cmp = 0;
+ else if (!d1->v.varchar)
+ cmp = -1;
+ else if (!d2->v.varchar)
+ cmp = 1;
+ else
+ cmp = strcmp(d1->v.varchar, d2->v.varchar);
+ break;
+
+ case mqi_integer:
+ if (d1->v.integer > d2->v.integer)
+ cmp = 1;
+ else if (d1->v.integer == d2->v.integer)
+ cmp = 0;
+ else
+ cmp = -1;
+ break;
+
+ case mqi_unsignd:
+ if (d1->v.unsignd > d2->v.unsignd)
+ cmp = 1;
+ else if (d1->v.unsignd == d2->v.unsignd)
+ cmp = 0;
+ else
+ cmp = -1;
+ break;
+
+ default:
+ return 0;
+ }
+
+ switch (op) {
+ case mqi_less: return cmp < 0;
+ case mqi_leq: return cmp <= 0;
+ case mqi_eq: return cmp == 0;
+ case mqi_geq: return cmp >= 0;
+ case mqi_gt: return cmp > 0;
+ default: return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int cond_binary_logicop(mqi_operator_t op,
+ cond_stack_t *v1,
+ cond_stack_t *v2)
+{
+ cond_data_t *d1 = &v1->data;
+ cond_data_t *d2 = &v2->data;
+
+ if (v1->precedence >= PRECEDENCE_DATA &&
+ v2->precedence >= PRECEDENCE_DATA &&
+ d1->type == d2->type)
+ {
+ switch (op) {
+
+ case mqi_and:
+ switch (d1->type) {
+ case mqi_integer: return d1->v.integer && d2->v.integer;
+ case mqi_unsignd: return d1->v.unsignd && d2->v.unsignd;
+ default: return 0;
+ }
+ break;
+
+ case mqi_or:
+ switch (d1->type) {
+ case mqi_integer: return d1->v.integer || d2->v.integer;
+ case mqi_unsignd: return d1->v.unsignd || d2->v.unsignd;
+ default: return 0;
+ }
+ break;
+
+ default:
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int cond_unary_logicop(mqi_operator_t op, cond_stack_t *v)
+{
+ cond_data_t *d = &v->data;
+
+ if (v->precedence >= PRECEDENCE_DATA && op == mqi_not) {
+ switch (d->type) {
+ case mqi_varchar: return d->v.varchar && d->v.varchar[0] ? 0 : 1;
+ case mqi_integer: return d->v.integer ? 0 : 1;
+ case mqi_unsignd: return d->v.unsignd ? 0 : 1;
+ default: return 0;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_COND_H__
+#define __MDB_COND_H__
+
+#include <murphy-db/mqi-types.h>
+#include <murphy-db/mdb.h>
+
+
+int mdb_cond_evaluate(mdb_table_t *, mqi_cond_entry_t **, void *);
+
+
+#endif /* __MDB_COND_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define __USE_GNU
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/handle.h>
+
+#define HANDLE_INDEX_INVALID -1
+#define HANDLE_USEID_INVALID -1
+
+#define HANDLE_USEID_BITS 16
+#define HANDLE_INDEX_BITS ((sizeof(mdb_handle_t) * 8) - HANDLE_USEID_BITS)
+#define HANDLE_USEID_MAX (((mdb_handle_t)1) << HANDLE_USEID_BITS)
+#define HANDLE_INDEX_MAX (((mdb_handle_t)1) << HANDLE_INDEX_BITS)
+#define HANDLE_USEID_MASK (HANDLE_USEID_MAX - 1)
+#define HANDLE_INDEX_MASK (HANDLE_INDEX_MAX - 1)
+
+#define HANDLE_MAKE(useid, index) ( \
+ (((mdb_handle_t)(useid) & HANDLE_USEID_MASK) << HANDLE_INDEX_BITS) | \
+ (((mdb_handle_t)(index) & HANDLE_INDEX_MASK)) \
+)
+
+#define HANDLE_USEID(h) (((h) >> HANDLE_INDEX_BITS) & HANDLE_USEID_MASK)
+#define HANDLE_INDEX(h) ((h) & HANDLE_INDEX_MASK)
+
+
+typedef long long int bucket_t;
+
+
+typedef struct {
+ int nbucket;
+ bucket_t *buckets;
+} freemap_t;
+
+typedef struct {
+ uint32_t useid;
+ void *data;
+} indextbl_entry_t;
+
+
+typedef struct {
+ int nentry;
+ indextbl_entry_t *entries;
+} indextbl_t;
+
+struct mdb_handle_map_s {
+ freemap_t freemap;
+ indextbl_t indextbl;
+};
+
+
+static mdb_handle_t index_alloc(indextbl_t *, int, void *);
+static void *index_realloc(indextbl_t *, uint32_t, int, void *);
+static void *index_free(indextbl_t *, uint32_t, int);
+static int freemap_alloc(freemap_t *);
+static int freemap_free(freemap_t *, int);
+
+static bucket_t empty_bucket = ~((bucket_t)0);
+static int bits_per_bucket = sizeof(bucket_t) * 8;
+
+
+mdb_handle_map_t *mdb_handle_map_create(void)
+{
+ mdb_handle_map_t *hmap;
+
+ if (!(hmap = calloc(1, sizeof(mdb_handle_map_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ return hmap;
+}
+
+int mdb_handle_map_destroy(mdb_handle_map_t *hmap)
+{
+ MDB_CHECKARG(hmap, -1);
+
+ free(hmap->freemap.buckets);
+ free(hmap->indextbl.entries);
+
+ free(hmap);
+
+ return 0;
+}
+
+
+mdb_handle_t mdb_handle_add(mdb_handle_map_t *hmap, void *data)
+{
+ int index;
+
+ MDB_CHECKARG(hmap && data, MDB_HANDLE_INVALID);
+
+ if ((index = freemap_alloc(&hmap->freemap)) == HANDLE_INDEX_INVALID) {
+ return MDB_HANDLE_INVALID;
+ }
+
+ return index_alloc(&hmap->indextbl, index, data);
+}
+
+void *mdb_handle_delete(mdb_handle_map_t *hmap, mdb_handle_t h)
+{
+ uint32_t useid = HANDLE_USEID(h);
+ int index = HANDLE_INDEX(h);
+ void *old_data;
+
+ MDB_CHECKARG(hmap && h != MDB_HANDLE_INVALID, NULL);
+
+
+ if (!(old_data = index_free(&hmap->indextbl, useid,index))) {
+ /* errno has been set by index_free() */
+ return NULL;
+ }
+
+ if (freemap_free(&hmap->freemap, index) < 0) {
+ /* errno has been set by freemap_free() */
+ return NULL;
+ }
+
+ return old_data;
+}
+
+void *mdb_handle_get_data(mdb_handle_map_t *hmap, mdb_handle_t h)
+{
+ uint32_t useid = HANDLE_USEID(h);
+ int index = HANDLE_INDEX(h);
+ indextbl_t *indextbl;
+ indextbl_entry_t *entry;
+
+ MDB_CHECKARG(hmap && h != MDB_HANDLE_INVALID, NULL);
+
+ indextbl = &hmap->indextbl;
+
+ if (index >= indextbl->nentry) {
+ errno = EKEYREJECTED;
+ return NULL;
+ }
+
+ entry = indextbl->entries + index;
+
+ if (entry->useid != useid) {
+ errno = ENOANO;
+ return NULL;
+ }
+
+ if (!entry->data)
+ errno = ENODATA;
+
+ return entry->data;
+}
+
+
+int mdb_handle_print(mdb_handle_map_t *hmap, char *buf, int len)
+{
+ indextbl_t *it;
+ char *p, *e;
+ int i;
+
+ MDB_CHECKARG(hmap && buf && len > 0, -1);
+
+ it = &hmap->indextbl;
+ e = (p = buf) + len;
+
+ p += snprintf(p, e-p, " useid index data\n");
+
+ for (i = 0; i < it->nentry && e > p; i++) {
+ indextbl_entry_t *en = it->entries + i;
+
+ if (en->data)
+ p += snprintf(p, e-p, " %5u %5d %p\n",en->useid, i, en->data);
+ }
+
+ return p - buf;
+}
+
+
+static mdb_handle_t index_alloc(indextbl_t *indextbl, int index, void *data)
+{
+#define ALIGN(i,a) ((((i) + (a)-1) / a) * a)
+
+ mdb_handle_t handle;
+ int nentry;
+ indextbl_entry_t *entries, *entry;
+ size_t size;
+
+ MDB_CHECKARG(index >= 0 && (mdb_handle_t)index < HANDLE_INDEX_MAX && data,
+ MDB_HANDLE_INVALID);
+
+ if (index >= indextbl->nentry) {
+ nentry = ALIGN(index + 1, bits_per_bucket);
+ size = sizeof(indextbl_entry_t) * nentry;
+ entries = realloc(indextbl->entries, size);
+
+ if (!entries) {
+ errno = ENOMEM;
+ return MDB_HANDLE_INVALID;
+ }
+
+ size = sizeof(indextbl_entry_t) * (nentry - indextbl->nentry);
+ memset(entries + indextbl->nentry, 0, size);
+
+ indextbl->nentry = nentry;
+ indextbl->entries = entries;
+ }
+
+ entry = indextbl->entries + index;
+
+ if (entry->data && entry->data != data) {
+ errno = EBUSY;
+ return MDB_HANDLE_INVALID;
+ }
+
+ entry->useid += 1;
+ entry->data = data;
+
+ handle = HANDLE_MAKE(entry->useid, index);
+
+ return handle;
+
+#undef ALIGN
+}
+
+
+static void *index_realloc(indextbl_t *indextbl,
+ uint32_t useid,
+ int index,
+ void *data)
+{
+ indextbl_entry_t *entry;
+ void *old_data;
+
+ MDB_CHECKARG(indextbl, NULL);
+
+ if (index < 0 || index >= indextbl->nentry) {
+ errno = EKEYREJECTED;
+ return NULL;
+ }
+
+ entry = indextbl->entries + index;
+
+ if (entry->useid != useid) {
+ errno = ENOKEY;
+ return NULL;
+ }
+
+ if (!(old_data = entry->data)) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+
+ entry->data = data;
+
+ return old_data;
+}
+
+static void *index_free(indextbl_t *indextbl, uint32_t useid, int index)
+{
+ return index_realloc(indextbl, useid,index, NULL);
+}
+
+static int freemap_alloc(freemap_t *freemap)
+{
+ bucket_t mask;
+ bucket_t *bucket;
+ int nbucket;
+ bucket_t *buckets;
+ int bucket_idx;
+ int bit_idx;
+ int index;
+ size_t size;
+
+ for (bucket_idx = 0; bucket_idx < freemap->nbucket; bucket_idx++) {
+ bucket = freemap->buckets + bucket_idx;
+
+ if (*bucket && (bit_idx = ffsll(*bucket) - 1) >= 0) {
+ index = bucket_idx * bits_per_bucket + bit_idx;
+ mask = ~(((bucket_t)1) << bit_idx);
+ *bucket &= mask;
+ return index;
+ }
+ }
+
+ index = bucket_idx * bits_per_bucket;
+ nbucket = bucket_idx + 1;
+ size = sizeof(bucket_t) * nbucket;
+ buckets = realloc(freemap->buckets, size);
+
+ if (!buckets) {
+ errno = ENOMEM;
+ return HANDLE_INDEX_INVALID;
+ }
+
+ buckets[bucket_idx] = ~((bucket_t)1);
+
+ freemap->nbucket = nbucket;
+ freemap->buckets = buckets;
+
+ return index;
+}
+
+
+static int freemap_free(freemap_t *freemap, int index)
+{
+ int bucket_idx = index / bits_per_bucket;
+ int bit_idx = index % bits_per_bucket;
+ int nbucket;
+ bucket_t *buckets;
+ size_t size;
+
+ if (freemap && index >= 0 && bucket_idx < freemap->nbucket) {
+ freemap->buckets[bucket_idx] |= ((bucket_t)1) << bit_idx;
+
+ if ((bucket_idx + 1 == freemap->nbucket) &&
+ freemap->buckets[bucket_idx] == empty_bucket) {
+ if (freemap->nbucket == 1) {
+ free(freemap->buckets);
+ freemap->nbucket = 0;
+ freemap->buckets = NULL;
+ }
+ else {
+ nbucket = bucket_idx;
+ size = sizeof(bucket_t) * nbucket;
+ buckets = realloc(freemap->buckets, size);
+
+ if (!buckets) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ freemap->buckets = buckets;
+ }
+ }
+
+ return 0;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/hash.h>
+#include <murphy-db/list.h>
+
+#ifndef HASH_STATISTICS
+#define HASH_STATISTICS
+#endif
+
+typedef struct mdb_hash_entry_s {
+ mdb_dlist_t clink; /* hash link, ie. chaining */
+ mdb_dlist_t elink; /* entry link, ie. linking all entries */
+ void *key;
+ void *data;
+} hash_entry_t;
+
+typedef struct {
+ mdb_dlist_t head;
+#ifdef HASH_STATISTICS
+ struct {
+ int curr;
+ int max;
+ } entries;
+#endif
+} hash_chain_t;
+
+
+struct mdb_hash_s {
+ int bits;
+ mdb_hash_function_t hfunc;
+ mdb_hash_compare_t hcomp;
+ mdb_hash_print_t hprint;
+ struct {
+ mdb_dlist_t head;
+#ifdef HASH_STATISTICS
+ int curr;
+ int max;
+#endif
+ } entries;
+ int nchain;
+ hash_chain_t chains[0];
+};
+
+
+typedef struct {
+ int nchain;
+ int bits;
+} table_size_t;
+
+static table_size_t sizes[] = {
+ { 2, 2}, { 3, 2}, { 5, 3}, { 7, 3}, { 11, 4},
+ { 13, 4}, { 17, 5}, { 19, 5}, { 23, 5}, { 29, 5},
+ { 31, 5}, { 37, 6}, { 41, 6}, { 43, 6}, { 47, 6},
+ { 53, 6}, { 59, 6}, { 61, 6}, { 67, 7}, { 71, 7},
+ { 73, 7}, { 79, 7}, { 83, 7}, { 89, 7}, { 97, 7},
+ { 101, 7}, { 103, 7}, { 107, 7}, { 109, 7}, { 113, 7},
+ { 127, 7}, { 131, 8}, { 137, 8}, { 139, 8}, { 149, 8},
+ { 151, 8}, { 157, 8}, { 163, 8}, { 167, 8}, { 173, 8},
+ { 179, 8}, { 181, 8}, { 191, 8}, { 193, 8}, { 197, 8},
+ { 199, 8}, { 211, 8}, { 223, 8}, { 227, 8}, { 229, 8},
+ { 233, 8}, { 239, 8}, { 241, 8}, { 251, 8}, { 257, 9},
+ { 263, 9}, { 269, 9}, { 271, 9}, { 277, 9}, { 281, 9},
+ { 283, 9}, { 293, 9}, { 307, 9}, { 311, 9}, { 313, 9},
+ { 317, 9}, { 331, 9}, { 337, 9}, { 347, 9}, { 349, 9},
+ { 353, 9}, { 359, 9}, { 367, 9}, { 373, 9}, { 379, 9},
+ { 383, 9}, { 389, 9}, { 397, 9}, { 401, 9}, { 409, 9},
+ { 419, 9}, { 421, 9}, { 431, 9}, { 433, 9}, { 439, 9},
+ { 443, 9}, { 449, 9}, { 457, 9}, { 461, 9}, { 463, 9},
+ { 467, 9}, { 479, 9}, { 487, 9}, { 491, 9}, { 499, 9},
+ { 503, 9}, { 509, 9}, { 521, 10}, { 523, 10}, { 541, 10},
+ { 547, 10}, { 557, 10}, { 563, 10}, { 569, 10}, { 571, 10},
+ { 577, 10}, { 587, 10}, { 593, 10}, { 599, 10}, { 601, 10},
+ { 607, 10}, { 613, 10}, { 617, 10}, { 619, 10}, { 631, 10},
+ { 641, 10}, { 643, 10}, { 647, 10}, { 653, 10}, { 659, 10},
+ { 661, 10}, { 673, 10}, { 677, 10}, { 683, 10}, { 691, 10},
+ { 701, 10}, { 709, 10}, { 719, 10}, { 727, 10}, { 733, 10},
+ { 739, 10}, { 743, 10}, { 751, 10}, { 757, 10}, { 761, 10},
+ { 769, 10}, { 773, 10}, { 787, 10}, { 797, 10}, { 809, 10},
+ { 811, 10}, { 821, 10}, { 823, 10}, { 827, 10}, { 829, 10},
+ { 839, 10}, { 853, 10}, { 857, 10}, { 859, 10}, { 863, 10},
+ { 877, 10}, { 881, 10}, { 883, 10}, { 887, 10}, { 907, 10},
+ { 911, 10}, { 919, 10}, { 929, 10}, { 937, 10}, { 941, 10},
+ { 947, 10}, { 953, 10}, { 967, 10}, { 971, 10}, { 977, 10},
+ { 983, 10}, { 991, 10}, { 997, 10}, {65535, 16}
+};
+static uint32_t charmap[256] = {
+ /* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f */
+ /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 20 */ 0, 0, 0, 0, 0, 0, 0, 0, 52, 53, 54, 55, 56, 37, 40, 50,
+ /* 30 */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 41, 0, 42, 43, 44, 45,
+ /* 40 */ 46, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ /* 50 */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 47, 48, 49, 51, 38,
+ /* 60 */ 0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ /* 70 */ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 57, 58, 59, 60, 0,
+ /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static void htable_reset(mdb_hash_t *, int);
+static table_size_t *get_table_size(int);
+static int print_chain(mdb_hash_t *, int, char *, int);
+
+
+mdb_hash_t *mdb_hash_table_create(int max_entries,
+ mdb_hash_function_t hfunc,
+ mdb_hash_compare_t hcomp,
+ mdb_hash_print_t hprint)
+{
+ mdb_hash_t *htbl;
+ table_size_t *ts;
+ size_t size;
+ int i;
+
+ MDB_CHECKARG(hfunc && hcomp && hprint &&
+ max_entries > 1 && max_entries < 65536, NULL);
+
+ if ((ts = get_table_size(max_entries)) == NULL) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+
+ size = sizeof(mdb_hash_t) + sizeof(hash_chain_t) * ts->nchain;
+ htbl = calloc(1, size);
+
+ if (!htbl) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ htbl->bits = ts->bits;
+ htbl->nchain = ts->nchain;
+ htbl->hfunc = hfunc;
+ htbl->hcomp = hcomp;
+ htbl->hprint = hprint;
+
+ MDB_DLIST_INIT(htbl->entries.head);
+
+ for (i = 0; i < htbl->nchain; i++)
+ MDB_DLIST_INIT(htbl->chains[i].head);
+
+ return htbl;
+}
+
+int mdb_hash_table_destroy(mdb_hash_t *htbl)
+{
+ MDB_CHECKARG(htbl, -1);
+
+ htable_reset(htbl, 0);
+ free(htbl);
+
+ return 0;
+}
+
+int mdb_hash_table_reset(mdb_hash_t *htbl)
+{
+ MDB_CHECKARG(htbl, -1);
+
+ htable_reset(htbl, 1);
+
+ return 0;
+}
+
+void *mdb_hash_table_iterate(mdb_hash_t *htbl,void **key_ret,void **cursor_ptr)
+{
+ mdb_dlist_t *head, *link;
+ hash_entry_t *entry;
+
+ MDB_CHECKARG(htbl && cursor_ptr, NULL);
+
+ head = &htbl->entries.head;
+
+ if (!(link = *cursor_ptr))
+ *cursor_ptr = link = head->next;
+
+ if (link == head)
+ return NULL;
+
+ *cursor_ptr = link->next;
+
+ entry = MDB_LIST_RELOCATE(hash_entry_t, elink, link);
+
+ if (key_ret)
+ *key_ret = entry->key;
+
+ return entry->data;
+}
+
+int mdb_hash_table_print(mdb_hash_t *htbl, char *buf, int len)
+{
+ char *p, *e;
+ int i;
+
+ MDB_CHECKARG(htbl && buf && len > 0, 0);
+
+ e = (p = buf) + len;
+ *buf = '\0';
+
+ for (i = 0; i < htbl->nchain; i++) {
+ if (!MDB_DLIST_EMPTY(htbl->chains[i].head)
+#ifdef HASH_STATISTICS
+ || htbl->chains[i].entries.max > 0
+#endif
+ )
+ p += print_chain(htbl, i, p, e-p);
+ }
+
+ return p - buf;
+}
+
+int mdb_hash_add(mdb_hash_t *htbl, int klen, void *key, void *data)
+{
+ hash_entry_t *entry;
+ hash_chain_t *chain;
+ int index;
+
+ MDB_CHECKARG(htbl && key && klen >= 0 && data, -1);
+
+ index = htbl->hfunc(htbl->bits, htbl->nchain, klen, key);
+ chain = htbl->chains + index;
+
+ MDB_DLIST_FOR_EACH(hash_entry_t, clink, entry, &chain->head) {
+ if (htbl->hcomp(klen, key, entry->key) == 0) {
+ if (data == entry->data)
+ return 0;
+ else {
+ errno = EEXIST;
+ return -1;
+ }
+ }
+ }
+
+ if (!(entry = calloc(1, sizeof(hash_entry_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+ entry->key = key;
+ entry->data = data;
+
+ MDB_DLIST_APPEND(hash_entry_t, clink, entry, &chain->head);
+ MDB_DLIST_APPEND(hash_entry_t, elink, entry, &htbl->entries.head);
+
+#ifdef HASH_STATISTICS
+ if (++chain->entries.curr > chain->entries.max)
+ chain->entries.max = chain->entries.curr;
+
+ if (++htbl->entries.curr > htbl->entries.max)
+ htbl->entries.max = htbl->entries.curr;
+#endif
+
+ return 0;
+}
+
+void *mdb_hash_delete(mdb_hash_t *htbl, int klen, void *key)
+{
+ hash_entry_t *entry;
+ hash_entry_t *n;
+ hash_chain_t *chain;
+ int index;
+ void *data;
+
+ MDB_CHECKARG(htbl && klen >= 0 && key, NULL);
+
+ index = htbl->hfunc(htbl->bits, htbl->nchain, klen, key);
+ chain = htbl->chains + index;
+
+ MDB_DLIST_FOR_EACH_SAFE(hash_entry_t, clink, entry,n, &chain->head) {
+ if (htbl->hcomp(klen, key, entry->key) == 0) {
+ if (!(data = entry->data))
+ break;
+
+ MDB_DLIST_UNLINK(hash_entry_t, clink, entry);
+ MDB_DLIST_UNLINK(hash_entry_t, elink, entry);
+ free(entry);
+
+#ifdef HASH_STATISTICS
+ if (--chain->entries.curr < 0)
+ chain->entries.curr = 0;
+
+ if (--htbl->entries.curr < 0)
+ htbl->entries.curr = 0;
+#endif
+ return data;
+ }
+ }
+
+ errno = ENOENT;
+ return NULL;
+}
+
+void *mdb_hash_get_data(mdb_hash_t *htbl, int klen, void *key)
+{
+ hash_entry_t *entry;
+ hash_chain_t *chain;
+ int index;
+
+ MDB_CHECKARG(htbl && klen >= 0 && key, NULL);
+
+ index = htbl->hfunc(htbl->bits, htbl->nchain, klen, key);
+ chain = htbl->chains + index;
+
+ MDB_DLIST_FOR_EACH(hash_entry_t, clink, entry, &chain->head) {
+ if (htbl->hcomp(klen, key, entry->key) == 0)
+ return entry->data;
+ }
+
+ errno = ENOENT;
+ return NULL;
+}
+
+
+int mdb_hash_function_integer(int bits, int nchain, int klen, void *key)
+{
+ return mdb_hash_function_unsignd(bits, nchain, klen, key);
+}
+
+
+int mdb_hash_function_unsignd(int bits, int nchain, int klen, void *key)
+{
+ uint32_t unsignd;
+
+ if (klen != sizeof(unsignd) || !key ||
+ bits < 1 || bits > 16 ||
+ nchain < (1 << (bits-1)) || nchain >= (1 << bits))
+ return 0;
+
+ unsignd = *(uint32_t *)key;
+
+ return (int)(unsignd % nchain);
+}
+
+
+int mdb_hash_function_string(int bits, int nchain, int klen, void *key)
+{
+ typedef union {
+ uint64_t wide;
+ uint8_t narrow[8];
+ } hash_t;
+
+ (void)klen;
+
+ uint8_t *varchar = (uint8_t *)key;
+ int hashval = 0;
+ hash_t h;
+ int shift;
+
+ if (varchar && bits >= 1 && bits <= 16 &&
+ nchain > (1 << (bits-1)) && nchain < (1 << bits))
+ {
+ uint8_t s;
+ int i;
+
+ for (h.wide = 0; (s = *varchar); varchar++)
+ h.wide = 33ULL * h.wide + (uint64_t)charmap[s];
+
+ if (bits <= 8) {
+ hashval = h.narrow[0] ^ h.narrow[1] ^ h.narrow[2] ^ h.narrow[3] ^
+ h.narrow[4] ^ h.narrow[5] ^ h.narrow[6] ^ h.narrow[7];
+ }
+ else {
+ shift = (nchain + 7) / 8;
+ for (hashval = h.narrow[0], i = 1; i < 8; i++)
+ hashval ^= h.narrow[i] << (i * shift);
+ }
+
+ hashval %= nchain;
+ }
+
+ return hashval;
+}
+
+int mdb_hash_function_pointer(int bits, int nchain, int klen, void *key)
+{
+#define MASK(t) ((((uint##t##_t)1) << (sizeof(int) * 8 - 3)) - 1)
+ int hash;
+
+ MQI_UNUSED(bits);
+ MQI_UNUSED(klen);
+
+#if __SIZEOF_POINTER__ == 8
+ hash = (int)(((uint64_t)key >> 2) & MASK(64)) % nchain;
+#else
+ hash = ((int)key >> 2) & MASK(32) % nchain;
+#endif
+
+ return hash;
+#undef MASK
+}
+
+int mdb_hash_function_varchar(int bits, int nchain, int klen, void *key)
+{
+ return mdb_hash_function_string(bits, nchain, klen, key);
+}
+
+int mdb_hash_function_blob(int bits, int nchain, int klen, void *key)
+{
+ typedef union {
+ uint64_t wide;
+ uint8_t narrow[8];
+ } hash_t;
+
+ uint8_t *data = (uint8_t *)key;
+ int hashval = 0;
+ hash_t h;
+ int shift;
+ int i;
+
+ if (klen > 0 && data && bits >= 1 && bits <= 16 &&
+ nchain > (1 << (bits-1)) && nchain < (1 << bits))
+ {
+ for (i = 0, h.wide = 0; i < klen; i++)
+ h.wide = 33ULL * h.wide + (uint64_t)data[i];
+
+ if (bits <= 8) {
+ hashval = h.narrow[0] ^ h.narrow[1] ^ h.narrow[2] ^ h.narrow[3] ^
+ h.narrow[4] ^ h.narrow[5] ^ h.narrow[6] ^ h.narrow[7];
+ }
+ else {
+ shift = (nchain + 7) / 8;
+ for (hashval = h.narrow[0], i = 1; i < 8; i++)
+ hashval ^= h.narrow[i] << (i * shift);
+ }
+
+ hashval %= nchain;
+ }
+
+ return hashval;
+}
+
+
+
+static void htable_reset(mdb_hash_t *htbl, int do_chain_statistics)
+{
+ hash_entry_t *entry;
+ hash_entry_t *n;
+#ifdef HASH_STATISTICS
+ int i;
+#else
+ (void)do_statistics;
+#endif
+
+ MDB_DLIST_FOR_EACH_SAFE(hash_entry_t, elink, entry,n, &htbl->entries.head){
+ MDB_DLIST_UNLINK(hash_entry_t, clink, entry);
+ MDB_DLIST_UNLINK(hash_entry_t, elink, entry);
+ free(entry);
+ }
+
+#ifdef HASH_STATISTICS
+ if (do_chain_statistics) {
+ for (i = 0; i < htbl->nchain; i++) {
+ htbl->chains[i].entries.curr = 0;
+ }
+ }
+
+ htbl->entries.curr = 0;
+#endif
+}
+
+static table_size_t *get_table_size(int max_entries)
+{
+ int dim = sizeof(sizes)/sizeof(sizes[0]);
+ int min = 0;
+ int max = dim - 1;
+ int idx;
+#ifdef DEBUG
+ int iterations = 0;
+#endif
+
+ for (;;) {
+#ifdef DEBUG
+ iterations++;
+#endif
+ idx = (min + max) / 2;
+
+ if (max_entries == sizes[idx].nchain)
+ break;
+
+ if (idx == min) {
+ idx = max;
+ break;
+ }
+
+ if (max_entries < sizes[idx].nchain)
+ max = idx;
+ else
+ min = idx;
+ }
+
+#ifdef DEBUG
+ printf("%s(%d) => {%d,%d} @ %d\n", __FUNCTION__, max_entries,
+ sizes[idx].nchain, sizes[idx].bits, iterations);
+#endif
+
+ return sizes + idx;
+}
+
+static int print_chain(mdb_hash_t *htbl, int index, char *buf, int len)
+{
+ hash_chain_t *chain = htbl->chains + index;
+ hash_entry_t *entry;
+ char *p, *e;
+ char key[256];
+
+ e = (p = buf) + len;
+
+#ifdef HASH_STATISTICS
+ p += snprintf(p, e-p, " %05d: %d/%d\n",
+ index, chain->entries.curr, chain->entries.max);
+#else
+ p += snprintf(p, e-p, " %05d\n", index);
+#endif
+
+ MDB_DLIST_FOR_EACH(hash_entry_t, clink, entry, &chain->head) {
+ if (p >= e)
+ break;
+
+ htbl->hprint(entry->key, key, sizeof(key));
+
+ p += snprintf(p, e-p, " '%s' / %p\n", key, entry->data);
+ }
+
+ return p - buf;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include "index.h"
+#include "row.h"
+#include "column.h"
+#include "table.h"
+
+#include "transaction.h"
+
+#define INDEX_HASH_CREATE(t) MDB_HASH_TABLE_CREATE(t,100)
+#define INDEX_SEQUENCE_CREATE(t) MDB_SEQUENCE_TABLE_CREATE(t,16)
+
+#define INDEX_HASH_DROP(ix) mdb_hash_table_destroy(ix->hash)
+#define INDEX_SEQUENCE_DROP(ix) mdb_sequence_table_destroy(ix->sequence)
+
+#define INDEX_HASH_RESET(ix) mdb_hash_table_reset(ix->hash)
+#define INDEX_SEQUENCE_RESET(ix) mdb_sequence_table_reset(ix->sequence)
+
+
+
+int mdb_index_create(mdb_table_t *tbl, char **index_columns)
+{
+ mdb_index_t *ix;
+ mdb_column_t *col;
+ mqi_data_type_t type;
+ int beg, end;
+ int *idxcols;
+ int i,j, idx;
+
+ MDB_CHECKARG(tbl && index_columns && index_columns[0], -1);
+
+ ix = &tbl->index;
+
+ beg = end = 0;
+ type = mqi_unknown;
+ idxcols = NULL;
+
+ for (i = 0; index_columns[i]; i++) {
+ if (!(idx = mdb_hash_get_data(tbl->chash,0,index_columns[i]) - NULL)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ col = tbl->columns + --idx;
+ col->flags |= MQI_COLUMN_KEY;
+
+ if (i == 0) {
+ type = col->type;
+ beg = col->offset;
+ end = beg + col->length;
+ }
+ else {
+ type = mqi_blob;
+
+ if (col->offset == end)
+ end += col->length;
+ else if (col->offset == beg - col->length)
+ beg = col->offset;
+ else {
+ type = mqi_unknown;
+ break; /* not an adjacent column */
+ }
+ }
+
+ if (!(idxcols = realloc(idxcols, sizeof(int) * (i+1)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ for (j = 0; j < i; j++) {
+ if (idx == idxcols[j])
+ break;
+
+ if (idx < idxcols[j]) {
+ memmove(idxcols + j+1, idxcols + j, sizeof(*idxcols) * (i-j));
+ break;
+ }
+ }
+
+ idxcols[j] = idx;
+ }
+
+ if (type == mqi_unknown || beg < 0 || end <= beg ||
+ end - beg > MDB_INDEX_LENGTH_MAX)
+ {
+ free(idxcols);
+ errno = EIO;
+ return -1;
+ }
+
+ ix->type = type;
+ ix->length = end - beg;
+ ix->offset = beg;
+ ix->ncolumn = i;
+ ix->columns = idxcols;
+
+ switch (type) {
+ case mqi_varchar:
+ ix->hash = INDEX_HASH_CREATE(varchar);
+ ix->sequence = INDEX_SEQUENCE_CREATE(varchar);
+ break;
+ case mqi_integer:
+ ix->hash = INDEX_HASH_CREATE(integer);
+ ix->sequence = INDEX_SEQUENCE_CREATE(integer);
+ break;
+ case mqi_unsignd:
+ ix->hash = INDEX_HASH_CREATE(unsignd);
+ ix->sequence = INDEX_SEQUENCE_CREATE(unsignd);
+ break;
+ case mqi_blob:
+ ix->hash = INDEX_HASH_CREATE(blob);
+ ix->sequence = INDEX_SEQUENCE_CREATE(blob);
+ break;
+ default:
+ free(idxcols);
+ memset(ix, 0, sizeof(*ix));
+ break;
+ }
+
+ return 0;
+}
+
+void mdb_index_drop(mdb_table_t *tbl)
+{
+ mdb_index_t *ix;
+
+ MDB_CHECKARG(tbl,);
+
+ ix = &tbl->index;
+
+ if (MDB_INDEX_DEFINED(ix)) {
+ INDEX_HASH_DROP(ix);
+ INDEX_SEQUENCE_DROP(ix);
+
+ free(ix->columns);
+
+ memset(ix, 0, sizeof(*ix));
+
+ ix->type = mqi_unknown;
+ }
+}
+
+void mdb_index_reset(mdb_table_t *tbl)
+{
+ mdb_index_t *ix;
+
+ MDB_CHECKARG(tbl,);
+
+ ix = &tbl->index;
+
+ if (MDB_INDEX_DEFINED(ix)) {
+ INDEX_HASH_RESET(ix);
+ INDEX_SEQUENCE_RESET(ix);
+ }
+}
+
+
+int mdb_index_insert(mdb_table_t *tbl,
+ mdb_row_t *row,
+ mqi_bitfld_t cmask,
+ int ignore)
+{
+ mdb_index_t *ix;
+ int lgh;
+ void *key;
+ mdb_hash_t *hash;
+ mdb_sequence_t *seq;
+ mdb_row_t *old;
+ uint32_t txdepth;
+
+ MDB_CHECKARG(tbl && row, -1);
+
+ ix = &tbl->index;
+
+ if (!MDB_INDEX_DEFINED(ix))
+ return 1; /* fake a sucessful insertion */
+
+ hash = ix->hash;
+ seq = ix->sequence;
+ lgh = ix->length;
+ key = (void *)row->data + ix->offset;
+
+ if (mdb_hash_add(hash, lgh,key, row) == 0) {
+ mdb_sequence_add(seq, lgh,key, row);
+ return 1;
+ }
+
+ /*
+ * we have a duplicate at hand
+ */
+
+ if (ignore) { /* replace the duplicate with the new row */
+
+ /* TODO: move the transaction & log related stuff to table,
+ ie. here deal with indexes only */
+ if ((txdepth = mdb_transaction_get_depth()) < 1) {
+ errno = EIO;
+ return -1;
+ }
+
+ if (!(old = mdb_hash_delete(hash, lgh,key)) ||
+ (old != mdb_sequence_delete(seq, lgh,key)))
+ {
+ /* something is really broken: get out quickly */
+ errno = EIO;
+ return -1;
+ }
+ else {
+ if (mdb_row_delete(tbl, old, 0,0) < 0 ||
+ mdb_log_change(tbl, txdepth, mdb_log_update,cmask,old,row) < 0)
+ {
+ /* errno is either EEXIST or ENOMEM set by mdb_hash_add */
+ return -1;
+ }
+
+ mdb_hash_add(hash, lgh,key, row);
+ mdb_sequence_add(seq, lgh,key, row);
+ }
+ }
+ else { /* duplicate insertion is an error. keep the original row */
+ mdb_row_delete(tbl, row, 0, 1);
+ /* errno is either EEXIST or ENOMEM set by mdb_hash_add */
+ return -1;
+ }
+
+ return 0;
+}
+
+int mdb_index_delete(mdb_table_t *tbl, mdb_row_t *row)
+{
+ mdb_index_t *ix;
+ int lgh;
+ void *key;
+ mdb_hash_t *hash;
+ mdb_sequence_t *seq;
+
+ MDB_CHECKARG(tbl && row, -1);
+
+ ix = &tbl->index;
+
+ if (!MDB_INDEX_DEFINED(ix))
+ return 0;
+
+ hash = ix->hash;
+ seq = ix->sequence;
+ lgh = ix->length;
+ key = (void *)row->data + ix->offset;
+
+ if (mdb_hash_delete(hash, lgh,key) != row ||
+ mdb_sequence_delete(seq, lgh,key) != row)
+ {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+mdb_row_t *mdb_index_get_row(mdb_table_t *tbl, int idxlen, void *idxval)
+{
+ mdb_index_t *ix;
+
+ MDB_CHECKARG(tbl && idxlen >= 0 && idxval, NULL);
+
+ ix = &tbl->index;
+
+ return mdb_hash_get_data(ix->hash, idxlen, idxval);
+}
+
+int mdb_index_print(mdb_table_t *tbl, char *buf, int len)
+{
+#define PRINT(args...) if (e > p) p += snprintf(p, e-p, args)
+ mdb_index_t *ix;
+ const char *sep;
+ char *p, *e;
+ int i;
+
+ MDB_CHECKARG(tbl && buf && len > 0, 0);
+
+ ix = &tbl->index;
+
+ MDB_PREREQUISITE(MDB_INDEX_DEFINED(ix), 0);
+
+ e = (p = buf) + len;
+
+ PRINT("index columns: ");
+
+ for (i = 0, sep = ""; i < ix->ncolumn; i++, sep = ",")
+ PRINT("%s%02d", sep, ix->columns[i]);
+
+ PRINT("\n type offset length\n ---------------------"
+ "\n %-7s %4d %4d\n",
+ mqi_data_type_str(ix->type), ix->offset, ix->length);
+
+ return p - buf;
+
+#undef PRINT
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_INDEX_H__
+#define __MDB_INDEX_H__
+
+
+#include <murphy-db/mqi-types.h>
+#include <murphy-db/hash.h>
+#include <murphy-db/sequence.h>
+#include <murphy-db/mdb.h>
+
+#include "row.h"
+
+#define MDB_INDEX_LENGTH_MAX 8192
+
+#define MDB_INDEX_DEFINED(ix) ((ix)->type != mqi_unknown)
+
+typedef struct {
+ mqi_data_type_t type;
+ int length;
+ int offset;
+ mdb_hash_t *hash;
+ mdb_sequence_t *sequence;
+ int ncolumn;
+ int *columns; /* sorted */
+} mdb_index_t;
+
+
+int mdb_index_create(mdb_table_t *, char **);
+void mdb_index_drop(mdb_table_t *);
+void mdb_index_reset(mdb_table_t *);
+int mdb_index_insert(mdb_table_t *, mdb_row_t *, mqi_bitfld_t, int);
+int mdb_index_delete(mdb_table_t *, mdb_row_t *);
+mdb_row_t *mdb_index_get_row(mdb_table_t *, int, void *);
+int mdb_index_print(mdb_table_t *, char *, int);
+
+
+#endif /* __MDB_INDEX_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/hash.h>
+#include <murphy-db/sequence.h>
+#include "log.h"
+#include "row.h"
+#include "table.h"
+
+#ifndef LOG_STATISTICS
+#define LOG_STATISTICS
+#endif
+
+#define LOG_COMMON_FIELDS \
+ mdb_dlist_t vlink; \
+ mdb_dlist_t hlink; \
+ uint32_t depth
+
+typedef struct {
+ LOG_COMMON_FIELDS;
+} log_t;
+
+typedef struct {
+ LOG_COMMON_FIELDS;
+} tx_log_t;
+
+typedef struct {
+ LOG_COMMON_FIELDS;
+ mdb_table_t *table;
+ mdb_dlist_t changes;
+} tbl_log_t;
+
+typedef struct {
+ mdb_dlist_t link;
+ mdb_log_type_t type;
+ mqi_bitfld_t colmask;
+ union {
+ mdb_row_t *before;
+ mdb_opcnt_t *cnt;
+ };
+ mdb_row_t *after;
+} change_t;
+
+
+
+static inline log_t *new_log(mdb_dlist_t *, mdb_dlist_t *, uint32_t, int);
+static inline void delete_log(log_t *);
+static inline log_t *get_last_vlog(mdb_dlist_t *);
+static tx_log_t *get_tx_log(uint32_t);
+static tbl_log_t *get_tbl_log(mdb_dlist_t *, mdb_dlist_t *, uint32_t,
+ mdb_table_t *);
+static void delete_tx_log(uint32_t);
+
+static MDB_DLIST_HEAD(tx_head);
+
+int mdb_log_create(mdb_table_t *tbl)
+{
+ MDB_CHECKARG(tbl, -1);
+
+ MDB_DLIST_INIT(tbl->logs);
+
+ return 0;
+}
+
+
+int mdb_log_change(mdb_table_t *tbl,
+ uint32_t depth,
+ mdb_log_type_t type,
+ mqi_bitfld_t colmask,
+ mdb_row_t *before,
+ mdb_row_t *after)
+{
+ tx_log_t *txlog;
+ tbl_log_t *tblog;
+ change_t *change;
+
+ MDB_CHECKARG(tbl, -1);
+
+ if (!depth)
+ return 0;
+
+ if (!(txlog = get_tx_log(depth)) ||
+ !(tblog = get_tbl_log(&tbl->logs, &txlog->hlink, depth, tbl)))
+ {
+ return -1;
+ }
+
+ if (!(change = calloc(1, sizeof(change_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ change->type = type;
+ change->colmask = colmask;
+ change->before = before;
+ change->after = after;
+
+ switch (type) {
+ case mdb_log_insert: tbl->cnt.inserts++; break;
+ case mdb_log_delete: tbl->cnt.deletes++; break;
+ case mdb_log_update: tbl->cnt.updates++; break;
+ default: break;
+ }
+
+ MDB_DLIST_PREPEND(change_t, link, change, &tblog->changes);
+
+ return 0;
+}
+
+mdb_log_entry_t *mdb_log_transaction_iterate(uint32_t depth,
+ void **cursor_ptr,
+ bool forward,
+ int delete)
+{
+ typedef struct {
+ uint32_t depth;
+ mdb_dlist_t *hhead;
+ mdb_dlist_t *chead;
+ mdb_dlist_t *hlink;
+ mdb_dlist_t *clink;
+ mdb_log_entry_t entry;
+ } cursor_t;
+
+ static cursor_t empty_cursor;
+
+ cursor_t *cursor;
+ tx_log_t *txlog;
+ tbl_log_t *tblog;
+ mdb_dlist_t *hhead;
+ mdb_dlist_t *chead;
+ change_t *change;
+ mdb_log_entry_t *entry;
+
+ MDB_CHECKARG(cursor_ptr, NULL);
+
+ if (!depth)
+ return NULL;
+
+ if ((cursor = *cursor_ptr)) {
+ if (cursor == &empty_cursor)
+ return NULL;
+
+ entry = &cursor->entry;
+ }
+ else {
+ if (!(txlog = (tx_log_t *)get_last_vlog(&tx_head)))
+ return NULL;
+
+ if (depth > txlog->depth)
+ return NULL;
+
+ hhead = &txlog->hlink;
+
+ if (MDB_DLIST_EMPTY(*hhead)) {
+ if (delete)
+ delete_log((log_t *)txlog);
+ return NULL;
+ }
+
+ tblog = MDB_LIST_RELOCATE(tbl_log_t, hlink, hhead->next);
+
+ if (MDB_DLIST_EMPTY(tblog->changes))
+ return NULL;
+
+ chead = &tblog->changes;
+
+ if (!(*cursor_ptr = cursor = calloc(1, sizeof(cursor_t))))
+ return NULL;
+ else {
+ entry = &cursor->entry;
+
+ cursor->depth = txlog->depth;
+ cursor->hhead = hhead;
+ cursor->chead = chead;
+ cursor->hlink = tblog->hlink.next;
+ cursor->clink = forward ? chead->next : chead->prev;
+
+ entry->table = tblog->table;
+ }
+ }
+
+ for (;;) {
+ if (cursor->clink == cursor->chead) {
+ if (delete) {
+ tblog = MDB_LIST_RELOCATE(tbl_log_t, changes, cursor->chead);
+ delete_log((log_t *)tblog);
+ }
+ }
+ else {
+ change = MDB_LIST_RELOCATE(change_t, link, cursor->clink);
+
+ cursor->clink = forward ? change->link.next : change->link.prev;
+
+ entry->change = change->type;
+ entry->colmask = change->colmask;
+ entry->before = change->before;
+ entry->after = change->after;
+
+ if (delete) {
+ MDB_DLIST_UNLINK(change_t, link, change);
+ free(change);
+ }
+
+ return entry;
+ }
+
+ if (cursor->hlink == cursor->hhead) {
+ if (cursor != &empty_cursor) {
+ if (delete)
+ delete_tx_log(cursor->depth);
+ *cursor_ptr = &empty_cursor;
+ free(cursor);
+ }
+ return NULL;
+ }
+ else {
+ tblog = MDB_LIST_RELOCATE(tbl_log_t, hlink, cursor->hlink);
+ chead = &tblog->changes;
+
+ cursor->hlink = tblog->hlink.next;
+ cursor->chead = chead;
+ cursor->clink = forward ? chead->next : chead->prev;
+
+ entry->table = tblog->table;
+ }
+ }
+}
+
+
+
+mdb_log_entry_t *mdb_log_table_iterate(mdb_table_t *tbl,
+ void **cursor_ptr,
+ int delete)
+{
+ typedef struct {
+ mdb_dlist_t *vhead;
+ mdb_dlist_t *chead;
+ mdb_dlist_t *vlink;
+ mdb_dlist_t *clink;
+ mdb_log_entry_t entry;
+ } cursor_t;
+
+ static cursor_t empty_cursor;
+
+ cursor_t *cursor;
+ tbl_log_t *tblog;
+ mdb_dlist_t *vhead;
+ mdb_dlist_t *chead;
+ change_t *change;
+ mdb_log_entry_t *entry;
+
+ MDB_CHECKARG(tbl && cursor_ptr, NULL);
+
+ if ((cursor = *cursor_ptr))
+ entry = &cursor->entry;
+ else {
+ vhead = &tbl->logs;
+
+ if (MDB_DLIST_EMPTY(*vhead))
+ return NULL;
+
+ if (!(tblog = (tbl_log_t *)get_last_vlog(vhead)))
+ return NULL;
+
+ if (tblog->table != tbl || MDB_DLIST_EMPTY(tblog->changes))
+ return NULL;
+
+ chead = &tblog->changes;
+
+ if (!(*cursor_ptr = cursor = calloc(1, sizeof(cursor_t))))
+ return NULL;
+ else {
+ entry = &cursor->entry;
+
+ cursor->vhead = vhead;
+ cursor->chead = chead;
+ cursor->vlink = tblog->vlink.prev;
+ cursor->clink = chead->next;
+
+ entry->table = tblog->table;
+ }
+ }
+
+ for (;;) {
+ if (cursor->clink == cursor->chead) {
+ if (delete) {
+ tblog = MDB_LIST_RELOCATE(tbl_log_t, changes, cursor->chead);
+ delete_log((log_t *)tblog);
+ }
+ }
+ else {
+ change = MDB_LIST_RELOCATE(change_t, link, cursor->clink);
+
+ cursor->clink = change->link.next;
+
+ entry->change = change->type;
+ entry->colmask = change->colmask;
+ entry->before = change->before;
+ entry->after = change->after;
+
+ if (delete) {
+ MDB_DLIST_UNLINK(change_t, link, change);
+ free(change);
+ }
+
+ return entry;
+ }
+
+ if (cursor->vlink == cursor->vhead) {
+ if (cursor != &empty_cursor) {
+ *cursor_ptr = &empty_cursor;
+ free(cursor);
+ }
+ return NULL;
+ }
+ else {
+ tblog = MDB_LIST_RELOCATE(tbl_log_t, vlink, cursor->vlink);
+ chead = &tblog->changes;
+
+ cursor->vlink = tblog->vlink.prev;
+ cursor->chead = chead;
+ cursor->clink = chead->next;
+
+ if (tbl != tblog->table)
+ return NULL;
+ }
+ }
+}
+
+
+
+static inline log_t *new_log(mdb_dlist_t *vhead,
+ mdb_dlist_t *hhead,
+ uint32_t depth,
+ int size)
+{
+ log_t *log;
+
+ if ((log = calloc(1, size))) {
+ MDB_DLIST_APPEND(mdb_log_t, vlink, log, vhead);
+
+ if (hhead)
+ MDB_DLIST_APPEND(mdb_log_t, hlink, log, hhead);
+ else
+ MDB_DLIST_INIT(log->hlink);
+
+ log->depth = depth;
+ }
+
+ return log;
+}
+
+static inline void delete_log(log_t *log)
+{
+ MDB_DLIST_UNLINK(log_t, vlink, log);
+ MDB_DLIST_UNLINK(log_t, hlink, log);
+
+ free(log);
+}
+
+
+static inline log_t *get_last_vlog(mdb_dlist_t *vhead)
+{
+ if (MDB_DLIST_EMPTY(*vhead))
+ return NULL;
+
+ return MDB_LIST_RELOCATE(log_t, vlink, vhead->prev);
+}
+
+
+static tx_log_t *get_tx_log(uint32_t depth)
+{
+ tx_log_t *log;
+
+ if (!(log = (tx_log_t *)get_last_vlog(&tx_head)) || depth > log->depth) {
+ return (tx_log_t *)new_log(&tx_head, NULL, depth, sizeof(*log));
+ }
+
+ if (depth < log->depth) {
+ errno = ENOKEY;
+ return NULL;
+ }
+
+ return log;
+}
+
+static tbl_log_t *get_tbl_log(mdb_dlist_t *vhead,
+ mdb_dlist_t *hhead,
+ uint32_t depth,
+ mdb_table_t *tbl)
+{
+ tbl_log_t *log;
+ change_t *change;
+
+ if (!(log = (tbl_log_t *)get_last_vlog(vhead)) || depth > log->depth) {
+ if ((log = (tbl_log_t *)new_log(vhead, hhead, depth, sizeof(*log)))) {
+ log->table = tbl;
+ MDB_DLIST_INIT(log->changes);
+
+ if (!(change = calloc(1, sizeof(change_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (!(change->cnt = calloc(1, sizeof(*change->cnt)))) {
+ free(change);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ change->type = mdb_log_start;
+ *change->cnt = tbl->cnt;
+ tbl->cnt.stamp++;
+
+ MDB_DLIST_PREPEND(change_t, link, change, &log->changes);
+ }
+ }
+
+ if (!log) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (tbl != log->table) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (depth < log->depth) {
+ errno = ENOKEY;
+ return NULL;
+ }
+
+ return log;
+}
+
+static void delete_tx_log(uint32_t depth)
+{
+ log_t *log;
+
+ if ((log = get_last_vlog(&tx_head)) && depth == log->depth)
+ delete_log(log);
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_LOG_H__
+#define __MDB_LOG_H__
+
+#include <murphy-db/list.h>
+#include <murphy-db/mdb.h>
+#include "row.h"
+
+typedef struct {
+ uint32_t stamp;
+ uint32_t inserts;
+ uint32_t deletes;
+ uint32_t updates;
+} mdb_opcnt_t;
+
+#define MDB_FORWARD true
+#define MDB_BACKWARD false
+
+#define MDB_TRANSACTION_LOG_FOR_EACH(depth, entry, fw, curs) \
+ for (curs = NULL; (entry = mdb_log_transaction_iterate(depth,&curs,fw,0));)
+
+#define MDB_TRANSACTION_LOG_FOR_EACH_DELETE(depth, entry, fw, curs) \
+ for (curs = NULL; (entry = mdb_log_transaction_iterate(depth,&curs,fw,1));)
+
+#define MDB_TABLE_LOG_FOR_EACH(table, entry, curs) \
+ for (curs = NULL; (entry = mdb_log_table_iterate(table, &curs, 0));)
+
+#define MDB_TABLE_LOG_FOR_EACH_DELETE(table, entry, curs) \
+ for (curs = NULL; (entry = mdb_log_table_iterate(table, &curs, 1));)
+
+typedef enum {
+ mdb_log_unknown = 0,
+ mdb_log_insert,
+ mdb_log_delete,
+ mdb_log_update,
+ mdb_log_start
+} mdb_log_type_t;
+
+typedef struct {
+ mdb_table_t *table;
+ mdb_log_type_t change;
+ mqi_bitfld_t colmask;
+ union {
+ mdb_row_t *before;
+ mdb_opcnt_t *cnt;
+ };
+ mdb_row_t *after;
+} mdb_log_entry_t;
+
+
+int mdb_log_create(mdb_table_t *);
+int mdb_log_change(mdb_table_t *, uint32_t, mdb_log_type_t,
+ mqi_bitfld_t, mdb_row_t *, mdb_row_t *);
+mdb_log_entry_t *mdb_log_transaction_iterate(uint32_t, void **, bool, int);
+mdb_log_entry_t *mdb_log_table_iterate(mdb_table_t *, void **, int);
+
+
+#endif /* __MDB_LOG_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <murphy-db/mqi-types.h>
+
+const char *mqi_data_type_str(mqi_data_type_t type)
+{
+ switch (type) {
+ case mqi_integer: return "integer";
+ case mqi_unsignd: return "unsigned";
+ case mqi_varchar: return "varchar";
+ case mqi_floating: return "floating";
+ case mqi_blob: return "blob";
+ default: return "unknown";
+ }
+}
+
+int mqi_data_compare_integer(int datalen, void *data1, void *data2)
+{
+ int32_t integer1;
+ int32_t integer2;
+
+ if (datalen != sizeof(int32_t) || !data1 || !data2)
+ return 0;
+
+ integer1 = *(int32_t *)data1;
+ integer2 = *(int32_t *)data2;
+
+
+ return (integer1 - integer2);
+}
+
+int mqi_data_compare_unsignd(int datalen, void *data1, void *data2)
+{
+ uint32_t unsigned1;
+ uint32_t unsigned2;
+
+ if (datalen != sizeof(uint32_t) || !data1 || !data2)
+ return 0;
+
+ unsigned1 = *(uint32_t *)data1;
+ unsigned2 = *(uint32_t *)data2;
+
+ if (unsigned1 < unsigned2)
+ return -1;
+
+ if (unsigned1 > unsigned2)
+ return 1;
+
+ return 0;
+}
+
+int mqi_data_compare_string(int datalen, void *data1, void *data2)
+{
+ const char *varchar1 = (const char *)data1;
+ const char *varchar2 = (const char *)data2;
+
+ (void)datalen;
+
+ if (!varchar1 || !varchar1[0] || !varchar2 || !varchar2[0])
+ return 0;
+
+ return strcmp(varchar1, varchar2);
+}
+
+int mqi_data_compare_pointer(int datalen, void *data1, void *data2)
+{
+ (void)datalen;
+
+ return ((char *)data1 - (char *)data2);
+}
+
+int mqi_data_compare_varchar(int datalen, void *data1, void *data2)
+{
+ return mqi_data_compare_string(datalen, data1, data2);
+}
+
+int mqi_data_compare_blob(int datalen, void *data1, void *data2)
+{
+ if (!datalen || !data1 || !data2)
+ return 0;
+
+ return memcmp(data1, data2, datalen);
+}
+
+int mqi_data_print_integer(void *data, char *buf, int len)
+{
+ int32_t integer = *(int32_t *)data;
+
+ return snprintf(buf, len, "%d", integer);
+}
+
+int mqi_data_print_unsignd(void *data, char *buf, int len)
+{
+ uint32_t unsignd = *(uint32_t *)data;
+
+ return snprintf(buf, len, "%u", unsignd);
+}
+
+int mqi_data_print_string(void *data, char *buf, int len)
+{
+ const char *varchar = (const char *)data;
+
+ return snprintf(buf, len, "%s", varchar);
+}
+
+int mqi_data_print_pointer(void *data, char *buf, int len)
+{
+ return snprintf(buf, len, "%p", data);
+}
+
+int mqi_data_print_varchar(void *data, char *buf, int len)
+{
+ const char *varchar = (const char *)data;
+
+ return snprintf(buf, len, "%s", varchar);
+}
+
+int mqi_data_print_blob(void *data, char *buf, int len)
+{
+ MQI_UNUSED(data);
+ MQI_UNUSED(buf);
+ MQI_UNUSED(len);
+
+ return 0;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include "row.h"
+#include "table.h"
+#include "index.h"
+#include "column.h"
+
+
+
+mdb_row_t *mdb_row_create(mdb_table_t *tbl)
+{
+ mdb_row_t *row;
+
+ MDB_CHECKARG(tbl, NULL);
+
+ if (!(row = calloc(1, sizeof(mdb_row_t) + tbl->dlgh))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ MDB_DLIST_APPEND(mdb_row_t, link, row, &tbl->rows);
+
+ return row;
+}
+
+mdb_row_t *mdb_row_duplicate(mdb_table_t *tbl, mdb_row_t *row)
+{
+ mdb_row_t *dup;
+
+ MDB_CHECKARG(tbl && row, NULL);
+
+ if (!(dup = calloc(1, sizeof(mdb_row_t) + tbl->dlgh))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ MDB_DLIST_INIT(dup->link);
+ memcpy(dup->data, row->data, tbl->dlgh);
+
+ return dup;
+}
+
+int mdb_row_delete(mdb_table_t *tbl,
+ mdb_row_t *row,
+ int index_update,
+ int free_it)
+{
+ int sts = 0;
+
+ (void)tbl;
+
+ MDB_CHECKARG(row, -1);
+
+ if (index_update && mdb_index_delete(tbl, row) < 0)
+ sts = -1;
+
+ if (!MDB_DLIST_EMPTY(row->link))
+ MDB_DLIST_UNLINK(mdb_row_t, link, row);
+
+ if (free_it)
+ free(row);
+ else
+ MDB_DLIST_INIT(row->link);
+
+ return sts;
+}
+
+int mdb_row_update(mdb_table_t *tbl,
+ mdb_row_t *row,
+ mqi_column_desc_t *cds,
+ void *data,
+ int index_update,
+ mqi_bitfld_t *cmask_ret)
+{
+ mdb_column_t *columns;
+ mqi_column_desc_t *source_dsc;
+ int cidx, cmod;
+ mqi_bitfld_t cmask;
+ int i;
+
+ MDB_CHECKARG(tbl && row && cds && data, -1);
+
+ columns = tbl->columns;
+
+ if (index_update)
+ mdb_index_delete(tbl, row);
+
+ cmod = 0;
+ for (cmask = i = 0; (cidx = (source_dsc = cds + i)->cindex) >= 0; i++) {
+ cmask |= (((mqi_bitfld_t)1) << cidx);
+ cmod |= mdb_column_write(columns + cidx, row->data, source_dsc, data);
+ }
+
+ if (index_update) {
+ if (mdb_index_insert(tbl, row, cmask, 0) < 0) {
+ if (cmask_ret)
+ *cmask_ret = 0;
+ return -1;
+ }
+ }
+
+ if (cmask_ret)
+ *cmask_ret = cmask;
+
+ if (!cmod)
+ return 0;
+ else
+ return 1;
+}
+
+int mdb_row_copy_over(mdb_table_t *tbl, mdb_row_t *dst, mdb_row_t *src)
+{
+ MDB_CHECKARG(tbl && dst && src, -1);
+
+ if (mdb_index_delete(tbl, dst) < 0)
+ return -1;
+
+ memcpy(dst->data, src->data, tbl->dlgh);
+
+ if (mdb_index_insert(tbl, dst, 0, 0) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_ROW_H__
+#define __MDB_ROW_H__
+
+#include <murphy-db/mqi-types.h>
+#include <murphy-db/list.h>
+#include <murphy-db/mdb.h>
+
+typedef struct mdb_row_s mdb_row_t;
+
+struct mdb_row_s {
+ mdb_dlist_t link;
+ uint8_t data[0];
+};
+
+mdb_row_t *mdb_row_create(mdb_table_t *);
+mdb_row_t *mdb_row_duplicate(mdb_table_t *, mdb_row_t *);
+int mdb_row_delete(mdb_table_t *, mdb_row_t *, int, int);
+int mdb_row_update(mdb_table_t *, mdb_row_t *, mqi_column_desc_t *,
+ void *, int, mqi_bitfld_t *);
+int mdb_row_copy_over(mdb_table_t *, mdb_row_t *, mdb_row_t *);
+
+#endif /* __MDB_ROW_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+
+#ifndef SEQUENCE_STATISTICS
+#define SEQUENCE_STATISTICS
+#endif
+
+#include <murphy-db/assert.h>
+#include <murphy-db/sequence.h>
+
+typedef struct mdb_sequence_entry_s {
+ void *key;
+ void *data;
+} sequence_entry_t;
+
+
+struct mdb_sequence_s {
+ int alloc;
+ mdb_sequence_compare_t scomp;
+ mdb_sequence_print_t sprint;
+#ifdef SEQUENCE_STATISTICS
+ int max_entry;
+#endif
+ int size;
+ int nentry;
+ sequence_entry_t *entries;
+};
+
+
+
+mdb_sequence_t *mdb_sequence_table_create(int alloc,
+ mdb_sequence_compare_t scomp,
+ mdb_sequence_print_t sprint)
+{
+ mdb_sequence_t *seq;
+
+ MDB_CHECKARG(scomp && sprint && alloc > 0 && alloc < 65536, NULL);
+
+ if (!(seq = calloc(1, sizeof(mdb_sequence_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ seq->alloc = alloc;
+ seq->scomp = scomp;
+ seq->sprint = sprint;
+
+ return seq;
+}
+
+int mdb_sequence_table_destroy(mdb_sequence_t *seq)
+{
+ MDB_CHECKARG(seq, -1);
+
+ free(seq->entries);
+ free(seq);
+
+ return 0;
+}
+
+int mdb_sequence_table_get_size(mdb_sequence_t *seq)
+{
+ MDB_CHECKARG(seq, -1);
+
+ return seq->nentry;
+}
+
+int mdb_sequence_table_reset(mdb_sequence_t *seq)
+{
+ MDB_CHECKARG(seq, -1);
+
+ free(seq->entries);
+
+ seq->size = 0;
+ seq->nentry = 0;
+ seq->entries = NULL;
+
+ return 0;
+}
+
+int mdb_sequence_table_print(mdb_sequence_t *seq, char *buf, int len)
+{
+ sequence_entry_t *entry;
+ char *p, *e;
+ int i;
+ char key[256];
+
+ MDB_CHECKARG(seq && buf && len > 0, 0);
+
+ e = (p = buf) + len;
+ *buf = '\0';
+
+ for (i = 0; i < seq->nentry && p < e; i++) {
+ entry = seq->entries + i;
+
+ seq->sprint(entry->key, key, sizeof(key));
+
+ p += snprintf(p, e-p, " %05d: '%s' / %p\n", i, key, entry->data);
+ }
+
+ return p - buf;
+}
+
+int mdb_sequence_add(mdb_sequence_t *seq, int klen, void *key, void *data)
+{
+ sequence_entry_t *entry;
+ int nentry;
+ size_t old_size;
+ size_t length;
+ int cmp;
+ int min, max, i;
+
+ MDB_CHECKARG(seq && key && data, -1);
+
+ nentry = seq->nentry++;
+
+ if ((nentry + 1) > seq->size) {
+ old_size = seq->size;
+ seq->size += seq->alloc;
+ length = sizeof(sequence_entry_t) * seq->size;
+
+ if (!(seq->entries = realloc(seq->entries, length))) {
+ seq->nentry = 0;
+ errno = ENOMEM;
+ return 0;
+ }
+
+
+ memset(seq->entries + old_size, 0,
+ length - (old_size * sizeof(sequence_entry_t)));
+ }
+
+ for (min = 0, i = (max = nentry)/2; ; i = (min+max)/2) {
+ if (!(cmp = seq->scomp(klen, key, seq->entries[i].key)))
+ break;
+
+ if (i == min) {
+ if (cmp > 0)
+ i = max;
+ break;
+ }
+
+ if (cmp < 0)
+ max = i;
+ else
+ min = i;
+ }
+
+ entry = seq->entries + i;
+
+ if (i < nentry) {
+ memmove(entry+1, entry, sizeof(sequence_entry_t) * (nentry - i));
+ }
+
+ entry->key = key;
+ entry->data = data;
+
+#ifdef SEQUENCE_STATISTICS
+ if (seq->nentry > seq->max_entry)
+ seq->max_entry = seq->nentry;
+#endif
+
+ return 0;
+}
+
+void *mdb_sequence_delete(mdb_sequence_t *seq, int klen, void *key)
+{
+ sequence_entry_t *entry;
+ int i;
+ int min, max;
+ int cmp;
+ int found;
+ void *data;
+ size_t length;
+
+ MDB_CHECKARG(seq && key, NULL);
+
+ for (found = 0, min = 0, i = (max = seq->nentry)/2; ; i = (min+max)/2) {
+ entry = seq->entries + i;
+
+ if (!(cmp = seq->scomp(klen, key, entry->key))) {
+ found = 1;
+ break;
+ }
+
+ if (i == min) {
+ if (i != max) {
+ entry = seq->entries + max;
+ if (!seq->scomp(klen, key, entry->key))
+ found = 1;
+ }
+ break;
+ }
+
+ if (cmp < 0)
+ max = i;
+ else
+ min = i;
+ }
+
+ if (!found) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ data = entry->data;
+
+ if (--seq->nentry <= 0) {
+ free(seq->entries);
+
+ seq->size = 0;
+ seq->nentry = 0;
+ seq->entries = NULL;
+ }
+ else {
+ if (i < seq->nentry) {
+ length = sizeof(sequence_entry_t) * (seq->nentry - i);
+ memmove(entry, entry+1, length);
+ }
+
+ if (seq->nentry <= (seq->size - seq->alloc)) {
+ length = sizeof(sequence_entry_t) * (seq->size -= seq->alloc);
+
+ if (!(seq->entries = realloc(seq->entries, length))) {
+ seq->nentry = 0;
+ errno = ENOMEM;
+ }
+ }
+ }
+
+ return data;
+}
+
+
+
+void *mdb_sequence_iterate(mdb_sequence_t *seq, void **cursor_ptr)
+{
+ typedef struct {
+ int index;
+ int nentry;
+ void *entries[];
+ } cursor_t;
+
+ static cursor_t empty_cursor;
+
+ size_t length;
+ cursor_t *cursor;
+ int i;
+
+ MDB_CHECKARG(seq && cursor_ptr, NULL);
+
+ if (!(cursor = *cursor_ptr)) {
+ length = sizeof(cursor_t) + sizeof(void *) * seq->nentry;
+
+ if (!(cursor = malloc(length)))
+ return NULL;
+
+ cursor->index = 0;
+ cursor->nentry = seq->nentry;
+
+ for (i = 0; i < seq->nentry; i++)
+ cursor->entries[i] = seq->entries[i].data;
+
+ *cursor_ptr = cursor;
+ }
+
+ if (cursor->index >= cursor->nentry) {
+ if (*cursor_ptr != &empty_cursor) {
+ *cursor_ptr = &empty_cursor;
+ free(cursor);
+ }
+ return NULL;
+ }
+
+ return (void *)cursor->entries[cursor->index++];
+}
+
+
+void mdb_sequence_cursor_destroy(mdb_sequence_t *seq, void **cursor)
+{
+ (void)seq;
+
+ if (cursor)
+ free(*cursor);
+}
+
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/handle.h>
+#include <murphy-db/sequence.h>
+#include "table.h"
+#include "row.h"
+#include "table.h"
+#include "cond.h"
+#include "transaction.h"
+
+#define TABLE_STATISTICS
+
+
+typedef struct {
+ int indexed;
+ void *cursor;
+} table_iterator_t;
+
+
+static mdb_hash_t *table_hash;
+static int table_count;
+
+static void destroy_table(mdb_table_t *);
+static mdb_row_t *table_iterator(mdb_table_t *, table_iterator_t *);
+#if 0
+static int table_print_info(mdb_table_t *, char *, int);
+#endif
+static int select_conditional(mdb_table_t *, mqi_cond_entry_t *,
+ mqi_column_desc_t *,void *, int, int);
+static int select_all(mdb_table_t *, mqi_column_desc_t *, void *, int, int);
+static int select_by_index(mdb_table_t*, int,void *, mqi_column_desc_t*,void*);
+static int update_conditional(mdb_table_t *, mqi_cond_entry_t *,
+ mqi_column_desc_t *, void *, int);
+static int update_all(mdb_table_t *, mqi_column_desc_t *, void *, int);
+static int update_single_row(mdb_table_t *, mdb_row_t *, mqi_column_desc_t *,
+ void *, int);
+static int delete_conditional(mdb_table_t *, mqi_cond_entry_t *);
+static int delete_all(mdb_table_t *);
+static int delete_single_row(mdb_table_t *, mdb_row_t *, int);
+
+
+mdb_table_t *mdb_table_create(char *name,
+ char **index_columns,
+ mqi_column_def_t *cdefs)
+{
+ mdb_table_t *tbl;
+ mdb_hash_t *chash;
+ mqi_data_type_t type;
+ int length;
+ int align;
+ int ncolumn;
+ mdb_column_t *columns;
+ mdb_column_t *col;
+ mqi_column_def_t *cdef;
+ int dlgh;
+ int i;
+
+ MDB_CHECKARG(name && cdefs, NULL);
+
+ if (!table_hash && !(table_hash = MDB_HASH_TABLE_CREATE(varchar, 256))) {
+ errno = EIO;
+ return NULL;
+ }
+
+ for (ncolumn = 0; cdefs[ncolumn].name; ncolumn++) {
+ cdef = cdefs + ncolumn;
+ type = cdef->type;
+ length = cdef->length;
+
+ if (!cdef->name[0]) {
+ ncolumn = 0;
+ break;
+ }
+
+ if (type == mqi_varchar) {
+ if (length < 1 || length > MDB_COLUMN_LENGTH_MAX) {
+ ncolumn = 0;
+ break;
+ }
+ }
+ else if (type != mqi_integer &&
+ type != mqi_unsignd &&
+ type != mqi_floating )
+ {
+ ncolumn = 0;
+ break;
+ }
+ }
+
+ if (!ncolumn) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+
+ length = sizeof(mdb_table_t) + sizeof(mdb_dlist_t) * ncolumn;
+
+ if (!(tbl = calloc(1, length)) ||
+ !(columns = calloc(ncolumn, sizeof(mdb_column_t))))
+ {
+ free(tbl);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (!(chash = MDB_HASH_TABLE_CREATE(varchar, 16))) {
+ free(tbl);
+ free(columns);
+ return NULL;
+ }
+
+ for (i = 0, dlgh = 0; i < ncolumn; i++) {
+ cdef = cdefs + i;
+ col = columns + i;
+
+ switch (cdef->type) {
+ case mqi_varchar: length = cdef->length + 1; align = 1; break;
+ case mqi_integer: length = sizeof(int32_t); align = 4; break;
+ case mqi_unsignd: length = sizeof(uint32_t); align = 4; break;
+ case mqi_floating: length = sizeof(double); align = 4; break;
+ default: length = cdef->length; align = 2; break;
+ }
+
+ col->name = strdup(cdef->name);
+ col->type = cdef->type;
+ col->length = length;
+ col->offset = (dlgh + (align - 1)) & ~(align - 1);
+
+ dlgh = col->offset + col->length;
+
+ mdb_hash_add(chash, 0,col->name, NULL + (i+1));
+ }
+
+ dlgh = (dlgh + 3) & ~3;
+
+ tbl->handle = MQI_HANDLE_INVALID;
+ tbl->name = strdup(name);
+ tbl->cnt.stamp = 1;
+ tbl->chash = chash;
+ tbl->ncolumn = ncolumn;
+ tbl->columns = columns;
+ tbl->dlgh = dlgh;
+
+ MDB_DLIST_INIT(tbl->rows);
+ mdb_log_create(tbl);
+ mdb_trigger_init(&tbl->trigger, ncolumn);
+
+ if (mdb_hash_add(table_hash, 0,tbl->name, tbl) < 0) {
+ destroy_table(tbl);
+ return NULL;
+ }
+
+ if (index_columns)
+ mdb_index_create(tbl, index_columns);
+
+ table_count++;
+
+ return tbl;
+}
+
+int mdb_table_register_handle(mdb_table_t *tbl, mqi_handle_t handle)
+{
+ MDB_CHECKARG(tbl && handle != MQI_HANDLE_INVALID, -1);
+ MDB_PREREQUISITE(tbl->handle == MQI_HANDLE_INVALID, -1);
+
+ tbl->handle = handle;
+
+ mdb_trigger_table_create(tbl);
+
+ return 0;
+}
+
+int mdb_table_drop(mdb_table_t *tbl)
+{
+ MDB_CHECKARG(tbl, -1);
+
+ mdb_trigger_table_drop(tbl);
+ mdb_trigger_reset(&tbl->trigger, tbl->ncolumn);
+
+ mdb_transaction_drop_table(tbl);
+
+ mdb_hash_delete(table_hash, 0,tbl->name);
+
+ destroy_table(tbl);
+
+ if (table_count > 1)
+ table_count--;
+ else {
+ MDB_HASH_TABLE_DESTROY(table_hash);
+ table_hash = NULL;
+ table_count = 0;
+ }
+
+ return 0;
+}
+
+int mdb_table_create_index(mdb_table_t *tbl, char **index_columns)
+{
+ mdb_row_t *row, *n;
+ int error = 0;
+
+ MDB_CHECKARG(tbl && index_columns && index_columns[0], -1);
+
+ if (MDB_TABLE_HAS_INDEX(tbl)) {
+ errno = EEXIST;
+ return -1;
+ }
+
+ if (mdb_index_create(tbl, index_columns) < 0)
+ return -1;
+
+ MDB_DLIST_FOR_EACH_SAFE(mdb_row_t, link, row,n, &tbl->rows) {
+ if (mdb_index_insert(tbl, row, 0, 0) < 0) {
+ if ((error = errno) != EEXIST)
+ return -1;
+ }
+ }
+
+ if (error) {
+ errno = error;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int mdb_table_describe(mdb_table_t *tbl, mqi_column_def_t *defs, int len)
+{
+ mdb_column_t *col;
+ mqi_column_def_t *def;
+ int i,n;
+
+ MDB_CHECKARG(tbl && defs && len > 0 && len >= (n = tbl->ncolumn), -1);
+
+ for (i = 0; i < n; i++) {
+ col = tbl->columns + i;
+ def = defs + i;
+
+ def->name = col->name;
+ def->type = col->type;
+ def->length = col->length;
+ def->flags = col->flags;
+
+ if (def->type == mqi_varchar && def->length > 0)
+ def->length--;
+ }
+
+ return n;
+}
+
+int mdb_table_insert(mdb_table_t *tbl,
+ int ignore,
+ mqi_column_desc_t *cds,
+ void **data)
+{
+ uint32_t txdepth = mdb_transaction_get_depth();
+ mdb_row_t *row;
+ int error;
+ int nrow;
+ int ninsert;
+ mqi_bitfld_t cmask;
+ int i;
+
+ MDB_CHECKARG(tbl && cds && data && data[0], -1);
+
+ for (i = 0, error = 0, ninsert = 0; data[i]; i++) {
+ if (!(row = mdb_row_create(tbl))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ mdb_row_update(tbl, row, cds, data[i], 0, &cmask);
+
+ if ((nrow = mdb_index_insert(tbl, row, cmask, ignore)) < 0) {
+ if ((error = errno) != EEXIST)
+ return -1;
+
+ ninsert = -1;
+ }
+ else if (nrow > 0) {
+ tbl->nrow++;
+
+ if (mdb_log_change(tbl,txdepth,mdb_log_insert,cmask,NULL,row) < 0)
+ ninsert = -1;
+ else
+ ninsert += (ninsert >= 0) ? 1 : 0;
+ }
+
+ }
+
+ if (error) {
+ errno = error;
+ return -1;
+ }
+
+ return ninsert;
+}
+
+int mdb_table_select(mdb_table_t *tbl,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *results,
+ int size,
+ int dim)
+{
+ int ndata;
+
+ MDB_CHECKARG(tbl, -1);
+
+ if (dim > MQI_QUERY_RESULT_MAX)
+ dim = MQI_QUERY_RESULT_MAX;
+
+ if (cond)
+ ndata = select_conditional(tbl, cond, cds, results, size, dim);
+ else
+ ndata = select_all(tbl, cds, results, size, dim);
+
+ return ndata;
+}
+
+int mdb_table_select_by_index(mdb_table_t *tbl,
+ mqi_variable_t *idxvars,
+ mqi_column_desc_t *cds,
+ void *result)
+{
+ mqi_variable_t *var;
+ mdb_index_t *ix;
+ mdb_column_t *col;
+ void *data;
+ mqi_column_desc_t src;
+ int idxlen;
+ char idxval[MDB_INDEX_LENGTH_MAX];
+ int i;
+
+ MDB_CHECKARG(tbl && idxvars && cds && result, -1);
+ MDB_PREREQUISITE(MDB_TABLE_HAS_INDEX(tbl), -1);
+
+ ix = &tbl->index;
+ data = idxval - ix->offset;
+ src.offset = 0;
+ idxlen = ix->length;
+
+ for (i = 0; i < ix->ncolumn; i++) {
+ var = idxvars + i;
+ col = tbl->columns + (src.cindex = ix->columns[i]);
+
+ if (col->type != var->type) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ mdb_column_write(col, data, &src, var->v.generic);
+ }
+
+ return select_by_index(tbl, idxlen,idxval, cds, result);
+}
+
+int mdb_table_update(mdb_table_t *tbl,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *data)
+{
+ int index_update = 0;
+ mdb_column_t *col;
+ int cindex;
+ int nupdate;
+ int i;
+
+ MDB_CHECKARG(tbl, -1);
+
+
+ if (MDB_TABLE_HAS_INDEX(tbl)) {
+ for (i = 0; (cindex = cds[i].cindex) >= 0; i++) {
+ col = tbl->columns + cindex;
+ if ((col->flags & MQI_COLUMN_KEY)) {
+ index_update = 1;
+ break;
+ }
+ }
+ }
+
+ if (cond)
+ nupdate = update_conditional(tbl, cond, cds, data, index_update);
+ else
+ nupdate = update_all(tbl, cds, data, index_update);
+
+ return nupdate;
+}
+
+int mdb_table_delete(mdb_table_t *tbl, mqi_cond_entry_t *cond)
+{
+ int ndelete;
+
+ MDB_CHECKARG(tbl, -1);
+
+ if (cond)
+ ndelete = delete_conditional(tbl, cond);
+ else
+ ndelete = delete_all(tbl);
+
+ return ndelete;
+}
+
+mdb_table_t *mdb_table_find(char *table_name)
+{
+ MDB_CHECKARG(table_name, NULL);
+ MDB_PREREQUISITE(table_hash, NULL);
+
+ return mdb_hash_get_data(table_hash, 0,table_name);
+}
+
+
+int mdb_table_get_column_index(mdb_table_t *tbl, char *column_name)
+{
+ MDB_CHECKARG(tbl && column_name, -1);
+
+ return (mdb_hash_get_data(tbl->chash, 0,column_name) - NULL) - 1;
+}
+
+int mdb_table_get_size(mdb_table_t *tbl)
+{
+ MDB_CHECKARG(tbl, -1);
+
+ return tbl->nrow;
+}
+
+char *mdb_table_get_column_name(mdb_table_t *tbl, int colidx)
+{
+ MDB_CHECKARG(tbl && colidx >= 0 && colidx < tbl->ncolumn, NULL);
+
+ return tbl->columns[colidx].name;
+}
+
+mqi_data_type_t mdb_table_get_column_type(mdb_table_t *tbl, int colidx)
+{
+ MDB_CHECKARG(tbl && colidx >= 0 && colidx < tbl->ncolumn, mqi_error);
+
+ return tbl->columns[colidx].type;
+}
+
+int mdb_table_get_column_size(mdb_table_t *tbl, int colidx)
+{
+ MDB_CHECKARG(tbl && colidx >= 0 && colidx < tbl->ncolumn, -1);
+
+ return tbl->columns[colidx].length;
+}
+
+uint32_t mdb_table_get_stamp(mdb_table_t *tbl)
+{
+ return tbl->cnt.stamp;
+}
+
+int mdb_table_print_rows(mdb_table_t *tbl, char *buf, int len)
+{
+ mdb_row_t *row;
+ char *p, *e;
+ int l;
+ int i;
+ char dashes[1024];
+ table_iterator_t it;
+
+ MDB_CHECKARG(tbl && buf && len > 0, 0);
+
+ e = (p = buf) + len;
+
+ for (i = 0; i < tbl->ncolumn; i++)
+ p += mdb_column_print_header(tbl->columns + i, p, e-p);
+
+ if (p + ((l = p - buf) + 3) < e) {
+ if (l > (int)sizeof(dashes) - 1)
+ l = sizeof(dashes) - 1;
+
+ memset(dashes, '-', l);
+ dashes[l] = '\0';
+
+ p += snprintf(p, e-p, "\n%s\n", dashes);
+
+ for (it.cursor = NULL; (row = table_iterator(tbl, &it)) && p < e;) {
+ for (i = 0; i < tbl->ncolumn && p < e; i++)
+ p += mdb_column_print(tbl->columns + i, row->data, p, e-p);
+ if (p < e)
+ p += snprintf(p, e-p, "\n");
+ }
+ }
+
+ return p - buf;
+}
+
+
+static void destroy_table(mdb_table_t *tbl)
+{
+ mdb_row_t *row, *n;
+ mdb_column_t *cols;
+ int i;
+
+ mdb_index_drop(tbl);
+
+ mdb_hash_table_destroy(tbl->chash);
+
+ MDB_DLIST_FOR_EACH_SAFE(mdb_row_t, link, row,n, &tbl->rows)
+ mdb_row_delete(tbl, row, 0, 1);
+
+ for (i = 0, cols = tbl->columns; i < tbl->ncolumn; i++)
+ free(cols[i].name);
+
+ free(tbl->columns);
+ free(tbl->name);
+ free(tbl);
+}
+
+
+static mdb_row_t *table_iterator(mdb_table_t *tbl, table_iterator_t *it)
+{
+ mdb_dlist_t *next;
+ mdb_dlist_t *head;
+ mdb_row_t *row;
+
+ if (!it->cursor)
+ it->indexed = MDB_TABLE_HAS_INDEX(tbl);
+
+ if (it->indexed)
+ row = mdb_sequence_iterate(tbl->index.sequence, &it->cursor);
+ else {
+ head = &tbl->rows;
+ next = it->cursor ? (mdb_dlist_t *)it->cursor : head->next;
+
+ if (next == head)
+ row = NULL;
+ else {
+ row = MDB_LIST_RELOCATE(mdb_row_t, link, next);
+ it->cursor = next->next;
+ }
+ }
+
+ return row;
+}
+
+#if 0
+static int table_print_info(mdb_table_t *tbl, char *buf, int len)
+{
+#define PRINT(args...) if (e > p) p += snprintf(p, e-p, args)
+
+ mdb_column_t *col;
+ char *p, *e;
+ int i;
+
+ MDB_CHECKARG(tbl && buf && len > 0, 0);
+
+ e = (p = buf) + len;
+ *buf = '\0';
+
+ PRINT("table name : '%s'\n", tbl->name);
+ PRINT("table stamp : %u\n" , tbl->stamp);
+ PRINT("row length : %d\n" , tbl->dlgh);
+ PRINT("no of column: %d\n" , tbl->ncolumn);
+ PRINT(" index name type offset length\n"
+ " ---------------------------------------------\n");
+
+ for (i = 0; i < tbl->ncolumn; i++) {
+ col = tbl->columns + i;
+
+ PRINT(" %s %02d: %-16s %-8s %4d %4d\n",
+ col->flags & MQI_COLUMN_KEY ? "*" : " ",
+ i+1, col->name, mqi_data_type_str(col->type),
+ col->offset, col->length);
+ }
+
+ p += mdb_index_print(tbl, p, e-p);
+
+ return p - buf;
+
+#undef PRINT
+}
+#endif
+
+
+static int select_conditional(mdb_table_t *tbl,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *results,
+ int size,
+ int dim)
+{
+ mdb_column_t *columns = tbl->columns;
+ mdb_row_t *row;
+ mqi_cond_entry_t *ce;
+ table_iterator_t it;
+ int nresult;
+ void *result;
+ mqi_column_desc_t *result_dsc;
+ int cindex;
+ int i;
+
+ for (it.cursor = NULL, nresult = 0; (row = table_iterator(tbl, &it)); ) {
+ ce = cond;
+ if (mdb_cond_evaluate(tbl, &ce, row->data)) {
+ if (nresult >= dim) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ result = results + (size * nresult++);
+
+ for (i = 0; (cindex = (result_dsc = cds + i)->cindex) >= 0; i++)
+ mdb_column_read(result_dsc, result, columns+cindex, row->data);
+ }
+ }
+
+ return nresult;
+}
+
+static int select_all(mdb_table_t *tbl,
+ mqi_column_desc_t *cds,
+ void *results,
+ int size,
+ int dim)
+{
+ mdb_column_t *columns = tbl->columns;
+ mdb_row_t *row;
+ table_iterator_t it;
+ int nresult;
+ void *result;
+ mqi_column_desc_t *result_dsc;
+ int cindex;
+ int j;
+
+ MQI_UNUSED(dim);
+
+ for (it.cursor = NULL, nresult = 0;
+ (row = table_iterator(tbl, &it));
+ nresult++)
+ {
+ result = results + (size * nresult);
+
+ for (j = 0; (cindex = (result_dsc = cds + j)->cindex) >= 0; j++)
+ mdb_column_read(result_dsc, result, columns + cindex, row->data);
+ }
+
+ return nresult;
+}
+
+static int select_by_index(mdb_table_t *tbl,
+ int idxlen,
+ void *idxval,
+ mqi_column_desc_t *cds,
+ void *result)
+{
+ mdb_column_t *columns = tbl->columns;
+ mdb_row_t *row;
+ mqi_column_desc_t *result_dsc;
+ int cindex;
+ int j;
+
+ if (!(row = mdb_index_get_row(tbl, idxlen,idxval)))
+ return 0;
+
+ for (j = 0; (cindex = (result_dsc = cds + j)->cindex) >= 0; j++)
+ mdb_column_read(result_dsc, result, columns + cindex, row->data);
+
+ return 1;
+}
+
+
+static int update_conditional(mdb_table_t *tbl,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *data,
+ int index_update)
+{
+ mdb_row_t *row;
+ mqi_cond_entry_t *ce;
+ table_iterator_t it;
+ int nupdate, changed;
+
+ for (it.cursor = NULL, nupdate = 0; (row = table_iterator(tbl, &it)); ) {
+ ce = cond;
+ if (mdb_cond_evaluate(tbl, &ce, row->data)) {
+ changed = update_single_row(tbl, row, cds, data, index_update);
+
+ if (changed < 0)
+ nupdate = -1;
+ else
+ nupdate += (nupdate >= 0) ? changed : 0;
+ }
+ }
+
+ return nupdate;
+}
+
+static int update_all(mdb_table_t *tbl,
+ mqi_column_desc_t *cds,
+ void *data,
+ int index_update)
+{
+ mdb_row_t *row;
+ table_iterator_t it;
+ int nupdate, changed;
+
+ for (it.cursor = NULL, nupdate = 0; (row = table_iterator(tbl, &it)); )
+ {
+ changed = update_single_row(tbl, row, cds, data, index_update);
+
+ if (changed < 0)
+ nupdate = -1;
+ else
+ nupdate += (nupdate >= 0) ? changed : 0;
+ }
+
+ if (nupdate < 0)
+ errno = EEXIST;
+
+ return nupdate;
+}
+
+static int update_single_row(mdb_table_t *tbl,
+ mdb_row_t *row,
+ mqi_column_desc_t *cds,
+ void *data,
+ int index_update)
+{
+ mdb_row_t *before = NULL;
+ uint32_t txdepth = mdb_transaction_get_depth();
+ mqi_bitfld_t cmask;
+ int changed;
+
+ if (txdepth > 0 && !(before = mdb_row_duplicate(tbl, row)))
+ return -1;
+
+ changed = mdb_row_update(tbl, row, cds, data, index_update, &cmask);
+
+ if (changed <= 0) {
+ mdb_row_delete(tbl, before, 0, 1);
+ return changed;
+ }
+
+ if (mdb_log_change(tbl, txdepth, mdb_log_update, cmask, before, row) < 0)
+ return -1;
+
+ return 1;
+}
+
+static int delete_conditional(mdb_table_t *tbl, mqi_cond_entry_t *cond)
+{
+ table_iterator_t it;
+ mdb_row_t *row;
+ mqi_cond_entry_t *ce;
+ int ndelete;
+
+ for (it.cursor = NULL, ndelete = 0; (row = table_iterator(tbl, &it)); )
+ {
+ ce = cond;
+ if (mdb_cond_evaluate(tbl, &ce, row->data)) {
+ if (delete_single_row(tbl, row, 1) < 0)
+ ndelete = -1;
+ else
+ ndelete += (ndelete >= 0) ? 1 : 0;
+ }
+ }
+
+ return ndelete;
+}
+
+
+static int delete_all(mdb_table_t *tbl)
+{
+ mdb_row_t *row, *n;
+ int ndelete = 0;
+
+ mdb_index_reset(tbl);
+
+ MDB_DLIST_FOR_EACH_SAFE(mdb_row_t, link, row,n, &tbl->rows) {
+ if (delete_single_row(tbl, row, 0) < 0)
+ ndelete = -1;
+ else
+ ndelete += (ndelete >= 0) ? 1 : 0;
+ }
+
+ return ndelete;
+}
+
+static int delete_single_row(mdb_table_t *tbl, mdb_row_t *row,int index_update)
+{
+ uint32_t txdepth = mdb_transaction_get_depth();
+
+ mdb_row_delete(tbl, row, index_update, !txdepth);
+
+ if (txdepth)
+ mdb_log_change(tbl, txdepth, mdb_log_delete, 0, row, NULL);
+
+ return 0;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_TABLE_H__
+#define __MDB_TABLE_H__
+
+#include <murphy-db/mdb.h>
+#include <murphy-db/hash.h>
+#include <murphy-db/list.h>
+#include "index.h"
+#include "column.h"
+#include "log.h"
+#include "trigger.h"
+
+#define MDB_TABLE_HAS_INDEX(t) MDB_INDEX_DEFINED(&t->index)
+
+struct mdb_table_s {
+ mqi_handle_t handle;
+ char *name;
+ mdb_index_t index;
+ mdb_hash_t *chash; /* hash table for column names */
+ int ncolumn;
+ mdb_column_t *columns;
+ int dlgh; /* length of row data */
+ int nrow;
+ mdb_dlist_t rows;
+ mdb_dlist_t logs; /* transaction logs */
+ mdb_opcnt_t cnt;
+ mdb_trigger_t trigger; /* must be the last: it has a array[0] @end */
+};
+
+
+#endif /* __MDB_TABLE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include "transaction.h"
+#include "log.h"
+#include "index.h"
+#include "table.h"
+
+#define TRANSACTION_STATISTICS
+
+
+static uint32_t txdepth;
+
+static int destroy_row(mdb_table_t *, mdb_row_t *);
+static int remove_row(mdb_table_t *, mdb_row_t *);
+static int add_row(mdb_table_t *, mdb_row_t *);
+static int copy_row(mdb_table_t *, mdb_row_t *, mdb_row_t *);
+static int check_stamp(mdb_log_entry_t *);
+
+
+uint32_t mdb_transaction_begin(void)
+{
+ return ++txdepth;
+}
+
+int mdb_transaction_commit(uint32_t depth)
+{
+#define DATA_MAX (MQI_COLUMN_MAX * MQI_QUERY_RESULT_MAX)
+#define CHECK_TRIGGER_START(en) do { \
+ if (!start_triggered) { \
+ start_triggered = true; \
+ mdb_trigger_transaction_start(depth); \
+ } \
+ } while (0)
+#define CHECK_TRIGGER_END() do { \
+ if (start_triggered) { \
+ mdb_trigger_transaction_end(depth); \
+ } \
+ } while (0)
+
+
+ static uint8_t blank[sizeof(mdb_row_t) + DATA_MAX];
+
+ mdb_log_entry_t *en;
+ mdb_row_t *before;
+ mdb_row_t *after;
+ void *cursor;
+ bool start_triggered = false;
+ int sts = 0, s;
+
+ MDB_CHECKARG(depth > 0 && depth == txdepth, -1);
+
+ MDB_TRANSACTION_LOG_FOR_EACH_DELETE(depth, en, MDB_BACKWARD, cursor) {
+
+ if (!(before = en->before))
+ before = (mdb_row_t *)blank;
+
+ if (!(after = en->after))
+ after = (mdb_row_t *)blank;
+
+ switch (en->change) {
+
+ case mdb_log_insert:
+ CHECK_TRIGGER_START(en);
+ mdb_trigger_row_insert(en->table, after);
+ mdb_trigger_column_change(en->table, en->colmask, before, after);
+ s = 0;
+ break;
+
+ case mdb_log_update:
+ CHECK_TRIGGER_START(en);
+ mdb_trigger_column_change(en->table, en->colmask, before, after);
+ s = destroy_row(en->table, en->before);
+ break;
+
+ case mdb_log_delete:
+ CHECK_TRIGGER_START(en);
+ mdb_trigger_row_delete(en->table, before);
+ s = destroy_row(en->table, en->before);
+ break;
+
+ case mdb_log_start:
+ check_stamp(en);
+ free(en->cnt);
+ s = 0;
+ break;
+
+ default:
+ s = -1;
+ break;
+ }
+
+ if (sts == 0)
+ sts = s;
+ }
+
+ txdepth--;
+
+ CHECK_TRIGGER_END();
+
+ return sts;
+
+#undef DATA_MAX
+}
+
+int mdb_transaction_rollback(uint32_t depth)
+{
+ mdb_log_entry_t *en;
+ mdb_table_t *tbl;
+ void *cursor;
+ int sts = 0, s;
+
+ MDB_CHECKARG(depth > 0 && depth == txdepth, -1);
+
+ MDB_TRANSACTION_LOG_FOR_EACH_DELETE(depth, en, MDB_FORWARD, cursor) {
+
+ tbl = en->table;
+
+ switch (en->change) {
+
+ case mdb_log_insert: s = remove_row(tbl, en->after); break;
+ case mdb_log_delete: s = add_row(tbl, en->before); break;
+ case mdb_log_update: s = copy_row(tbl, en->after, en->before); break;
+ case mdb_log_start: s = check_stamp(en); break;
+ default: s = -1; break;
+ }
+
+ if (sts == 0)
+ sts = s;
+ }
+
+ txdepth--;
+
+ return sts;
+}
+
+int mdb_transaction_drop_table(mdb_table_t *tbl)
+{
+ mdb_log_entry_t *en;
+ void *cursor;
+ int sts = 0, s;
+
+ MDB_CHECKARG(tbl, -1);
+
+ MDB_TABLE_LOG_FOR_EACH_DELETE(tbl, en, cursor) {
+
+ switch (en->change) {
+
+ case mdb_log_insert: s = 0; break;
+ case mdb_log_delete:
+ case mdb_log_update: s = destroy_row(en->table, en->before); break;
+ case mdb_log_start: s = 0; break;
+ default: s = -1; break;
+ }
+
+ if (sts == 0)
+ sts = s;
+ }
+
+ return sts;
+}
+
+uint32_t mdb_transaction_get_depth(void)
+{
+ return txdepth;
+}
+
+static int destroy_row(mdb_table_t *tbl, mdb_row_t *row)
+{
+ MDB_CHECKARG(tbl && row && MDB_DLIST_EMPTY(row->link), -1);
+
+ return mdb_row_delete(tbl, row, 0, 1);
+}
+
+static int remove_row(mdb_table_t *tbl, mdb_row_t *row)
+{
+ MDB_CHECKARG(tbl && row, -1);
+
+ if (mdb_index_delete(tbl, row) < 0 ||
+ mdb_row_delete(tbl, row, 0, 1) < 0 )
+ {
+ return -1;
+ }
+
+ tbl->cnt.inserts--;
+
+ return 0;
+}
+
+static int add_row(mdb_table_t *tbl, mdb_row_t *row)
+{
+ MDB_CHECKARG(tbl && row, -1);
+
+ MDB_DLIST_APPEND(mdb_row_t, link, row, &tbl->rows);
+
+ tbl->cnt.deletes--;
+
+ return mdb_index_insert(tbl, row, 0, 0);
+}
+
+static int copy_row(mdb_table_t *tbl, mdb_row_t *dst, mdb_row_t *src)
+{
+
+ MDB_CHECKARG(tbl && dst && src && MDB_DLIST_EMPTY(src->link), -1);
+
+ if (src == dst)
+ return 0;
+
+ if (mdb_row_copy_over(tbl,dst,src) < 0 || mdb_row_delete(tbl,src,0,1) < 0)
+ return -1;
+
+ tbl->cnt.updates--;
+
+ return 0;
+}
+
+
+static int check_stamp(mdb_log_entry_t *en)
+{
+ mdb_table_t *tbl;
+
+ if (en->change != mdb_log_start)
+ return -1;
+
+ tbl = en->table;
+
+ if (tbl->cnt.inserts == en->cnt->inserts &&
+ tbl->cnt.deletes == en->cnt->deletes &&
+ tbl->cnt.updates == en->cnt->updates)
+ tbl->cnt.stamp = en->cnt->stamp;
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_TRANSACTION_H__
+#define __MDB_TRANSACTION_H__
+
+#include <murphy-db/mdb.h>
+
+
+int mdb_transaction_drop_table(mdb_table_t *);
+
+
+#endif /* __MDB_TRANSACTION_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <stdio.h>
+#include <alloca.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include "table.h"
+#include "row.h"
+
+#ifndef LOG_TRIGGER
+#define LOG_TRIGGER
+#endif
+
+typedef struct callback_s callback_t;
+typedef struct select_s select_t;
+
+typedef struct column_trigger_s column_trigger_t;
+typedef struct row_trigger_s row_trigger_t;
+typedef struct table_trigger_s table_trigger_t;
+typedef struct transact_trigger_s transact_trigger_t;
+
+struct callback_s {
+ mqi_trigger_cb_t function;
+ void *user_data;
+};
+
+struct select_s {
+ int length;
+ size_t cdsiz;
+ mqi_column_desc_t column[0];
+};
+
+struct column_trigger_s {
+ mdb_dlist_t link;
+ callback_t callback;
+ select_t select;
+};
+
+struct row_trigger_s {
+ mdb_dlist_t link;
+ callback_t callback;
+ select_t select;
+};
+
+struct table_trigger_s {
+ mdb_dlist_t link;
+ callback_t callback;
+};
+
+struct transact_trigger_s {
+ mdb_dlist_t link;
+ callback_t callback;
+};
+
+
+static int8_t lowest_bit_in[256] = {
+ /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
+ /* -------------------------------------------------------------- */
+ /* 00 */ -1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+ /* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
+};
+
+
+static MDB_DLIST_HEAD(table_change_triggers);
+static MDB_DLIST_HEAD(transact_change_triggers);
+
+static int get_select_params(mdb_table_t *, mqi_column_desc_t *, int *, int *);
+static void row_change(mqi_event_type_t, mdb_table_t *, mdb_row_t *);
+static void table_change(mqi_event_type_t, mdb_table_t *);
+static void transaction_change(mqi_event_type_t, uint32_t);
+
+
+void mdb_trigger_init(mdb_trigger_t *trigger, int ncol)
+{
+ int i;
+
+ if (!trigger || ncol < 1)
+ return;
+
+ MDB_DLIST_INIT(trigger->row_change);
+
+ for (i = 0; i < ncol; i++)
+ MDB_DLIST_INIT(trigger->column_change[i]);
+}
+
+void mdb_trigger_reset(mdb_trigger_t *trigger, int ncol)
+{
+ row_trigger_t *rt, *n;
+ column_trigger_t *ct, *m;
+ mdb_dlist_t *head;
+ int i;
+
+ if (!trigger || ncol < 1)
+ return;
+
+ MDB_DLIST_FOR_EACH_SAFE(row_trigger_t, link, rt,n, &trigger->row_change) {
+ MDB_DLIST_UNLINK(row_trigger_t, link, rt);
+ free(rt);
+ }
+
+ for (i = 0; i < ncol; i++) {
+ head = trigger-> column_change + i;
+
+ MDB_DLIST_FOR_EACH_SAFE(column_trigger_t, link, ct,m, head) {
+ MDB_DLIST_UNLINK(column_trigger_t, link, ct);
+ free(ct);
+ }
+ }
+}
+
+
+int mdb_trigger_add_column_callback(mdb_table_t *tbl,
+ int cidx,
+ mqi_trigger_cb_t cb_function,
+ void *cb_data,
+ mqi_column_desc_t *cds)
+{
+ column_trigger_t *tr;
+ size_t cdsiz;
+ int length, ncd;
+ mdb_dlist_t *head;
+
+ MDB_CHECKARG(tbl && cidx >= 0 && cidx < tbl->ncolumn && cb_function, -1);
+
+ if (!cds)
+ ncd = length = 0;
+ else {
+ if (get_select_params(tbl, cds, &ncd, &length) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ cdsiz = sizeof(mqi_column_desc_t) * ncd;
+ head = tbl->trigger.column_change + cidx;
+
+ MDB_DLIST_FOR_EACH(column_trigger_t, link, tr, head) {
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ if (cdsiz == tr->select.cdsiz) {
+ if (!cdsiz || memcmp(cds, tr->select.column, cdsiz))
+ return 0; /* silently ignore multiple registrations */
+ }
+
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ if (!(tr = calloc(1, sizeof(column_trigger_t) + cdsiz))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ MDB_DLIST_APPEND(column_trigger_t, link, tr, head);
+
+ tr->callback.function = cb_function;
+ tr->callback.user_data = cb_data;
+
+ tr->select.length = length;
+ tr->select.cdsiz = cdsiz;
+
+ if (ncd > 0)
+ memcpy(tr->select.column, cds, cdsiz);
+
+ return 0;
+}
+
+int mdb_trigger_delete_column_callback(mdb_table_t *tbl,
+ int cidx,
+ mqi_trigger_cb_t cb_function,
+ void *cb_data)
+{
+ column_trigger_t *tr, *n;
+ mdb_dlist_t *head;
+
+ MDB_CHECKARG(tbl && cidx >= 0 && cidx < tbl->ncolumn && cb_function, -1);
+
+ head = tbl->trigger.column_change + cidx;
+
+ MDB_DLIST_FOR_EACH_SAFE(column_trigger_t, link, tr,n, head) {
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ MDB_DLIST_UNLINK(column_trigger_t, link, tr);
+ free(tr);
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+int mdb_trigger_add_row_callback(mdb_table_t *tbl,
+ mqi_trigger_cb_t cb_function,
+ void *cb_data,
+ mqi_column_desc_t *cds)
+{
+ row_trigger_t *tr;
+ size_t cdsiz;
+ int length, ncd;
+ mdb_dlist_t *head;
+
+ MDB_CHECKARG(tbl && cb_function, -1);
+
+ if (!cds)
+ ncd = length = 0;
+ else {
+ if (get_select_params(tbl, cds, &ncd, &length) < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ cdsiz = sizeof(mqi_column_desc_t) * ncd;
+ head = &tbl->trigger.row_change;
+
+ MDB_DLIST_FOR_EACH(row_trigger_t, link, tr, head) {
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ if (cdsiz == tr->select.cdsiz) {
+ if (!cdsiz || memcmp(cds, tr->select.column, cdsiz))
+ return 0; /* silently ignore multiple registrations */
+ }
+
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ if (!(tr = calloc(1, sizeof(row_trigger_t) + cdsiz))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ MDB_DLIST_APPEND(row_trigger_t, link, tr, head);
+
+ tr->callback.function = cb_function;
+ tr->callback.user_data = cb_data;
+
+ tr->select.length = length;
+ tr->select.cdsiz = cdsiz;
+
+ if (ncd > 0)
+ memcpy(tr->select.column , cds, cdsiz);
+
+ return 0;
+}
+
+
+int mdb_trigger_delete_row_callback(mdb_table_t *tbl,
+ mqi_trigger_cb_t cb_function,
+ void *cb_data)
+{
+ row_trigger_t *tr, *n;
+
+ MDB_CHECKARG(tbl && cb_function, -1);
+
+ MDB_DLIST_FOR_EACH_SAFE(row_trigger_t,link, tr,n,&tbl->trigger.row_change){
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ MDB_DLIST_UNLINK(row_trigger_t, link, tr);
+ free(tr);
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+
+int mdb_trigger_add_table_callback(mqi_trigger_cb_t cb_function,
+ void *cb_data)
+{
+ table_trigger_t *tr;
+
+ MDB_CHECKARG(cb_function, -1);
+
+ MDB_DLIST_FOR_EACH(table_trigger_t, link, tr, &table_change_triggers) {
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ return 0; /* silently ignore multiple registrations */
+ }
+ }
+
+ if (!(tr = calloc(1, sizeof(table_trigger_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ MDB_DLIST_APPEND(table_trigger_t, link, tr, &table_change_triggers);
+
+ tr->callback.function = cb_function;
+ tr->callback.user_data = cb_data;
+
+ return 0;
+}
+
+
+int mdb_trigger_delete_table_callback(mqi_trigger_cb_t cb_function,
+ void *cb_data)
+{
+ table_trigger_t *tr, *n;
+
+ MDB_CHECKARG(cb_function, -1);
+
+ MDB_DLIST_FOR_EACH_SAFE(table_trigger_t,link, tr,n,&table_change_triggers){
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ MDB_DLIST_UNLINK(table_trigger_t,link, tr);
+ free(tr);
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+int mdb_trigger_add_transaction_callback(mqi_trigger_cb_t cb_function,
+ void *cb_data)
+{
+ transact_trigger_t *tr;
+
+ MDB_CHECKARG(cb_function, -1);
+
+ MDB_DLIST_FOR_EACH(transact_trigger_t,link, tr, &transact_change_triggers){
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ return 0; /* silently ignore multiple registrations */
+ }
+ }
+
+ if (!(tr = calloc(1, sizeof(transact_trigger_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ MDB_DLIST_APPEND(transact_trigger_t, link, tr, &transact_change_triggers);
+
+ tr->callback.function = cb_function;
+ tr->callback.user_data = cb_data;
+
+ return 0;
+}
+
+int mdb_trigger_delete_transaction_callback(mqi_trigger_cb_t cb_function,
+ void *cb_data)
+{
+ mdb_dlist_t *head = &transact_change_triggers;
+ transact_trigger_t *tr, *n;
+
+ MDB_CHECKARG(cb_function, -1);
+
+ MDB_DLIST_FOR_EACH_SAFE(transact_trigger_t, link, tr,n, head) {
+ if (cb_function == tr->callback.function &&
+ cb_data == tr->callback.user_data)
+ {
+ MDB_DLIST_UNLINK(transact_trigger_t, link, tr);
+ free(tr);
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+void mdb_trigger_column_change(mdb_table_t *tbl,
+ mqi_bitfld_t colmask,
+ mdb_row_t *before,
+ mdb_row_t *after)
+{
+ mqi_event_t evt;
+ mdb_dlist_t *hd;
+ column_trigger_t *tr;
+ mdb_column_t *col;
+ mqi_column_desc_t cd;
+ mqi_column_event_t *ce;
+ int cx;
+ int sx;
+ mqi_bitfld_t mask, byte;
+ int i,j,k;
+
+ if (!tbl || !colmask || !before || !after)
+ return;
+
+ memset(&evt, 0, sizeof(evt));
+ ce = &evt.column;
+
+ ce->event = mqi_column_changed;
+
+ ce->table.handle = tbl->handle;
+ ce->table.name = tbl->name;
+
+ ce->select.data = alloca(MDB_COLUMN_LENGTH_MAX * tbl->ncolumn);
+
+ if (!ce->select.data)
+ return;
+
+ for (mask = colmask, i = 0; mask != 0; mask >>= 8, i += 8) {
+ byte = mask & 0xff;
+
+ while ((j = lowest_bit_in[byte]) >= 0) {
+ byte &= ~MQI_BIT(j);
+ cx = i + j;
+ col = tbl->columns + cx;
+ hd = tbl->trigger.column_change + cx;
+
+ MDB_DLIST_FOR_EACH(column_trigger_t, link, tr, hd) {
+ ce->column.index = cx;
+ ce->column.name = tbl->columns[cx].name;
+
+ ce->value.type = tbl->columns[cx].type;
+
+ cd.cindex = cx;
+ cd.offset = 0;
+
+ mdb_column_read(&cd, &ce->value.old, col, before->data);
+ mdb_column_read(&cd, &ce->value.new_, col, after->data );
+
+ if (tr->select.length > 0) {
+ for (k = 0; (sx = tr->select.column[k].cindex) >= 0; k++){
+ mdb_column_read(tr->select.column + k, ce->select.data,
+ tbl->columns + sx, after->data);
+ }
+ }
+
+ tr->callback.function(&evt, tr->callback.user_data);
+ }
+ }
+ }
+}
+
+
+void mdb_trigger_row_insert(mdb_table_t *tbl, mdb_row_t *row)
+{
+ if (tbl && row)
+ row_change(mqi_row_inserted, tbl, row);
+}
+
+void mdb_trigger_row_delete(mdb_table_t *tbl, mdb_row_t *row)
+{
+ if (tbl && row)
+ row_change(mqi_row_deleted, tbl, row);
+}
+
+
+void mdb_trigger_table_create(mdb_table_t *tbl)
+{
+ if (tbl)
+ table_change(mqi_table_created, tbl);
+}
+
+void mdb_trigger_table_drop(mdb_table_t *tbl)
+{
+ if (tbl)
+ table_change(mqi_table_dropped, tbl);
+}
+
+void mdb_trigger_transaction_start(uint32_t depth)
+{
+ transaction_change(mqi_transaction_start, depth);
+}
+
+void mdb_trigger_transaction_end(uint32_t depth)
+{
+ transaction_change(mqi_transaction_end, depth);
+}
+
+static int get_select_params(mdb_table_t *tbl,
+ mqi_column_desc_t *cds,
+ int *ncd_ret,
+ int *length_ret)
+{
+ mqi_column_desc_t *cd;
+ int ncd, length;
+ int end;
+ int cx;
+
+ *ncd_ret = *length_ret = 0;
+
+ for (ncd = length = 0; (cx = (cd = cds + ncd)->cindex) >= 0; ncd++) {
+ if ((end = cd->offset + tbl->columns[cx].length) > length)
+ length = end;
+ }
+
+ *ncd_ret = ncd + 1;
+ *length_ret = length;
+
+ return 0;
+}
+
+
+static void row_change(mqi_event_type_t event,
+ mdb_table_t *tbl,
+ mdb_row_t *row)
+{
+ mqi_event_t evt;
+ row_trigger_t *tr;
+ mqi_row_event_t *re;
+ int sx;
+ int i;
+
+ memset(&evt, 0, sizeof(evt));
+ re = &evt.row;
+
+ re->event = event;
+
+ re->table.handle = tbl->handle;
+ re->table.name = tbl->name;
+
+ re->select.data = alloca(MDB_COLUMN_LENGTH_MAX * tbl->ncolumn);
+
+ if (!re->select.data)
+ return;
+
+ MDB_DLIST_FOR_EACH(row_trigger_t, link, tr, &tbl->trigger.row_change) {
+ if (tr->select.length > 0) {
+ for (i = 0; (sx = tr->select.column[i].cindex) >= 0; i++) {
+ mdb_column_read(tr->select.column + i, re->select.data,
+ tbl->columns + sx, row->data);
+ }
+ }
+
+ tr->callback.function(&evt, tr->callback.user_data);
+ }
+}
+
+static void table_change(mqi_event_type_t event, mdb_table_t *tbl)
+{
+ mqi_event_t evt;
+ table_trigger_t *tr;
+ mqi_table_event_t *te;
+
+ memset(&evt, 0, sizeof(evt));
+ te = &evt.table;
+
+ te->event = event;
+
+ te->table.handle = tbl->handle;
+ te->table.name = tbl->name;
+
+ MDB_DLIST_FOR_EACH(table_trigger_t, link, tr, &table_change_triggers) {
+ tr->callback.function(&evt, tr->callback.user_data);
+ }
+}
+
+static void transaction_change(mqi_event_type_t event, uint32_t depth)
+{
+ mqi_event_t evt;
+ transact_trigger_t *tr;
+ mqi_transact_event_t *te;
+
+ memset(&evt, 0, sizeof(evt));
+ te = &evt.transact;
+
+ te->event = event;
+ te->depth = depth;
+
+ MDB_DLIST_FOR_EACH(transact_trigger_t,link, tr, &transact_change_triggers){
+ tr->callback.function(&evt, tr->callback.user_data);
+ }
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MDB_TRIGGER_H__
+#define __MDB_TRIGGER_H__
+
+#include <murphy-db/mqi-types.h>
+#include <murphy-db/list.h>
+
+
+
+typedef struct {
+ mdb_dlist_t row_change;
+ mdb_dlist_t column_change[0];
+} mdb_trigger_t;
+
+void mdb_trigger_init(mdb_trigger_t *, int);
+void mdb_trigger_reset(mdb_trigger_t *, int);
+
+void mdb_trigger_column_change(mdb_table_t*, mqi_bitfld_t,
+ mdb_row_t *, mdb_row_t *);
+
+void mdb_trigger_row_delete(mdb_table_t *, mdb_row_t *);
+void mdb_trigger_row_insert(mdb_table_t *, mdb_row_t *);
+
+void mdb_trigger_table_create(mdb_table_t *);
+void mdb_trigger_table_drop(mdb_table_t *);
+
+void mdb_trigger_transaction_start(uint32_t);
+void mdb_trigger_transaction_end(uint32_t);
+
+#endif /* __MDB_TRIGGER_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+pkglib_LTLIBRARIES = libmqi.la
+
+LINKER_SCRIPT = linker-script.mqi
+QUIET_GEN = $(Q:@=@echo ' GEN '$@;)
+
+libmqi_la_CFLAGS = -I../include
+
+libmqi_ladir = \
+ $(includedir)/murphy-db
+
+libmqi_la_HEADERS = \
+ ../include/murphy-db/mqi.h
+
+libmqi_la_SOURCES = \
+ $(libmqi_ls_HEADERS) \
+ mqi.c db.h mdb-backend.h mdb-backend.c
+
+libmqi_la_LDFLAGS = \
+ -Wl,-version-script=$(LINKER_SCRIPT)
+# -version-info @MURPHYDB_VERSION_INFO@
+
+libmqi_la_DEPENDENCIES = $(LINKER_SCRIPT)
+
+# linker script generation
+$(LINKER_SCRIPT): $(libmqi_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmqi_la_CFLAGS)" -p "^mqi_" -o $@ $^
+
+clean-$(LINKER_SCRIPT):
+ -rm -f $(LINKER_SCRIPT)
+
+# cleanup
+clean-local:: # clean-$(LINKER_SCRIPT)
+ rm -f *~
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQI_DB_H__
+#define __MQI_DB_H__
+
+typedef struct {
+ int (*create_transaction_trigger)(mqi_trigger_cb_t, void *);
+ int (*create_table_trigger)(mqi_trigger_cb_t, void *);
+ int (*create_row_trigger)(void *, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+ int (*create_column_trigger)(void *, int, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+ int (*drop_transaction_trigger)(mqi_trigger_cb_t, void *);
+ int (*drop_table_trigger)(mqi_trigger_cb_t, void *);
+ int (*drop_row_trigger)(void *, mqi_trigger_cb_t, void *);
+ int (*drop_column_trigger)(void *, int, mqi_trigger_cb_t, void *);
+ uint32_t (*begin_transaction)(void);
+ int (*commit_transaction)(uint32_t);
+ int (*rollback_transaction)(uint32_t);
+ uint32_t (*get_transaction_id)(void);
+ void *(*create_table)(char *, char **, mqi_column_def_t *);
+ int (*register_table_handle)(void *, mqi_handle_t);
+ int (*create_index)(void *, char **);
+ int (*drop_table)(void *);
+ int (*describe)(void *, mqi_column_def_t *, int);
+ int (*insert_into)(void *, int, mqi_column_desc_t *, void **);
+ int (*select)(void *, mqi_cond_entry_t *, mqi_column_desc_t *,
+ void *, int, int);
+ int (*select_by_index)(void *, mqi_variable_t *,
+ mqi_column_desc_t *, void *);
+ int (*update)(void *, mqi_cond_entry_t *, mqi_column_desc_t *,void*);
+ int (*delete_from)(void *, mqi_cond_entry_t *);
+ void *(*find_table)(char *);
+ int (*get_column_index)(void *, char *);
+ int (*get_table_size)(void *);
+ uint32_t (*get_table_stamp)(void *);
+ char *(*get_column_name)(void *, int);
+ mqi_data_type_t (*get_column_type)(void *, int);
+ int (*get_column_size)(void *, int);
+ int (*print_rows)(void *, char *, int);
+} mqi_db_functbl_t;
+
+
+
+#endif /* __MQI_DB_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/handle.h>
+#include <murphy-db/mdb.h>
+
+#include "mdb-backend.h"
+
+
+static int create_transaction_trigger(mqi_trigger_cb_t, void *);
+static int create_table_trigger(mqi_trigger_cb_t, void *);
+static int create_row_trigger(void *, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+static int create_column_trigger(void *, int, mqi_trigger_cb_t, void *,
+ mqi_column_desc_t *);
+static int drop_transaction_trigger(mqi_trigger_cb_t, void *);
+static int drop_table_trigger(mqi_trigger_cb_t, void *);
+static int drop_row_trigger(void *, mqi_trigger_cb_t, void *);
+static int drop_column_trigger(void*, int, mqi_trigger_cb_t, void *);
+static uint32_t begin_transaction(void);
+static int commit_transaction(uint32_t);
+static int rollback_transaction(uint32_t);
+static uint32_t get_transaction_id(void);
+static void * create_table(char *, char **, mqi_column_def_t *);
+static int register_table_handle(void *, mqi_handle_t);
+static int create_index(void *, char **);
+static int drop_table(void *);
+static int describe(void *, mqi_column_def_t *, int);
+static int insert_into(void *, int, mqi_column_desc_t *, void **);
+static int select_general(void *, mqi_cond_entry_t *, mqi_column_desc_t *,
+ void *, int, int);
+static int select_by_index(void *, mqi_variable_t *, mqi_column_desc_t *,
+ void *);
+static int update(void *, mqi_cond_entry_t *, mqi_column_desc_t*,void*);
+static int delete_from(void *, mqi_cond_entry_t *);
+static void * find_table(char *);
+static int get_column_index(void *, char *);
+static int get_table_size(void *);
+static uint32_t get_table_stamp(void *);
+static char * get_column_name(void *, int);
+static mqi_data_type_t get_column_type(void *, int);
+static int get_column_size(void *, int);
+static int print_rows(void *, char *, int);
+
+static mqi_db_functbl_t functbl = {
+ create_transaction_trigger,
+ create_table_trigger,
+ create_row_trigger,
+ create_column_trigger,
+ drop_transaction_trigger,
+ drop_table_trigger,
+ drop_row_trigger,
+ drop_column_trigger,
+ begin_transaction,
+ commit_transaction,
+ rollback_transaction,
+ get_transaction_id,
+ create_table,
+ register_table_handle,
+ create_index,
+ drop_table,
+ describe,
+ insert_into,
+ select_general,
+ select_by_index,
+ update,
+ delete_from,
+ find_table,
+ get_column_index,
+ get_table_size,
+ get_table_stamp,
+ get_column_name,
+ get_column_type,
+ get_column_size,
+ print_rows
+};
+
+
+mqi_db_functbl_t *mdb_backend_init(void)
+{
+ return &functbl;
+}
+
+
+static int create_transaction_trigger(mqi_trigger_cb_t cb, void *data)
+{
+ return mdb_trigger_add_transaction_callback(cb, data);
+}
+
+static int create_table_trigger(mqi_trigger_cb_t cb, void *data)
+{
+ return mdb_trigger_add_table_callback(cb, data);
+}
+
+static int create_row_trigger(void *t,
+ mqi_trigger_cb_t cb,
+ void *data,
+ mqi_column_desc_t *cds)
+{
+ return mdb_trigger_add_row_callback((mdb_table_t *)t, cb, data, cds);
+}
+
+static int create_column_trigger(void *t,
+ int colidx,
+ mqi_trigger_cb_t cb,
+ void *data,
+ mqi_column_desc_t *cds)
+{
+ return mdb_trigger_add_column_callback((mdb_table_t *)t, colidx,
+ cb, data, cds);
+}
+
+static int drop_transaction_trigger(mqi_trigger_cb_t cb, void *data)
+{
+ return mdb_trigger_delete_transaction_callback(cb, data);
+}
+
+static int drop_table_trigger(mqi_trigger_cb_t cb, void *data)
+{
+ return mdb_trigger_delete_table_callback(cb, data);
+}
+
+static int drop_row_trigger(void *t, mqi_trigger_cb_t cb, void *data)
+{
+ return mdb_trigger_delete_row_callback((mdb_table_t *)t, cb, data);
+}
+
+static int drop_column_trigger(void *t,
+ int colidx,
+ mqi_trigger_cb_t cb,
+ void *data)
+{
+ return mdb_trigger_delete_column_callback((mdb_table_t *)t,colidx,cb,data);
+}
+
+static uint32_t begin_transaction(void)
+{
+ uint32_t depth = mdb_transaction_begin();
+
+ if (!depth)
+ return MDB_HANDLE_INVALID;
+
+ return depth;
+}
+
+static int commit_transaction(uint32_t depth)
+{
+ return mdb_transaction_commit(depth);
+}
+
+static int rollback_transaction(uint32_t depth)
+{
+ return mdb_transaction_rollback(depth);
+}
+
+static uint32_t get_transaction_id(void)
+{
+ return mdb_transaction_get_depth();
+}
+
+static void *create_table(char *name,
+ char **index_columns,
+ mqi_column_def_t *cdefs)
+{
+ return mdb_table_create(name, index_columns, cdefs);
+}
+
+static int register_table_handle(void *t, mqi_handle_t handle)
+{
+ return mdb_table_register_handle((mdb_table_t *)t, handle);
+}
+
+
+
+static int create_index(void *t, char **index_columns)
+{
+ return mdb_table_create_index((mdb_table_t *)t, index_columns);
+}
+
+static int drop_table(void *t)
+{
+ return mdb_table_drop((mdb_table_t *)t);
+}
+
+static int describe(void *t, mqi_column_def_t *defs, int len)
+{
+ return mdb_table_describe((mdb_table_t *)t, defs, len);
+}
+
+static int insert_into(void *t,
+ int ignore,
+ mqi_column_desc_t *cds,
+ void **data)
+{
+ return mdb_table_insert((mdb_table_t *)t, ignore, cds, data);
+}
+
+static int select_general(void *t,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *results,
+ int size,
+ int dim)
+{
+ return mdb_table_select((mdb_table_t *)t, cond, cds, results, size, dim);
+}
+
+static int select_by_index(void *t,
+ mqi_variable_t *idxvars,
+ mqi_column_desc_t *cds,
+ void *result)
+{
+ return mdb_table_select_by_index((mdb_table_t *)t, idxvars, cds, result);
+}
+
+
+static int update(void *t,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *data)
+{
+ return mdb_table_update((mdb_table_t *)t, cond, cds, data);
+}
+
+static int delete_from(void *t, mqi_cond_entry_t *cond)
+{
+ return mdb_table_delete((mdb_table_t *)t, cond);
+}
+
+
+static void *find_table(char *table_name)
+{
+ return mdb_table_find(table_name);
+}
+
+
+static int get_column_index(void *t, char *column_name)
+{
+ return mdb_table_get_column_index((mdb_table_t *)t, column_name);
+}
+
+static int get_table_size(void *t)
+{
+ return mdb_table_get_size((mdb_table_t *)t);
+}
+
+static uint32_t get_table_stamp(void *t)
+{
+ return mdb_table_get_stamp((mdb_table_t *)t);
+}
+
+static char *get_column_name(void *t, int colidx)
+{
+ return mdb_table_get_column_name((mdb_table_t *)t, colidx);
+}
+
+static mqi_data_type_t get_column_type(void *t, int colidx)
+{
+ return mdb_table_get_column_type((mdb_table_t *)t, colidx);
+}
+
+static int get_column_size(void *t, int colidx)
+{
+ return mdb_table_get_column_size((mdb_table_t *)t, colidx);
+}
+
+static int print_rows(void *t, char *buf, int len)
+{
+ return mdb_table_print_rows((mdb_table_t *)t, buf, len);
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MQI_MDB_BACKEND_H__
+#define __MQI_MDB_BACKEND_H__
+
+#include "db.h"
+
+mqi_db_functbl_t *mdb_backend_init(void);
+
+
+#endif /* __MQI_MDB_BACKEND_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <string.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/handle.h>
+#include <murphy-db/mqi.h>
+#include <murphy-db/handle.h>
+#include <murphy-db/hash.h>
+#include "mdb-backend.h"
+
+#define MAX_DB 2
+
+#define TX_DEPTH_BITS 4
+#define TX_USEID_BITS ((sizeof(mqi_handle_t) * 8) - TX_DEPTH_BITS)
+#define TX_DEPTH_MAX (((mqi_handle_t)1) << TX_DEPTH_BITS)
+#define TX_USEID_MAX (((mqi_handle_t)1) << TX_USEID_BITS)
+#define TX_DEPTH_MASK (TX_DEPTH_MAX - 1)
+#define TX_USEID_MASK (TX_USEID_MAX - 1)
+
+#define TX_DEPTH(h) ((h) & TX_DEPTH_MASK)
+#define TX_USEID(h) ((h) & (TX_USEID_MASK << TX_DEPTH_BITS))
+
+#define TX_HANDLE(useid, depth) \
+ (((useid) & (TX_USEID_MASK << TX_DEPTH_BITS)) | ((depth) & TX_DEPTH_MASK))
+
+#define TX_USEID_INCREMENT(u) \
+ (u) = ((((u) + ((mqi_handle_t)1)) << TX_DEPTH_BITS) & \
+ (TX_USEID_MASK << TX_DEPTH_BITS))
+
+
+#if MQI_TXDEPTH_MAX > (1 << TX_DEPTH_BITS)
+#error "Too few TX_DEPTH_BITS to represent MQI_TXDEPTH_MAX"
+#endif
+
+#define DB_TYPE(db) ((db)->flags & MQI_TABLE_TYPE_MASK)
+
+#define GET_TABLE(tbl, ftb, h, errval) \
+ do { \
+ mqi_table_t *t; \
+ mqi_db_t *db; \
+ if (!(t = mdb_handle_get_data(table_handle, h)) || !(db = t->db)) { \
+ errno = ENOENT; \
+ return errval; \
+ } \
+ if (!(tbl = t->handle) || !(ftb = db->functbl)) { \
+ errno = EIO; \
+ return errval; \
+ } \
+ } while(0)
+
+typedef struct {
+ const char *engine;
+ uint32_t flags;
+ mqi_db_functbl_t *functbl;
+} mqi_db_t;
+
+typedef struct {
+ mqi_db_t *db;
+ void *handle;
+} mqi_table_t;
+
+typedef struct {
+ uint32_t useid;
+ uint32_t txid[MAX_DB];
+} mqi_transaction_t;
+
+
+static int db_register(const char *, uint32_t, mqi_db_functbl_t *);
+
+
+static int ndb;
+static mqi_db_t *dbs;
+mdb_handle_map_t *table_handle;
+mdb_hash_t *table_name_hash;
+mdb_handle_map_t *transact_handle;
+mqi_transaction_t txstack[MQI_TXDEPTH_MAX];
+int txdepth;
+
+
+int mqi_open(void)
+{
+ if (!ndb && !dbs) {
+ if (!(dbs = calloc(MAX_DB, sizeof(mqi_db_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ table_handle = MDB_HANDLE_MAP_CREATE();
+ table_name_hash = MDB_HASH_TABLE_CREATE(varchar, 256);
+
+ transact_handle = MDB_HANDLE_MAP_CREATE();
+
+ if (db_register("MurphyDB", MQI_TEMPORARY, mdb_backend_init()) < 0) {
+ errno = EIO;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int mqi_close(void)
+{
+ int i;
+
+ if (ndb > 0 && dbs) {
+ for (i = 0; i < ndb; i++)
+ free((void *)dbs[i].engine);
+
+ free(dbs);
+
+ MDB_HANDLE_MAP_DESTROY(table_handle);
+ MDB_HASH_TABLE_DESTROY(table_name_hash);
+ MDB_HANDLE_MAP_DESTROY(transact_handle);
+
+ table_handle = NULL;
+ table_name_hash = NULL;
+ transact_handle = NULL;
+
+ dbs = NULL;
+ ndb = 0;
+ }
+
+ return 0;
+}
+
+
+int mqi_show_tables(uint32_t flags, char **buf, int len)
+{
+ mqi_handle_t h;
+ mqi_table_t *tbl;
+ mqi_db_t *db;
+ void *data;
+ char *name;
+ void *cursor;
+ int i = 0;
+ int j;
+
+ MDB_CHECKARG(buf && len > 0, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ MDB_HASH_TABLE_FOR_EACH_WITH_KEY(table_name_hash, data, name, cursor) {
+ if (i >= len) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ if ((h = data - NULL) == MQI_HANDLE_INVALID)
+ continue;
+
+ if (!(tbl = mdb_handle_get_data(table_handle, h)) || !(db = tbl->db))
+ continue;
+
+ if (!(DB_TYPE(db) & flags))
+ continue;
+
+ for (j = 0; j < i; j++) {
+ if (strcasecmp(name, buf[j]) < 0) {
+ memmove(buf + (j+1), buf + j, sizeof(char *) * (i-j));
+ break;
+ }
+ }
+ buf[j] = name;
+
+ i++;
+ }
+
+ return i;
+}
+
+
+int mqi_create_transaction_trigger(mqi_trigger_cb_t callback, void *user_data)
+{
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ int i;
+
+ MDB_CHECKARG(callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ for (i = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ if (ftb->create_transaction_trigger(callback, user_data) < 0) {
+
+ for (i--; i >= 0; i--) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ ftb->drop_transaction_trigger(callback, user_data);
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int mqi_create_table_trigger(mqi_trigger_cb_t callback, void *user_data)
+{
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ int i;
+
+ MDB_CHECKARG(callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ for (i = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ if (ftb->create_table_trigger(callback, user_data) < 0) {
+
+ for (i--; i >= 0; i--) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ ftb->drop_table_trigger(callback, user_data);
+ }
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int mqi_create_row_trigger(mqi_handle_t h,
+ mqi_trigger_cb_t callback,
+ void *user_data,
+ mqi_column_desc_t *cds)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->create_row_trigger(tbl, callback, user_data, cds);
+}
+
+
+int mqi_create_column_trigger(mqi_handle_t h,
+ int colidx,
+ mqi_trigger_cb_t callback,
+ void *user_data,
+ mqi_column_desc_t *cds)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->create_column_trigger(tbl, colidx, callback, user_data, cds);
+}
+
+
+int mqi_drop_transaction_trigger(mqi_trigger_cb_t callback, void *user_data)
+{
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ int sts;
+ int i;
+
+ MDB_CHECKARG(callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ for (sts = 0, i = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ if (ftb->drop_transaction_trigger(callback, user_data) < 0)
+ sts = -1;
+ }
+
+ return sts;
+}
+
+
+int mqi_drop_table_trigger(mqi_trigger_cb_t callback, void *user_data)
+{
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ int sts;
+ int i;
+
+ MDB_CHECKARG(callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ for (sts = 0, i = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ if (ftb->drop_table_trigger(callback, user_data) < 0)
+ sts = -1;
+ }
+
+ return sts;
+}
+
+
+int mqi_drop_row_trigger(mqi_handle_t h,
+ mqi_trigger_cb_t callback,
+ void *user_data)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->drop_row_trigger(tbl, callback, user_data);
+}
+
+
+int mqi_drop_column_trigger(mqi_handle_t h,
+ int colidx,
+ mqi_trigger_cb_t callback,
+ void *user_data)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && callback, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->drop_column_trigger(tbl, colidx, callback, user_data);
+}
+
+
+mqi_handle_t mqi_begin_transaction(void)
+{
+ mqi_transaction_t *tx;
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ uint32_t depth;
+ int i;
+
+ MDB_PREREQUISITE(dbs && ndb > 0 && transact_handle, MQI_HANDLE_INVALID);
+ MDB_ASSERT(txdepth < MQI_TXDEPTH_MAX - 1, EOVERFLOW, MQI_HANDLE_INVALID);
+
+ depth = txdepth++;
+ tx = txstack + depth;
+
+ TX_USEID_INCREMENT(tx->useid);
+
+ for (i = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+ tx->txid[i] = ftb->begin_transaction();
+ }
+
+ return TX_HANDLE(tx->useid, depth);
+}
+
+
+int mqi_commit_transaction(mqi_handle_t h)
+{
+ uint32_t depth = TX_DEPTH(h);
+ uint32_t useid = TX_USEID(h);
+ mqi_transaction_t *tx;
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ int err;
+ int i;
+
+ MDB_CHECKARG(h != MQI_HANDLE_INVALID && depth < MQI_TXDEPTH_MAX, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+ MDB_ASSERT(txdepth > 0 && depth == (uint32_t)txdepth - 1, EBADSLT, -1);
+
+ tx = txstack + depth;
+
+ MDB_ASSERT(tx->useid == useid, EBADSLT, -1);
+
+ for (i = 0, err = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ if (ftb->commit_transaction(tx->txid[i]) < 0)
+ err = -1;
+ }
+
+ txdepth--;
+
+ return err;
+}
+
+int mqi_rollback_transaction(mqi_handle_t h)
+{
+ uint32_t depth = TX_DEPTH(h);
+ uint32_t useid = TX_USEID(h);
+ mqi_transaction_t *tx;
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ int err;
+ int i;
+
+ MDB_CHECKARG(h != MQI_HANDLE_INVALID && depth < MQI_TXDEPTH_MAX, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+ MDB_ASSERT(txdepth > 0 && depth == (uint32_t)txdepth - 1, EBADSLT, -1);
+
+ tx = txstack + depth;
+
+ MDB_ASSERT(tx->useid == useid, EBADSLT, -1);
+
+ for (i = 0, err = 0; i < ndb; i++) {
+ db = dbs + i;
+ ftb = db->functbl;
+
+ if (ftb->rollback_transaction(tx->txid[i]) < 0)
+ err = -1;
+ }
+
+ txdepth--;
+
+ return err;
+}
+
+mqi_handle_t mqi_get_transaction_handle(void)
+{
+ uint32_t depth;
+ mqi_transaction_t *tx;
+
+ MDB_CHECKARG(txdepth > 0, MQI_HANDLE_INVALID);
+ MDB_PREREQUISITE(dbs && ndb > 0, MQI_HANDLE_INVALID);
+
+ depth = txdepth - 1;
+ tx = txstack + depth;
+
+ return TX_HANDLE(tx->useid, depth);
+}
+
+
+uint32_t mqi_get_transaction_depth(void)
+{
+ return txdepth;
+}
+
+
+mqi_handle_t mqi_create_table(char *name,
+ uint32_t flags,
+ char **index_columns,
+ mqi_column_def_t *cdefs)
+{
+ mqi_db_t *db;
+ mqi_db_functbl_t *ftb;
+ mqi_table_t *tbl = NULL;
+ mqi_handle_t h = MQI_HANDLE_INVALID;
+ char *namedup = NULL;
+ int i;
+
+ MDB_CHECKARG(name && cdefs, MQI_HANDLE_INVALID);
+ MDB_PREREQUISITE(dbs && ndb > 0, MQI_HANDLE_INVALID);
+
+ for (i = 0, ftb = NULL; i < ndb; i++) {
+ db = dbs + i;
+
+ if ((DB_TYPE(db) & flags) != 0) {
+ ftb = db->functbl;
+ break;
+ }
+ }
+
+ MDB_ASSERT(ftb, ENOENT, MQI_HANDLE_INVALID);
+
+ if(!(tbl = calloc(1, sizeof(mqi_table_t))))
+ return MQI_HANDLE_INVALID;
+
+ tbl->db = db;
+ tbl->handle = NULL;
+
+ if (!(namedup = strdup(name)))
+ goto cleanup;
+
+ if (!(tbl->handle = ftb->create_table(name, index_columns, cdefs)))
+ goto cleanup;
+
+ if ((h = mdb_handle_add(table_handle, tbl)) == MQI_HANDLE_INVALID)
+ goto cleanup;
+
+ if (mdb_hash_add(table_name_hash, 0,namedup, NULL + h) < 0) {
+ mdb_handle_delete(table_handle, h);
+ h = MQI_HANDLE_INVALID;
+ }
+
+ ftb->register_table_handle(tbl->handle, h);
+
+ return h;
+
+ cleanup:
+ if (tbl) {
+ if (tbl->handle) {
+ mdb_handle_delete(table_handle, h);
+ ftb->drop_table(tbl->handle);
+ }
+ mdb_hash_delete(table_name_hash, 0,name);
+ free(namedup);
+ free(tbl);
+ }
+
+ return MDB_HANDLE_INVALID;
+}
+
+
+int mqi_create_index(mqi_handle_t h, char **index_columns)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && index_columns, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->create_index(tbl, index_columns);
+}
+
+int mqi_drop_table(mqi_handle_t h)
+{
+ mqi_table_t *tbl;
+ mqi_db_functbl_t *ftb;
+ char *name;
+ void *data;
+ void *cursor;
+ int sts;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ if (!(tbl = mdb_handle_delete(table_handle, h)))
+ return -1;
+
+ ftb = tbl->db->functbl;
+
+ MDB_HASH_TABLE_FOR_EACH_WITH_KEY_SAFE(table_name_hash, data,name, cursor) {
+ if ((mqi_handle_t)(data - NULL) == h) {
+ mdb_hash_delete(table_name_hash, 0,name);
+ sts = ftb->drop_table(tbl->handle);
+ free(name);
+ free(tbl);
+ return sts;
+ }
+ }
+
+ return -1;
+}
+
+int mqi_describe(mqi_handle_t h, mqi_column_def_t *defs, int len)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && defs && len > 0, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->describe(tbl, defs, len);
+}
+
+int mqi_insert_into(mqi_handle_t h,
+ int ignore,
+ mqi_column_desc_t *cds,
+ void **data)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && cds && data && data[0], -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->insert_into(tbl, ignore, cds, data);
+}
+
+int mqi_select(mqi_handle_t h,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *rows,
+ int rowsize,
+ int dim)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && cds &&
+ rows && rowsize > 0 && dim > 0, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->select(tbl, cond, cds, rows, rowsize, dim);
+}
+
+int mqi_select_by_index(mqi_handle_t h,
+ mqi_variable_t *idxvars,
+ mqi_column_desc_t *cds,
+ void *result)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && idxvars && cds && result, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->select_by_index(tbl, idxvars, cds, result);
+}
+
+int mqi_update(mqi_handle_t h,
+ mqi_cond_entry_t *cond,
+ mqi_column_desc_t *cds,
+ void *data)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && cds && data, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->update(tbl, cond, cds, data);
+}
+
+int mqi_delete_from(mqi_handle_t h, mqi_cond_entry_t *cond)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->delete_from(tbl, cond);
+}
+
+mqi_handle_t mqi_get_table_handle(char *table_name)
+{
+ void *data;
+
+ MDB_CHECKARG(table_name, MQI_HANDLE_INVALID);
+ MDB_PREREQUISITE(dbs && ndb > 0, MQI_HANDLE_INVALID);
+
+ data = mdb_hash_get_data(table_name_hash, 0,table_name);
+
+ if (data != NULL)
+ return data - NULL;
+ else
+ return MQI_HANDLE_INVALID;
+}
+
+
+int mqi_get_column_index(mqi_handle_t h, char *column_name)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && column_name, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->get_column_index(tbl, column_name);
+}
+
+int mqi_get_table_size(mqi_handle_t h)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->get_table_size(tbl);
+}
+
+uint32_t mqi_get_table_stamp(mqi_handle_t h)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID, MQI_STAMP_NONE);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->get_table_stamp(tbl);
+}
+
+char *mqi_get_column_name(mqi_handle_t h, int colidx)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && colidx >= 0, NULL);
+ MDB_PREREQUISITE(dbs && ndb > 0, NULL);
+
+ GET_TABLE(tbl, ftb, h, NULL);
+
+ return ftb->get_column_name(tbl, colidx);
+}
+
+mqi_data_type_t mqi_get_column_type(mqi_handle_t h, int colidx)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && colidx >= 0, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->get_column_type(tbl, colidx);
+}
+
+int mqi_get_column_size(mqi_handle_t h, int colidx)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && colidx >= 0, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->get_column_size(tbl, colidx);
+}
+
+int mqi_print_rows(mqi_handle_t h, char *buf, int len)
+{
+ mqi_db_functbl_t *ftb;
+ void *tbl;
+
+ MDB_CHECKARG(h != MDB_HANDLE_INVALID && buf && len > 0, -1);
+ MDB_PREREQUISITE(dbs && ndb > 0, -1);
+
+ GET_TABLE(tbl, ftb, h, -1);
+
+ return ftb->print_rows(tbl, buf, len);
+}
+
+
+
+static int db_register(const char *engine,
+ uint32_t flags,
+ mqi_db_functbl_t *functbl)
+{
+ mqi_db_t *db;
+ int i;
+
+ MDB_CHECKARG(engine && engine[0] && functbl, -1);
+ MDB_PREREQUISITE(dbs, -1);
+
+ if (ndb + 1 >= MAX_DB) {
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ for (i = 0; i < ndb; i++) {
+ if (!strcmp(engine, dbs[i].engine)) {
+ errno = EEXIST;
+ return -1;
+ }
+ }
+
+ db = dbs + ndb++;
+
+ db->engine = strdup(engine);
+ db->flags = flags;
+ db->functbl = functbl;
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+pkglib_LTLIBRARIES = libmql.la
+
+PARSER_PREFIX = yy_mql_
+AM_YFLAGS = -p $(PARSER_PREFIX)
+LEX_OUTPUT_ROOT = ./lex.$(PARSER_PREFIX)
+BUILT_SOURCES = mql-scanner.c mql-parser.c
+
+LINKER_SCRIPT = linker-script.mql
+QUIET_GEN = $(Q:@=@echo ' GEN '$@;)
+
+libmql_la_CFLAGS = -I../include
+
+libmql_ladir = \
+ $(includedir)/murphy-db
+
+libmql_la_HEADERS = \
+ ../include/murphy-db/mql.h \
+ ../include/murphy-db/mql-statement.h \
+ ../include/murphy-db/mql-result.h \
+ ../include/murphy-db/mql-trigger.h
+
+libmql_la_SOURCES = \
+ $(libmql_la_HEADERS) \
+ mql-scanner.l mql-parser.y \
+ statement.c result.c trigger.c transaction.c
+
+libmql_la_LDFLAGS = \
+ -Wl,-version-script=$(LINKER_SCRIPT)
+# -version-info @MURPHYDB_VERSION_INFO@
+
+libmql_la_LIBADD = ../mqi/libmqi.la ../mdb/libmdb.la
+
+libmql_la_DEPENDENCIES = $(LINKER_SCRIPT)
+
+
+mql-parser.h mql-parser.c: mql-parser.y
+ $(YACCCOMPILE) $<
+ mv -f y.tab.h mql-parser.h
+ mv -f y.tab.c mql-parser.c
+
+mql-scanner.c: mql-scanner.l mql-parser.c
+ $(LEXCOMPILE) $<
+ mv lex.$(PARSER_PREFIX).c $@
+
+clean-parser:
+ -rm -f mql-parser.[hc] *.tab.[hc]
+
+clean-scanner:
+ -rm -f mql-scanner.c
+
+# linker script generation
+$(LINKER_SCRIPT): $(libmql_la_HEADERS)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -P "$(CC)" -c "$(libmql_la_CFLAGS)" -p "^mql_" -o $@ $^
+
+clean-$(LINKER_SCRIPT):
+ -rm -f $(LINKER_SCRIPT)
+
+# cleanup
+clean-local:: clean-parser clean-scanner # clean-$(LINKER_SCRIPT)
+ rm -f *~
--- /dev/null
+%{
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <alloca.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/mqi.h>
+#include <murphy-db/mql.h>
+
+#define MQL_SUCCESS \
+ do { \
+ if (mode == mql_mode_exec) \
+ result = mql_result_success_create(); \
+ } while (0)
+
+#define MQL_ERROR(code, fmt...) \
+ do { \
+ switch (mode) { \
+ case mql_mode_exec: \
+ result = mql_result_error_create(code, fmt); \
+ break; \
+ case mql_mode_precompile: \
+ errno = code; \
+ free(statement); \
+ statement = NULL; \
+ break; \
+ case mql_mode_parser: \
+ fprintf(mqlout, "%s:%d: error: ", file, yy_mql_lineno); \
+ fprintf(mqlout, fmt); \
+ fprintf(mqlout, "\n"); \
+ break; \
+ } \
+ YYERROR; \
+ } while (0)
+
+
+#define SET_INPUT(t,v) \
+ input_t *input; \
+ if (ninput >= MQI_COLUMN_MAX) \
+ MQL_ERROR(EOVERFLOW, "Too many input values\n"); \
+ input = inputs + ninput++; \
+ input->type = mqi_##t; \
+ input->flags = 0; \
+ input->value.t = (v)
+
+typedef enum mql_mode_e mql_mode_t;
+typedef struct input_s input_t;
+
+enum mql_mode_e {
+ mql_mode_parser,
+ mql_mode_exec,
+ mql_mode_precompile,
+};
+
+struct input_s {
+ mqi_data_type_t type;
+ uint32_t flags;
+ union {
+ char *varchar;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+ } value;
+};
+
+extern int yy_mql_lineno;
+extern int yy_mql_lex(void);
+
+
+void yy_mql_error(const char *);
+
+static int set_select_variables(int *, mqi_data_type_t *, int *, char *,int);
+static void print_query_result(mqi_column_desc_t *, mqi_data_type_t *,
+ int *, int, int, void *);
+
+static mqi_handle_t table;
+static uint32_t table_flags;
+
+static char *trigger_name;
+static struct mql_callback_s *callback;
+
+static mqi_column_def_t coldefs[MQI_COLUMN_MAX + 1];
+static mqi_column_def_t *coldef = coldefs;
+
+static char *colnams[MQI_COLUMN_MAX + 1];
+static int ncolnam;
+
+static mqi_cond_entry_t conds[MQI_COND_MAX + 1];
+static mqi_cond_entry_t *cond = conds;
+static int binds;
+
+static input_t inputs[MQI_COLUMN_MAX];
+static int ninput;
+
+static mqi_column_desc_t coldescs[MQI_COLUMN_MAX + 1];
+static int ncoldesc;
+
+static char *strs[256];
+static int nstr;
+
+static int32_t ints[256];
+static int nint;
+
+static uint32_t uints[32];
+static int nuint;
+
+static double floats[256];
+static int nfloat;
+
+static mql_mode_t mode;
+
+static mql_statement_t *statement;
+
+static mql_result_type_t rtype;
+static mql_result_t *result;
+
+static char *file;
+static const char *mqlbuf;
+static int mqlin;
+static FILE *mqlout;
+
+%}
+
+%union {
+ mqi_data_type_t type;
+ char *string;
+ long long int number;
+ double floating;
+ int integer;
+ bool boolean;
+};
+
+
+%defines
+
+%token <string> TKN_SHOW
+%token <string> TKN_BEGIN
+%token <string> TKN_COMMIT
+%token <string> TKN_ROLLBACK
+%token <string> TKN_TRANSACTION
+%token <string> TKN_TRANSACTIONS
+%token <string> TKN_CREATE
+%token <string> TKN_UPDATE
+%token <string> TKN_REPLACE
+%token <string> TKN_DELETE
+%token <string> TKN_DROP
+%token <string> TKN_DESCRIBE
+%token <string> TKN_TABLE
+%token <string> TKN_TABLES
+%token <string> TKN_INDEX
+%token <string> TKN_ROWS
+%token <string> TKN_COLUMN
+%token <string> TKN_TRIGGER
+%token <string> TKN_INSERT
+%token <string> TKN_SELECT
+%token <string> TKN_INTO
+%token <string> TKN_FROM
+%token <string> TKN_WHERE
+%token <string> TKN_VALUES
+%token <string> TKN_SET
+%token <string> TKN_ON
+%token <string> TKN_IN
+%token <string> TKN_OR
+%token <string> TKN_PERSISTENT
+%token <string> TKN_TEMPORARY
+%token <string> TKN_CALLBACK
+%token <string> TKN_VARCHAR
+%token <string> TKN_INTEGER
+%token <string> TKN_UNSIGNED
+%token <string> TKN_REAL
+%token <string> TKN_BLOB
+%token <integer> TKN_PARAMETER
+%token <string> TKN_LOGICAL_AND
+%token <string> TKN_LOGICAL_OR
+%token <string> TKN_LESS
+%token <string> TKN_LESS_OR_EQUAL
+%token <string> TKN_EQUAL
+%token <string> TKN_GREATER_OR_EQUAL
+%token <string> TKN_GREATER
+%token <string> TKN_NOT
+%token <string> TKN_LEFT_PAREN
+%token <string> TKN_RIGHT_PAREN
+%token <string> TKN_COMMA
+%token <string> TKN_SEMICOLON
+%token <string> TKN_PLUS
+%token <string> TKN_MINUS
+%token <string> TKN_STAR
+%token <string> TKN_SLASH
+%token <number> TKN_NUMBER
+%token <floating> TKN_FLOATING
+%token <string> TKN_IDENTIFIER
+%token <string> TKN_QUOTED_STRING
+
+%type <boolean> optional_trigger_select
+
+%type <integer> insert
+%type <integer> insert_or_replace
+%type <integer> insert_option
+
+%type <integer> varchar
+%type <integer> blob
+%type <integer> sign
+
+%type <floating> floating_value
+
+%start statement_list
+
+%code requires {
+ #include <murphy-db/mqi.h>
+ #include <murphy-db/mql.h>
+
+ typedef struct mql_callback_s mql_callback_t;
+
+ int yy_mql_input(void *, unsigned);
+
+ mql_statement_t *mql_make_show_tables_statement(uint32_t);
+ mql_statement_t *mql_make_describe_statement(mqi_handle_t);
+ mql_statement_t *mql_make_transaction_statement(mql_statement_type_t,
+ char *);
+ mql_statement_t *mql_make_insert_statement(mqi_handle_t, int, int,
+ mqi_data_type_t*,
+ mqi_column_desc_t*, void*);
+ mql_statement_t *mql_make_update_statement(mqi_handle_t, int,
+ mqi_cond_entry_t *, int,
+ mqi_data_type_t *,
+ mqi_column_desc_t *, void *);
+ mql_statement_t *mql_make_delete_statement(mqi_handle_t, int,
+ mqi_cond_entry_t *);
+ mql_statement_t *mql_make_select_statement(mqi_handle_t, int, int,
+ mqi_cond_entry_t *, int,
+ char **, mqi_data_type_t *,
+ int *, mqi_column_desc_t *);
+
+ mql_result_t *mql_result_success_create(void);
+ mql_result_t *mql_result_error_create(int, const char *, ...);
+ mql_result_t *mql_result_event_column_change_create(mqi_handle_t, int,
+ mqi_change_value_t *,
+ mql_result_t *);
+ mql_result_t *mql_result_event_row_change_create(mqi_event_type_t,
+ mqi_handle_t,
+ mql_result_t *);
+ mql_result_t *mql_result_event_table_create(mqi_event_type_t,mqi_handle_t);
+ mql_result_t *mql_result_event_transaction_create(mqi_event_type_t);
+ mql_result_t *mql_result_columns_create(int, mqi_column_def_t *);
+ mql_result_t *mql_result_rows_create(int, mqi_column_desc_t*,
+ mqi_data_type_t*,int*,int,int,void*);
+ mql_result_t *mql_result_string_create_table_list(int, char **);
+ mql_result_t *mql_result_string_create_column_change(const char *,
+ const char *,
+ mqi_change_value_t *,
+ mql_result_t *);
+ mql_result_t *mql_result_string_create_row_change(mqi_event_type_t,
+ const char *,
+ mql_result_t *);
+ mql_result_t *mql_result_string_create_table_change(mqi_event_type_t,
+ const char *);
+ mql_result_t *mql_result_string_create_transaction_change(
+ mqi_event_type_t);
+ mql_result_t *mql_result_string_create_column_list(int, mqi_column_def_t*);
+ mql_result_t *mql_result_string_create_row_list(int, char **,
+ mqi_column_desc_t *,
+ mqi_data_type_t *, int *,
+ int, int, void *);
+ mql_result_t *mql_result_list_create(mqi_data_type_t, int, void *);
+
+ mql_callback_t *mql_find_callback(char *);
+ int mql_create_column_trigger(char *, mqi_handle_t, int,mqi_data_type_t,
+ mql_callback_t *,
+ int, char **, mqi_column_desc_t *,
+ mqi_data_type_t *, int *,
+ int);
+ int mql_create_row_trigger(char *, mqi_handle_t, mql_callback_t *,
+ int, char **, mqi_column_desc_t *,
+ mqi_data_type_t *, int *, int);
+ int mql_create_table_trigger(char *, mql_callback_t *);
+ int mql_create_transaction_trigger(char *, mql_callback_t *);
+
+ int mql_begin_transaction(char *);
+ int mql_rollback_transaction(char *);
+ int mql_commit_transaction(char *);
+}
+
+
+%%
+
+/*#toplevel#*/
+statement_list:
+ statement
+| statement_list semicolon statement
+;
+
+semicolon: TKN_SEMICOLON {
+ if (mode != mql_mode_parser) {
+ result = mql_result_error_create(EINVAL, "multiple MQL statements");
+ YYERROR;
+ }
+};
+
+/*#toplevel#*/
+statement:
+ show_statement
+| create_statement
+| drop_statement
+| begin_statement
+| commit_statement
+| rollback_statement
+| describe_statement
+| insert_statement
+| update_statement
+| delete_statement
+| select_statement
+| error
+;
+
+/***************************
+ *
+ * Show statement
+ *
+ */
+/*#toplevel#*/
+show_statement:
+ show_table_statement
+;
+
+show_table_statement: TKN_SHOW show_tables
+;
+
+show_tables: table_flags TKN_TABLES {
+ char *names[4096];
+ int n;
+
+ if (mode == mql_mode_precompile)
+ statement = mql_make_show_tables_statement(table_flags);
+ else {
+ if ((n = mqi_show_tables(table_flags, names,MQI_DIMENSION(names))) < 0)
+ MQL_ERROR(errno, "can't show tables: %s", strerror(errno));
+ else {
+ if (mode == mql_mode_exec) {
+ switch (rtype) {
+ case mql_result_string:
+ result = mql_result_string_create_table_list(n, names);
+ break;
+ case mql_result_list:
+ result = mql_result_list_create(mqi_string,n,(void*)names);
+ break;
+ default:
+ result = mql_result_error_create(EINVAL,
+ "can't show tables: %s",
+ strerror(EINVAL));
+ break;
+ }
+ }
+ else {
+ mql_result_t *r = mql_result_string_create_table_list(n,names);
+
+ fprintf(mqlout, "%s", mql_result_string_get(r));
+
+ mql_result_free(r);
+ }
+ }
+ }
+};
+
+/***********************************
+ *
+ * Create statement
+ *
+ */
+/*#toplevel#*/
+create_statement:
+ create_table_statement
+| create_index_statement
+| create_trigger_statement
+;
+
+/*#toplevel#*/
+create_table_statement: TKN_CREATE create_table table_definition
+;
+
+/*#toplevel#*/
+create_index_statement: TKN_CREATE create_index index_definition
+;
+
+/*#toplevel#*/
+create_trigger_statement:
+ create_transaction_trigger
+| create_table_trigger
+| create_row_trigger
+| create_column_trigger
+;
+
+
+/* create table */
+
+create_table: table_flags TKN_TABLE {
+ coldef = coldefs;
+
+ if (table_flags == MQI_ANY)
+ table_flags = MQI_TEMPORARY;
+};
+
+
+
+table_definition: TKN_IDENTIFIER TKN_LEFT_PAREN column_defs TKN_RIGHT_PAREN {
+ if (mqi_create_table($1, table_flags, NULL, coldefs) == MQI_HANDLE_INVALID)
+ MQL_ERROR(errno, "Can't create table: %s\n", strerror(errno));
+ else
+ MQL_SUCCESS;
+};
+
+/*#toplevel#*/
+column_defs:
+ column_def
+| column_defs TKN_COMMA column_def
+;
+
+/*#toplevel#*/
+column_def: column_name column_type {
+ memset(++coldef, 0, sizeof(mqi_column_def_t));
+};
+
+column_name: TKN_IDENTIFIER {
+ if ((coldef - coldefs) >= MQI_COLUMN_MAX) {
+ MQL_ERROR(EOVERFLOW, "Too many columns. Max %d columns allowed\n",
+ MQI_COLUMN_MAX);
+ }
+
+ coldef->name = $1;
+};
+
+/*#toplevel#*/
+column_type:
+ varchar { coldef->type = mqi_varchar; coldef->length = $1; }
+| TKN_INTEGER { coldef->type = mqi_integer; coldef->length = 0; }
+| TKN_UNSIGNED { coldef->type = mqi_unsignd; coldef->length = 0; }
+| TKN_REAL { coldef->type = mqi_floating; coldef->length = 0; }
+| blob { coldef->type = mqi_blob; coldef->length = $1; }
+;
+
+varchar: TKN_VARCHAR TKN_LEFT_PAREN TKN_NUMBER TKN_RIGHT_PAREN {
+ $$ = (int)$3;
+};
+
+blob: TKN_BLOB TKN_LEFT_PAREN TKN_NUMBER TKN_RIGHT_PAREN {
+ $$ = (int)$3;
+};
+
+/* create index */
+
+create_index: TKN_INDEX {
+ ncolnam = 0;
+};
+
+index_definition: TKN_ON table_name TKN_LEFT_PAREN column_list TKN_RIGHT_PAREN
+{
+ colnams[ncolnam] = NULL;
+
+ if (mqi_create_index(table, colnams) < 0)
+ MQL_ERROR(errno, "failed to create index: %s", strerror(errno));
+ else
+ MQL_SUCCESS;
+};
+
+
+/* create trigger */
+
+/*#toplevel#*/
+create_transaction_trigger: TKN_CREATE create_trigger transaction_trigger
+;
+
+/*#toplevel#*/
+create_table_trigger: TKN_CREATE create_trigger table_trigger
+;
+
+/*#toplevel#*/
+create_row_trigger: TKN_CREATE create_trigger row_trigger
+;
+
+/*#toplevel#*/
+create_column_trigger: TKN_CREATE create_trigger column_trigger
+;
+
+create_trigger: TKN_TRIGGER TKN_IDENTIFIER TKN_ON {
+ if (mode != mql_mode_exec)
+ MQL_ERROR(EPERM, "only mql_exec_string() can create triggers");
+ else {
+ table = MQI_HANDLE_INVALID;
+ ncolnam = 0;
+ trigger_name = $2;
+ callback = NULL;
+ }
+};
+
+
+
+transaction_trigger: TKN_TRANSACTIONS callback {
+
+ if (mql_create_transaction_trigger(trigger_name, callback) < 0) {
+ MQL_ERROR(errno, "failed to create transaction trigger: %s",
+ strerror(errno));
+ }
+ else {
+ MQL_SUCCESS;
+ }
+};
+
+table_trigger: TKN_TABLES callback {
+
+ if (mql_create_table_trigger(trigger_name, callback) < 0)
+ MQL_ERROR(errno, "failed to create table trigger: %s",strerror(errno));
+ else
+ MQL_SUCCESS;
+
+};
+
+row_trigger: TKN_ROWS TKN_IN table_name callback trigger_select {
+
+ int rowsize;
+ int colsizes[MQI_COLUMN_MAX + 1];
+ mqi_data_type_t coltypes[MQI_COLUMN_MAX + 1];
+ char errbuf[256];
+ int sts;
+
+ sts = set_select_variables(&rowsize, coltypes,colsizes,
+ errbuf, sizeof(errbuf));
+ if (sts < 0)
+ MQL_ERROR(errno, "%s", errbuf);
+
+ sts = mql_create_row_trigger(trigger_name, table, callback,
+ ncolnam,colnams,
+ coldescs, coltypes, colsizes,
+ rowsize);
+ if (sts < 0)
+ MQL_ERROR(errno, "failed to create row triger: %s",strerror(errno));
+ else
+ MQL_SUCCESS;
+};
+
+column_trigger: TKN_COLUMN TKN_IDENTIFIER TKN_IN table_name callback
+ optional_trigger_select
+{
+ int colidx;
+ mqi_data_type_t coltype;
+ int rowsize;
+ int colsizes[MQI_COLUMN_MAX + 1];
+ mqi_data_type_t coltypes[MQI_COLUMN_MAX + 1];
+ char errbuf[256];
+ int sts;
+
+ if ((colidx = mqi_get_column_index(table, $2)) < 0 ||
+ (coltype = mqi_get_column_type(table, colidx)) < 0 )
+ {
+ MQL_ERROR(errno, "do not know trigger column '%s'", $2);
+ }
+
+ if ($6) {
+ sts = set_select_variables(&rowsize, coltypes,colsizes,
+ errbuf, sizeof(errbuf));
+ if (sts < 0)
+ MQL_ERROR(errno, "%s", errbuf);
+
+ sts = mql_create_column_trigger(trigger_name,
+ table, colidx,coltype, callback,
+ ncolnam,colnams,
+ coldescs,coltypes,colsizes,
+ rowsize);
+ }
+ else {
+ sts = mql_create_column_trigger(trigger_name,
+ table, colidx,coltype, callback,
+ 0,NULL,NULL,NULL,NULL, 0);
+ }
+
+ if (sts < 0)
+ MQL_ERROR(errno,"failed to create column trigger: %s",strerror(errno));
+ else
+ MQL_SUCCESS;
+};
+
+
+callback: TKN_CALLBACK TKN_IDENTIFIER {
+ if (!(callback = mql_find_callback($2))) {
+ MQL_ERROR(ENOENT, "can't find callback '%s'", $2);
+ }
+};
+
+trigger_select: TKN_SELECT columns
+;
+
+optional_trigger_select:
+ /* no select */ { $$ = false; }
+| trigger_select { $$ = true; }
+;
+
+/***********************************
+ *
+ * Drop statement
+ *
+ */
+/*#toplevel#*/
+drop_statement:
+ drop_table_statement
+| drop_index_statement
+;
+
+/* drop table */
+
+/*#toplevel#*/
+drop_table_statement: TKN_DROP TKN_TABLE table_name {
+ if (mqi_drop_table(table) < 0)
+ MQL_ERROR(errno, "failed to drop table: %s", strerror(errno));
+ else
+ MQL_SUCCESS;
+}
+;
+
+
+/* drop index */
+
+/*#toplevel#*/
+drop_index_statement: TKN_DROP TKN_INDEX table_name {
+};
+
+
+/***********************************
+ *
+ * Begin/Commit/Rollback statement
+ *
+ */
+/*#toplevel#*/
+begin_statement: TKN_BEGIN transaction TKN_IDENTIFIER {
+ if (mode == mql_mode_precompile)
+ statement = mql_make_transaction_statement(mql_statement_begin, $3);
+ else {
+ if (mql_begin_transaction($3) < 0)
+ MQL_ERROR(errno, "can't start transaction: %s", strerror(errno));
+ else
+ MQL_SUCCESS;
+ }
+};
+
+/*#toplevel#*/
+commit_statement: TKN_COMMIT transaction TKN_IDENTIFIER {
+ if (mode == mql_mode_precompile)
+ statement = mql_make_transaction_statement(mql_statement_commit, $3);
+ else {
+ if (mql_commit_transaction($3) < 0)
+ MQL_ERROR(errno, "can't commit transaction: %s", strerror(errno));
+ else
+ MQL_SUCCESS;
+ }
+};
+
+/*#toplevel#*/
+rollback_statement: TKN_ROLLBACK transaction TKN_IDENTIFIER {
+ if (mode == mql_mode_precompile)
+ statement = mql_make_transaction_statement(mql_statement_rollback, $3);
+ else {
+ if (mql_rollback_transaction($3) < 0)
+ MQL_ERROR(errno, "can't rollback transaction: %s",strerror(errno));
+ else
+ MQL_SUCCESS;
+ }
+};
+
+
+
+/***********************************
+ *
+ * Describe statement
+ *
+ */
+/*#toplevel#*/
+describe_statement: TKN_DESCRIBE table_name {
+ mqi_column_def_t defs[MQI_COLUMN_MAX];
+ int n;
+
+ if (mode == mql_mode_precompile)
+ statement = mql_make_describe_statement(table);
+ else {
+ if ((n = mqi_describe(table, defs, MQI_COLUMN_MAX)) < 0)
+ MQL_ERROR(errno, "can't describe table: %s", strerror(errno));
+ else {
+ if (mode == mql_mode_exec) {
+ switch (rtype) {
+ case mql_result_columns:
+ result = mql_result_columns_create(n, defs);
+ break;
+ case mql_result_string:
+ result = mql_result_string_create_column_list(n, defs);
+ break;
+ default:
+ result = mql_result_error_create(EINVAL, "describe failed:"
+ " invalid result type %d",
+ rtype);
+ break;
+ }
+ }
+ else {
+ mql_result_t *r = mql_result_string_create_column_list(n,defs);
+
+ fprintf(mqlout, "%s", mql_result_string_get(r));
+
+ mql_result_free(r);
+ }
+ }
+ }
+};
+
+/***********************************
+ *
+ * Insert statement
+ *
+ */
+/*#toplevel#*/
+insert_statement: insert table_name insert_columns TKN_VALUES insert_values {
+ void *row[2];
+ char *col;
+ mqi_column_desc_t *cd;
+ mqi_data_type_t coltypes[MQI_COLUMN_MAX + 1];
+ input_t *inp;
+ mqi_data_type_t type;
+ int cindex;
+ int err;
+ int i;
+
+ if (!ncolnam) {
+ while ((colnams[ncolnam] = mqi_get_column_name(table, ncolnam)))
+ ncolnam++;
+ }
+
+ if (ncolnam != ninput)
+ MQL_ERROR(EINVAL, "unbalanced set of columns and values");
+
+ for (i = 0, err = 0; i < ncolnam; i++) {
+ col = colnams[i];
+ cd = coldescs + i;
+ inp = inputs + i;
+
+ if ((cindex = mqi_get_column_index(table, col)) < 0) {
+ MQL_ERROR(ENOENT, "know nothing about '%s'", col);
+ err = 1;
+ continue;
+ }
+
+ type = coltypes[i] = mqi_get_column_type(table, cindex);
+
+ if (type != inp->type) {
+ if (type != mqi_integer ||
+ inp->type != mqi_unsignd ||
+ inp->value.unsignd > INT32_MAX)
+ {
+ MQL_ERROR(EINVAL, "mismatching column and value type for '%s'",
+ col);
+ err = 1;
+ continue;
+ }
+ }
+
+ cd->cindex = cindex;
+ cd->offset = (void *)&inp->value - (void *)inputs;
+ }
+
+ cd = coldescs + i;
+ cd->cindex = -1;
+ cd->offset = -1;
+
+
+ if (mode == mql_mode_precompile) {
+ statement = mql_make_insert_statement(table, $1, ncolnam, coltypes,
+ coldescs, inputs);
+ }
+ else {
+ row[0] = (void *)inputs;
+ row[1] = NULL;
+
+ if (err || mqi_insert_into(table, $1, coldescs, row) < 0)
+ MQL_ERROR(errno, "insert failed: %s\n", strerror(errno));
+ else
+ MQL_SUCCESS;
+ }
+};
+
+
+insert: insert_or_replace {
+ table = MQI_HANDLE_INVALID;
+ ncolnam = 0;
+ ninput = 0;
+ ncoldesc = 0;
+ $$ = $1;
+};
+
+insert_or_replace:
+ TKN_INSERT insert_option TKN_INTO { $$ = $2; }
+| TKN_REPLACE TKN_INTO { $$ = 1; }
+;
+
+insert_option:
+ /* no option */ { $$ = 0; }
+| TKN_OR TKN_REPLACE { $$ = 1; }
+/*
+| TKN_IGNORE { $$ = 1; }
+*/
+;
+
+
+insert_columns:
+ /* all columns: leaves ncolnam as zero */
+| TKN_LEFT_PAREN column_list TKN_RIGHT_PAREN
+;
+
+insert_values: TKN_LEFT_PAREN input_value_list TKN_RIGHT_PAREN;
+
+/*#toplevel#*/
+input_value_list:
+ input_value
+| input_value_list TKN_COMMA input_value
+;
+
+
+
+/***********************************
+ *
+ * Update statement
+ *
+ */
+/*#toplevel#*/
+update_statement: update table_name TKN_SET assignment_list where_clause {
+ mqi_column_desc_t *cd = coldescs + ninput;
+ mqi_cond_entry_t *where = (cond == conds) ? NULL : conds;
+ mqi_data_type_t coltypes[MQI_COLUMN_MAX + 1];
+ int i;
+
+ if (!ninput)
+ MQL_ERROR(ENOMEDIUM, "No column to update");
+
+ cd->cindex = -1;
+ cd->offset = -1;
+
+ if (mode == mql_mode_precompile) {
+ for (i = 0; i < ninput; i++)
+ coltypes[i] = inputs[i].type;
+
+ statement = mql_make_update_statement(table, cond - conds, conds,
+ ninput, coltypes, coldescs,
+ inputs);
+ }
+ else {
+ if (mqi_update(table, where, coldescs, inputs) < 0)
+ MQL_ERROR(errno, "update failed: %s", strerror(errno));
+ else
+ MQL_SUCCESS;
+ }
+};
+
+update: TKN_UPDATE {
+ table = MQI_HANDLE_INVALID;
+ ninput = 0;
+ ncoldesc = 0;
+ nstr = 0;
+ nint = 0;
+ nuint = 0;
+ nfloat = 0;
+ cond = conds;
+ binds = 0;
+};
+
+
+/*#toplevel#*/
+assignment_list:
+ assignment
+| assignment_list TKN_COMMA assignment
+;
+
+/*#toplevel#*/
+assignment: TKN_IDENTIFIER TKN_EQUAL input_value {
+ int i = ninput - 1;
+ input_t *inp = inputs + i;
+ mqi_column_desc_t *cd = coldescs + i;
+ int cindex;
+ int offset;
+ mqi_data_type_t type;
+
+ if ((cindex = mqi_get_column_index(table, $1)) < 0)
+ MQL_ERROR(ENOENT, "know nothing about '%s'", $1);
+
+ if ((inp->flags & MQL_BINDABLE))
+ offset = -(MQL_BIND_INDEX(inp->flags) + 1);
+ else {
+ if ((type = mqi_get_column_type(table, cindex)) != inp->type) {
+ if (type != mqi_integer ||
+ inp->type != mqi_unsignd ||
+ inp->value.unsignd > INT32_MAX)
+ {
+ MQL_ERROR(EINVAL, "mismatching column and value type "
+ "for '%s'",$1);
+ }
+ }
+ offset = (void *)&inp->value - (void *)inputs;
+ }
+
+ cd->cindex = cindex;
+ cd->offset = offset;
+};
+
+
+
+/***********************************
+ *
+ * Delete statement
+ *
+ */
+/*#toplevel#*/
+delete_statement: delete table_name where_clause {
+ mqi_cond_entry_t *where = (cond == conds) ? NULL : conds;
+
+ if (mode == mql_mode_precompile)
+ statement = mql_make_delete_statement(table, cond - conds, where);
+ else {
+ if (mqi_delete_from(table, where) < 0)
+ MQL_ERROR(errno, "delete failed: %s", strerror(errno));
+ else
+ MQL_SUCCESS;
+ }
+};
+
+delete: TKN_DELETE TKN_FROM {
+ table = MQI_HANDLE_INVALID;
+ nstr = 0;
+ nint = 0;
+ nuint = 0;
+ nfloat = 0;
+ cond = conds;
+ binds = 0;
+};
+
+/***********************************
+ *
+ * Select statement
+ *
+ */
+/*#toplevel#*/
+select_statement: select columns TKN_FROM table_name where_clause {
+ int colsizes[MQI_COLUMN_MAX + 1];
+ mqi_data_type_t coltypes[MQI_COLUMN_MAX + 1];
+ mqi_cond_entry_t *where;
+ int rowsize;
+ int tsiz;
+ size_t rsiz;
+ void *rows;
+ char errbuf[256];
+ int sts;
+ int n;
+
+
+ if ((tsiz = mqi_get_table_size(table)) < 0)
+ MQL_ERROR(errno, "can't get table size: %s", strerror(errno));
+
+
+ sts = set_select_variables(&rowsize, coltypes,colsizes,
+ errbuf, sizeof(errbuf));
+ if (sts < 0)
+ MQL_ERROR(errno, "%s", errbuf);
+
+
+ if (mode != mql_mode_precompile && mode != mql_mode_exec && !tsiz) {
+ if (mode == mql_mode_parser)
+ fprintf(mqlout, "no rows\n");
+ }
+ else {
+ rsiz = tsiz * rowsize;
+ rows = alloca(rsiz);
+ where = (cond == conds) ? NULL : conds;
+
+ if (mode != mql_mode_precompile) {
+ if (tsiz != 0) {
+ if ((n = mqi_select(table, where,
+ coldescs, rows, rowsize, tsiz)) < 0)
+ MQL_ERROR(errno, "select failed: %s", strerror(errno));
+ }
+ else
+ n = 0;
+ }
+
+ switch (mode) {
+ case mql_mode_parser:
+ fprintf(mqlout, "Selected %d rows:\n", n);
+ print_query_result(coldescs, coltypes, colsizes, n, rowsize, rows);
+ break;
+ case mql_mode_exec:
+ if (rtype == mql_result_rows) {
+ result = mql_result_rows_create(ncolnam, coldescs, coltypes,
+ colsizes, n, rowsize, rows);
+ }
+ else {
+ result = mql_result_string_create_row_list(ncolnam, colnams,
+ coldescs, coltypes,
+ colsizes,
+ n, rowsize, rows);
+ }
+ break;
+ case mql_mode_precompile:
+ statement = mql_make_select_statement(table, rowsize,
+ cond - conds, where,
+ ncolnam, colnams, coltypes,
+ colsizes, coldescs);
+ break;
+ }
+ }
+};
+
+
+select: TKN_SELECT {
+ table = MQI_HANDLE_INVALID;
+ ncolnam = 0;
+ nstr = 0;
+ nint = 0;
+ nuint = 0;
+ nfloat = 0;
+ cond = conds;
+ binds = 0;
+};
+
+columns:
+ TKN_STAR
+| column_list
+;
+
+
+/***********************************
+ *
+ * Transaction
+ *
+ */
+transaction:
+ /* no token */
+| TKN_TRANSACTION
+;
+
+/***********************************
+ *
+ * Table name
+ *
+ */
+table_name: TKN_IDENTIFIER {
+ if ((table = mqi_get_table_handle($1)) == MQI_HANDLE_INVALID)
+ MQL_ERROR(errno, "Do not know anything about '%s'", $1);
+};
+
+/***********************************
+ *
+ * Table flags
+ *
+ */
+table_flags:
+ /* no option */ { table_flags = MQI_ANY; }
+| TKN_PERSISTENT { table_flags = MQI_PERSISTENT; }
+| TKN_TEMPORARY { table_flags = MQI_TEMPORARY; }
+;
+
+/***********************************
+ *
+ * Column list
+ *
+ */
+/*#toplevel#*/
+column_list:
+ column
+| column_list TKN_COMMA column
+;
+
+column: TKN_IDENTIFIER {
+ if (ncolnam < MQI_COLUMN_MAX)
+ colnams[ncolnam++] = $1;
+ else
+ MQL_ERROR(EOVERFLOW, "Too many columns");
+};
+
+/***********************************
+ *
+ * Input value
+ *
+ */
+/*#toplevel#*/
+input_value:
+ string_input
+| integer_input
+| unsigned_input
+| floating_input
+| parameter_input
+;
+
+string_input: TKN_QUOTED_STRING { SET_INPUT(varchar, $1); };
+integer_input: sign TKN_NUMBER { SET_INPUT(integer, $1 * $2); };
+unsigned_input: TKN_NUMBER { SET_INPUT(unsignd, $1); };
+floating_input: TKN_FLOATING { SET_INPUT(floating, $1); }
+| sign TKN_FLOATING { SET_INPUT(floating, (double)$1 * $2); };
+
+parameter_input: TKN_PARAMETER {
+ input_t *input;
+
+ if (mode != mql_mode_precompile) {
+ MQL_ERROR(EINVAL, "parameters are allowed only in "
+ "precompilation mode");
+ }
+ if (binds >= MQL_PARAMETER_MAX) {
+ MQL_ERROR(EOVERFLOW, "number of parameters exceeds %d",
+ MQL_PARAMETER_MAX);
+ }
+
+ input = inputs + ninput++;
+ input->type = $1;
+ input->flags = MQL_BINDABLE | MQL_BIND_INDEX(binds++);
+
+ memset(&input->value, 0, sizeof(input->value));
+};
+
+
+/***********************************
+ *
+ * Where clause
+ *
+ */
+where_clause:
+ /* no where clause */ {
+ }
+| TKN_WHERE conditional_expression {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_end;
+ cond++;
+ };
+
+
+/*#toplevel#*/
+conditional_expression:
+ relational_expression
+| relational_expression logical_operator relational_expression
+;
+
+/*#toplevel#*/
+relational_expression: value relational_operator value;
+
+/*#toplevel#*/
+value:
+ column_value
+| string_variable
+| integer_variable
+| unsigned_variable
+| floating_variable
+| parameter_value
+| expression_value
+| unary_operator value
+;
+
+column_value: TKN_IDENTIFIER {
+ int cx;
+
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+
+ if ((cx = mqi_get_column_index(table,$1)) < 0)
+ MQL_ERROR(ENOENT, "no column with name '%s'", $1);
+
+ cond->type = mqi_column;
+ cond->u.column = cx;
+ cond++;
+};
+
+string_variable: TKN_QUOTED_STRING {
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ strs[nstr] = $1;
+ cond->type = mqi_variable;
+ cond->u.variable.flags = 0;
+ cond->u.variable.type = mqi_varchar;
+ cond->u.variable.v.varchar = strs + nstr++;
+ cond++;
+};
+
+integer_variable: sign TKN_NUMBER {
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ ints[nint] = $1 * $2;
+ cond->type = mqi_variable;
+ cond->u.variable.type = mqi_integer;
+ cond->u.variable.v.integer = ints + nint++;
+ cond++;
+};
+
+unsigned_variable: TKN_NUMBER {
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ uints[nuint] = $1;
+ cond->type = mqi_variable;
+ cond->u.variable.flags = 0;
+ cond->u.variable.type = mqi_unsignd;
+ cond->u.variable.v.unsignd = uints + nuint++;
+ cond++;
+};
+
+floating_variable: floating_value {
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ floats[nfloat] = $1;
+ cond->type = mqi_variable;
+ cond->u.variable.flags = 0;
+ cond->u.variable.type = mqi_floating;
+ cond->u.variable.v.floating = floats + nfloat++;
+ cond++;
+};
+
+floating_value:
+ TKN_FLOATING { return $1; }
+| sign TKN_FLOATING { return (double)$1 * $2; }
+
+
+parameter_value: TKN_PARAMETER {
+ if (mode != mql_mode_precompile) {
+ MQL_ERROR(EINVAL, "parameters are allowed only in "
+ "precompilation mode");
+ }
+ if (binds >= MQL_PARAMETER_MAX) {
+ MQL_ERROR(EOVERFLOW, "number of parameters exceeds %d",
+ MQL_PARAMETER_MAX);
+ }
+ if (cond - conds >= MQI_COND_MAX) {
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ }
+ cond->type = mqi_variable;
+ cond->u.variable.flags = MQL_BINDABLE | MQL_BIND_INDEX(binds++);
+ cond->u.variable.type = $1;
+ cond->u.variable.v.generic = NULL;
+ cond++;
+};
+
+expression_value:
+ TKN_LEFT_PAREN {
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_begin;
+ cond++;
+ }
+ conditional_expression
+ TKN_RIGHT_PAREN {
+ if (cond - conds >= MQI_COND_MAX)
+ MQL_ERROR(EOVERFLOW, "too complex condition");
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_end;
+ cond++;
+ }
+;
+
+
+sign:
+ TKN_PLUS { $$ = +1; }
+| TKN_MINUS { $$ = -1; }
+;
+
+
+unary_operator:
+ TKN_NOT {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_not;
+ cond++;
+ }
+;
+
+relational_operator:
+ TKN_LESS {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_less;
+ cond++;
+ }
+| TKN_LESS_OR_EQUAL {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_leq;
+ cond++;
+ }
+| TKN_EQUAL {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_eq;
+ cond++;
+ }
+| TKN_GREATER_OR_EQUAL {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_geq;
+ cond++;
+ }
+| TKN_GREATER {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_gt;
+ cond++;
+ }
+;
+
+logical_operator:
+ TKN_LOGICAL_AND {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_and;
+ cond++;
+ }
+| TKN_LOGICAL_OR {
+ cond->type = mqi_operator;
+ cond->u.operator_ = mqi_or;
+ cond++;
+ }
+;
+
+
+%%
+
+
+int mql_exec_file(const char *path)
+{
+ char buf[1024];
+ int sts;
+
+ mode = mql_mode_parser;
+ rtype = mql_result_unknown;
+ mqlbuf = NULL;
+ mqlout = stderr;
+
+ if (!path) {
+ mqlin = fileno(stdin);
+ sts = yy_mql_parse() ? -1 : 0;
+ }
+ else {
+ strncpy(buf, path, sizeof(buf));
+ buf[sizeof(buf)-1] = '\0';
+
+ file = basename(buf);
+
+ if ((mqlin = open(path, O_RDONLY)) < 0) {
+ sts = -1;
+ fprintf(mqlout, "could not open file '%s': %s\n",
+ path, strerror(errno));
+ }
+ else {
+ sts = yy_mql_parse() ? -1 : 0;
+ close(mqlin);
+ }
+ }
+
+ mqlin = -1;
+
+ return sts;
+}
+
+
+mql_result_t *mql_exec_string(mql_result_type_t result_type, const char *str)
+{
+ if (result_type == mql_result_dontcare)
+ result_type = mql_result_string;
+
+ MDB_CHECKARG((result_type == mql_result_event ||
+ result_type == mql_result_columns ||
+ result_type == mql_result_rows ||
+ result_type == mql_result_string ) &&
+ str, NULL);
+
+ mode = mql_mode_exec;
+ result = NULL;
+ rtype = result_type;
+ mqlbuf = str;
+
+ if (yy_mql_parse() && !result) {
+ result = mql_result_error_create(EIO, "Syntax error in '%s'", str);
+ }
+
+
+ return result;
+}
+
+mql_statement_t *mql_precompile(const char *str)
+{
+ MDB_CHECKARG(str, NULL);
+
+ mode = mql_mode_precompile;
+ rtype = mql_result_unknown;
+ statement = NULL;
+ mqlbuf = str;
+
+ yy_mql_parse();
+
+ return statement;
+}
+
+int yy_mql_input(void *dst, unsigned dstlen)
+{
+ int len = 0;
+
+ if (dst && dstlen > 0) {
+
+ if (mqlbuf) {
+ if ((len = strlen(mqlbuf)) < 1)
+ len = 0;
+ else if ((unsigned)len + 1 <= dstlen) {
+ memcpy(dst, mqlbuf, len + 1);
+ mqlbuf += len;
+ }
+ else {
+ memcpy(dst, mqlbuf, dstlen);
+ mqlbuf += dstlen;
+ }
+ }
+ else if (mqlin >= 0) {
+ while ((len = read(mqlin, dst, dstlen)) < 0) {
+ if (errno != EINTR) {
+ break;
+ }
+ }
+ }
+ }
+
+ return len;
+}
+
+
+void yy_mql_error(const char *msg)
+{
+ if (mode == mql_mode_parser)
+ fprintf(mqlout, "Error: '%s'\n", msg);
+}
+
+
+static int set_select_variables(int *rowsize,
+ mqi_data_type_t *coltypes,
+ int *colsizes,
+ char *errbuf, int elgh)
+{
+ mqi_column_desc_t *cd;
+ int i;
+ int rlgh;
+ int colsize;
+ int colidx;
+ mqi_data_type_t coltype;
+
+ if (!ncolnam) {
+ while ((colnams[ncolnam] = mqi_get_column_name(table, ncolnam)))
+ ncolnam++;
+ }
+
+ for (i = 0, rlgh = 0; i < ncolnam; i++) {
+ cd = coldescs + i;
+
+ if ((colidx = mqi_get_column_index(table, colnams[i])) < 0 ||
+ (colsize = mqi_get_column_size(table, colidx)) < 0 ||
+ (coltype = mqi_get_column_type(table, colidx)) == mqi_error)
+ {
+ snprintf(errbuf, elgh, "invalid column '%s'", colnams[i]);
+ return -1;
+ }
+ cd->cindex = colidx;
+ cd->offset = rlgh;
+
+ coltypes[i] = coltype;
+ colsizes[i] = colsize;
+
+ switch (coltype) {
+ case mqi_varchar: rlgh += sizeof(char *); break;
+ case mqi_integer: rlgh += sizeof(int32_t); break;
+ case mqi_unsignd: rlgh += sizeof(uint32_t); break;
+ case mqi_floating: rlgh += sizeof(double); break;
+ case mqi_blob: rlgh += sizeof(void *); break;
+ default: break;
+ }
+ } /* for */
+
+ cd = coldescs + i;
+ cd->cindex = -1;
+ cd->offset = -1;
+
+ *rowsize = rlgh;
+
+ return 0;
+}
+
+
+static void print_query_result(mqi_column_desc_t *coldescs,
+ mqi_data_type_t *coltypes,
+ int *colsizes,
+ int nresult,
+ int recsize,
+ void *results)
+{
+ int i, j, recoffs;
+ void *data;
+ char name[4096];
+ int clgh;
+ int clghs[MQI_COLUMN_MAX + 1];
+ int n;
+
+ for (j = 0, n = 0; j < ncolnam; j++) {
+ snprintf(name, sizeof(name), "%s", colnams[j]);
+
+ switch (coltypes[j]) {
+ case mqi_varchar: clgh = colsizes[j] - 1; break;
+ case mqi_integer: clgh = 11; break;
+ case mqi_unsignd: clgh = 10; break;
+ case mqi_floating: clgh = 10; break;
+ default: clgh = 0; break;
+ }
+
+ clghs[j] = clgh;
+
+ if (clgh < (int)sizeof(name))
+ name[clgh] = '\0';
+
+ n += fprintf(mqlout, "%s%*s", j?" ":"", clgh,name);
+
+ }
+
+ if (n > (int)sizeof(name)-1)
+ n = sizeof(name)-1;
+ memset(name, '-', n);
+ name[n] = '\0';
+
+ fprintf(mqlout, "\n%s\n", name);
+
+
+
+ for (i = 0, recoffs = 0; i < nresult; i++, recoffs += recsize) {
+ for (j = 0; j < ncolnam; j++) {
+ if (j) fprintf(mqlout, " ");
+
+ data = results + (recoffs + coldescs[j].offset);
+ clgh = clghs[j];
+
+#define PRINT(t,f) fprintf(mqlout, f, clgh, *(t *)data)
+
+ switch (coltypes[j]) {
+ case mqi_varchar: PRINT(char * , "%*s" ); break;
+ case mqi_integer: PRINT(int32_t , "%*d" ); break;
+ case mqi_unsignd: PRINT(uint32_t, "%*u" ); break;
+ case mqi_floating: PRINT(double , "%*.2lf"); break;
+ case mqi_blob: break;
+ default: break;
+ }
+
+#undef PRINT
+
+ }
+ fprintf(mqlout, "\n");
+ }
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim:set expandtab shiftwidth=4:
+ */
--- /dev/null
+%{
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mql-parser.h"
+
+#if 0
+#define DEBUG_SCANNER
+#endif
+
+#ifdef DEBUG_SCANNER
+#define PRINT(fmt, args...) printf(fmt, args)
+#else
+#define PRINT(fmt, args...)
+#endif
+
+#define YY_SKIP_YYWRAP
+#define YY_NO_INPUT
+
+#define EOF_TOKEN \
+ YY_FLUSH_BUFFER; \
+ yy_mql_lex_destroy(); \
+ yyterminate()
+
+#define ARGLESS_TOKEN(t) \
+ do { \
+ PRINT("%s-", #t); \
+ yy_mql_lval.string = #t; \
+ return TKN_##t; \
+ } while (0)
+
+#define STRING_TOKEN(t) \
+ do { \
+ PRINT("%s(%s)-", #t, yytext); \
+ yy_mql_lval.string = copy_to_ringbuf(yytext); \
+ return TKN_##t; \
+ } while (0)
+
+#define NUMBER_TOKEN \
+ do { \
+ yy_mql_lval.number = strtoul(yytext, NULL, 10); \
+ PRINT("NUMBER(%lld)-", yy_mql_lval.number); \
+ return TKN_NUMBER; \
+ } while(0)
+
+#define FLOATING_TOKEN \
+ do { \
+ yy_mql_lval.floating = strtod(yytext, NULL); \
+ return TKN_FLOATING; \
+ } while (0)
+
+#define SEMICOLON_TOKEN \
+ do { \
+ PRINT("%s\n", "SEMICOLON"); \
+ yy_mql_lval.string = "SEMICOLON"; \
+ return TKN_SEMICOLON; \
+ } while (0)
+
+#define PARAMETER_TOKEN \
+ do { \
+ mqi_data_type_t type; \
+ switch(yytext[1]) { \
+ case 's': type = mqi_varchar; break; \
+ case 'd': type = mqi_integer; break; \
+ case 'u': type = mqi_unsignd; break; \
+ case 'f': type = mqi_floating; break; \
+ default : type = mqi_unknown; break; \
+ } \
+ yy_mql_lval.type = type; \
+ PRINT("PARAMETER(%d)-", yy_mql_lval.type); \
+ return TKN_PARAMETER; \
+ } while(0)
+
+
+#define YY_INPUT(buf, result, max_size) \
+ do { \
+ int n; \
+ if ((n = yy_mql_input(buf, max_size)) >= 0) \
+ result = n; \
+ else { \
+ result = 0; \
+ YY_FATAL_ERROR( "mql input failed" ); \
+ } \
+ } while (0)
+
+
+YYSTYPE yy_mql_lval;
+
+
+
+static char ringbuf[4096];
+static char *bufptr = ringbuf;
+static char *bufend = ringbuf + sizeof(ringbuf) - 1;
+
+static char *copy_to_ringbuf(const char *);
+
+
+%}
+
+%option prefix="yy_mql_"
+%option batch
+%option yylineno
+%option case-insensitive
+%option nounput noyymore noyywrap
+
+WHITESPACE [\ \t\n]+
+
+SHOW show
+BEGIN begin
+COMMIT commit
+ROLLBACK rollback
+TRANSACTION transaction
+TRANSACTIONS transactions
+CREATE create
+UPDATE update
+REPLACE replace
+DELETE delete
+DROP drop
+DESCRIBE describe
+TABLE table
+TABLES tables
+INDEX index
+ROWS rows
+COLUMN column
+TRIGGER trigger
+INSERT insert
+SELECT select
+INTO into
+FROM from
+WHERE where
+VALUES values
+SET set
+ON on
+IN in
+OR or
+PERSISTENT persistent
+TEMPORARY temporary
+CALLBACK callback
+
+VARCHAR varchar
+INTEGER integer
+UNSIGNED unsigned
+REAL real
+BLOB blob
+PARAMETER \%[sduf]
+
+LOGICAL_AND \&
+LOGICAL_OR \|
+LESS <
+LESS_OR_EQUAL <=
+EQUAL =
+GREATER_OR_EQUAL >=
+GREATER >
+NOT \!
+
+NOT_SQUOTE [^\n'\;]
+NOT_DQUOTE [^\n\"\;]
+
+NUMBER [0-9]+
+FLOATING [0-9]\.[0-9]*
+IDENTIFIER [a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])*
+QUOTED_STRING (('{NOT_SQUOTE}*')|(\"{NOT_DQUOTE}*\"))
+
+LEFT_PAREN \(
+RIGHT_PAREN \)
+COMMA ,
+SEMICOLON ;
+PLUS \+
+MINUS \-
+STAR \*
+SLASH \/
+
+
+
+
+%%
+
+<<EOF>> { EOF_TOKEN; }
+{WHITESPACE} { }
+
+{SHOW} { ARGLESS_TOKEN (SHOW); }
+{BEGIN} { ARGLESS_TOKEN (BEGIN); }
+{COMMIT} { ARGLESS_TOKEN (COMMIT); }
+{ROLLBACK} { ARGLESS_TOKEN (ROLLBACK); }
+{TRANSACTION} { ARGLESS_TOKEN (TRANSACTION); }
+{TRANSACTIONS} { ARGLESS_TOKEN (TRANSACTIONS); }
+{CREATE} { ARGLESS_TOKEN (CREATE); }
+{UPDATE} { ARGLESS_TOKEN (UPDATE); }
+{REPLACE} { ARGLESS_TOKEN (REPLACE); }
+{DELETE} { ARGLESS_TOKEN (DELETE); }
+{DROP} { ARGLESS_TOKEN (DROP); }
+{DESCRIBE} { ARGLESS_TOKEN (DESCRIBE); }
+{TABLE} { ARGLESS_TOKEN (TABLE); }
+{TABLES} { ARGLESS_TOKEN (TABLES); }
+{INDEX} { ARGLESS_TOKEN (INDEX); }
+{ROWS} { ARGLESS_TOKEN (ROWS); }
+{COLUMN} { ARGLESS_TOKEN (COLUMN); }
+{TRIGGER} { ARGLESS_TOKEN (TRIGGER); }
+{INSERT} { ARGLESS_TOKEN (INSERT); }
+{SELECT} { ARGLESS_TOKEN (SELECT); }
+{INTO} { ARGLESS_TOKEN (INTO); }
+{FROM} { ARGLESS_TOKEN (FROM); }
+{WHERE} { ARGLESS_TOKEN (WHERE); }
+{VALUES} { ARGLESS_TOKEN (VALUES); }
+{SET} { ARGLESS_TOKEN (SET); }
+{ON} { ARGLESS_TOKEN (ON); }
+{IN} { ARGLESS_TOKEN (IN); }
+{OR} { ARGLESS_TOKEN (OR); }
+{PERSISTENT} { ARGLESS_TOKEN (PERSISTENT); }
+{TEMPORARY} { ARGLESS_TOKEN (TEMPORARY); }
+{CALLBACK} { ARGLESS_TOKEN (CALLBACK); }
+
+{VARCHAR} { ARGLESS_TOKEN (VARCHAR); }
+{INTEGER} { ARGLESS_TOKEN (INTEGER); }
+{UNSIGNED} { ARGLESS_TOKEN (UNSIGNED); }
+{REAL} { ARGLESS_TOKEN (REAL); }
+{BLOB} { ARGLESS_TOKEN (BLOB); }
+{PARAMETER} { PARAMETER_TOKEN; }
+
+{LOGICAL_AND} { ARGLESS_TOKEN (LOGICAL_AND); }
+{LOGICAL_OR} { ARGLESS_TOKEN (LOGICAL_OR); }
+{LESS} { ARGLESS_TOKEN (LESS); }
+{LESS_OR_EQUAL} { ARGLESS_TOKEN (LESS_OR_EQUAL); }
+{EQUAL} { ARGLESS_TOKEN (EQUAL); }
+{GREATER_OR_EQUAL} { ARGLESS_TOKEN (GREATER_OR_EQUAL); }
+{GREATER} { ARGLESS_TOKEN (GREATER); }
+{NOT} { ARGLESS_TOKEN (NOT); }
+
+{LEFT_PAREN} { ARGLESS_TOKEN (LEFT_PAREN); }
+{RIGHT_PAREN} { ARGLESS_TOKEN (RIGHT_PAREN); }
+{COMMA} { ARGLESS_TOKEN (COMMA); }
+{SEMICOLON} { SEMICOLON_TOKEN; }
+{PLUS} { ARGLESS_TOKEN (PLUS); }
+{MINUS} { ARGLESS_TOKEN (MINUS); }
+{STAR} { ARGLESS_TOKEN (STAR); }
+{SLASH} { ARGLESS_TOKEN (SLASH); }
+
+{NUMBER} { NUMBER_TOKEN; }
+{FLOATING} { FLOATING_TOKEN; }
+{IDENTIFIER} { STRING_TOKEN (IDENTIFIER); }
+{QUOTED_STRING} { STRING_TOKEN (QUOTED_STRING); }
+
+%%
+
+
+
+static char *copy_to_ringbuf(const char *string)
+{
+ const char *src;
+ char *copy;
+ char qt;
+
+ for (;;) {
+ if (bufptr >= bufend)
+ bufptr = ringbuf;
+
+ copy = bufptr;
+
+ switch (*(src = string)) {
+ case '\'': qt = *src++; break;
+ case '\"': qt = *src++; break;
+ default: qt = 0xff; break;
+ }
+
+ while (bufptr < bufend) {
+ if (!(*bufptr++ = *src++)) {
+ if (bufptr[-2] == qt)
+ (--bufptr)[-1] = '\0';
+ return copy;
+ }
+ }
+ }
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ * vim:set expandtab shiftwidth=4:
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <alloca.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/mql-result.h>
+#include "mql-parser.h"
+
+typedef struct column_desc_s column_desc_t;
+typedef struct error_desc_s error_desc_t;
+typedef struct result_error_s result_error_t;
+typedef struct result_event_s result_event_t;
+typedef struct result_event_colchg_s result_event_colchg_t;
+typedef struct result_event_rowchg_s result_event_rowchg_t;
+typedef struct result_event_table_s result_event_table_t;
+typedef struct result_event_transact_s result_event_transact_t;
+typedef struct result_columns_s result_columns_t;
+typedef struct result_rows_s result_rows_t;
+typedef struct result_string_s result_string_t;
+typedef struct result_list_s result_list_t;
+
+struct column_desc_s {
+ int cindex;
+ mqi_data_type_t type;
+ int offset;
+};
+
+struct error_desc_s {
+ int code;
+ char msg[0];
+};
+
+struct result_error_s {
+ mql_result_type_t type;
+ error_desc_t error;
+};
+
+struct result_event_s {
+ mql_result_type_t type;
+ mqi_event_type_t event;
+ uint8_t data[0];
+};
+
+struct result_event_colchg_s {
+ mql_result_type_t type;
+ mqi_event_type_t event;
+ mqi_handle_t table;
+ int column;
+ mqi_change_value_t value;
+ mql_result_t *select;
+ uint8_t data[0];
+};
+
+struct result_event_rowchg_s {
+ mql_result_type_t type;
+ mqi_event_type_t event;
+ mqi_handle_t table;
+ mql_result_t *select;
+};
+
+struct result_event_table_s {
+ mql_result_type_t type;
+ mqi_event_type_t event;
+ mqi_handle_t table;
+};
+
+struct result_event_transact_s {
+ mql_result_type_t type;
+ mqi_event_type_t event;
+};
+
+struct result_columns_s {
+ mql_result_type_t type;
+ int ncol;
+ mqi_column_def_t cols[0];
+};
+
+struct result_rows_s {
+ mql_result_type_t type;
+ int rowsize;
+ int ncol;
+ int nrow;
+ void *data;
+ column_desc_t cols[0];
+};
+
+struct result_string_s {
+ mql_result_type_t type;
+ int length;
+ char string[0];
+};
+
+struct result_list_s {
+ mql_result_type_t type;
+ int length;
+ struct {
+ mqi_data_type_t type;
+ union {
+ char *varchar[0];
+ int32_t integer[0];
+ uint32_t unsignd[0];
+ double floating[0];
+ int generic[0];
+ } v;
+ } value;
+};
+
+static inline mqi_data_type_t get_column_type(result_rows_t *, int);
+static inline void *get_column_address(result_rows_t *, int, int);
+
+
+int mql_result_is_success(mql_result_t *r)
+{
+ result_error_t *e = (result_error_t *)r;
+
+ if (e) {
+ if (e->type != mql_result_error)
+ return 1;
+
+ if (!e->error.code)
+ return 1;
+ }
+
+ return 0;
+}
+
+mql_result_t *mql_result_success_create(void)
+{
+ return mql_result_error_create(0, "Success");
+}
+
+mql_result_t *mql_result_error_create(int code, const char *fmt, ...)
+{
+ va_list ap;
+ result_error_t *rslt;
+ char buf[1024];
+ int l;
+
+ MDB_CHECKARG(code >= 0 && fmt, NULL);
+
+ va_start(ap, fmt);
+ l = vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (l > (int)sizeof(buf))
+ l = sizeof(buf) - 1;
+
+ if ((rslt = calloc(1, sizeof(result_error_t) + l + 1))) {
+ rslt->type = mql_result_error;
+ rslt->error.code = code;
+ memcpy(rslt->error.msg, buf, l);
+ }
+
+ return (mql_result_t *)rslt;
+}
+
+int mql_result_error_get_code(mql_result_t *r)
+{
+ int code;
+
+ MDB_CHECKARG(r, -1);
+
+ if (r->type != mql_result_error)
+ code = 0;
+ else {
+ result_error_t *rslt = (result_error_t *)r;
+ code = rslt->error.code;
+ }
+
+ return code;
+}
+
+const char *mql_result_error_get_message(mql_result_t *r)
+{
+ const char *msg;
+
+ MDB_CHECKARG(r, NULL);
+
+ if (r->type != mql_result_error)
+ msg = "Success";
+ else {
+ result_error_t *rslt = (result_error_t *)r;
+ msg = rslt->error.msg;
+ }
+
+ return msg;
+}
+
+mqi_event_type_t mql_result_event_get_type(mql_result_t *r)
+{
+ result_event_t *ev;
+
+ if (!r || r->type != mql_result_event)
+ return mqi_event_unknown;
+
+ ev = (result_event_t *) r;
+
+ return ev->event;
+}
+
+mql_result_t *mql_result_event_get_changed_rows(mql_result_t *r)
+{
+ result_event_t *ev;
+ result_event_rowchg_t *rowchg_ev;
+
+ if (!r || r->type != mql_result_event)
+ return NULL;
+
+ ev = (result_event_t *) r;
+
+ if (ev->event != mqi_row_deleted && ev->event != mqi_row_inserted)
+ return NULL;
+
+ rowchg_ev = (result_event_rowchg_t *) ev;
+
+ return rowchg_ev->select;
+}
+
+mql_result_t *mql_result_event_column_change_create(mqi_handle_t table,
+ int column,
+ mqi_change_value_t *value,
+ mql_result_t *select)
+{
+ result_event_colchg_t *rslt;
+ int osiz;
+ int nsiz;
+ int poollen;
+ void *poolptr;
+ size_t size;
+
+ MDB_CHECKARG(table != MQI_HANDLE_INVALID &&
+ column >= 0 && column < MQI_COLUMN_MAX && value &&
+ (!select || (select && select->type == mql_result_rows)),
+ NULL);
+
+ switch (value->type) {
+
+ case mqi_varchar:
+ osiz = strlen(value->old.varchar) + 1;
+ nsiz = strlen(value->new_.varchar) + 1;
+ break;
+
+ case mqi_blob:
+ if ((osiz = nsiz = mqi_get_column_size(table, column)) < 0)
+ return NULL;
+ break;
+
+ default:
+ osiz = nsiz = 0;
+ break;
+ }
+
+ poollen = osiz + nsiz;
+ size = sizeof(result_event_colchg_t) + poollen;
+
+ if (!(rslt = calloc(1, size))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ poolptr = (void *)rslt->data;
+
+ rslt->type = mql_result_event;
+ rslt->event = mqi_column_changed;
+ rslt->table = table;
+ rslt->column = column;
+ rslt->value = *value;
+ rslt->select = select;
+
+ if (osiz > 0) {
+ rslt->value.old.generic = poolptr;
+ memcpy(poolptr, value->old.generic, osiz);
+ poolptr += osiz;
+ }
+
+ if (nsiz > 0) {
+ rslt->value.new_.generic = poolptr;
+ memcpy(poolptr, value->new_.generic, nsiz);
+ poolptr += nsiz;
+ }
+
+ return (mql_result_t *)rslt;
+}
+
+
+mql_result_t *mql_result_event_row_change_create(mqi_event_type_t event,
+ mqi_handle_t table,
+ mql_result_t *select)
+{
+ result_event_rowchg_t *rslt;
+
+ MDB_CHECKARG((event == mqi_row_inserted || event == mqi_row_deleted) &&
+ table != MQI_HANDLE_INVALID &&
+ select && select->type == mql_result_rows, NULL);
+
+ if (!(rslt = calloc(1, sizeof(result_event_rowchg_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_event;
+ rslt->event = event;
+ rslt->table = table;
+ rslt->select = select;
+
+ return (mql_result_t *)rslt;
+}
+
+
+mql_result_t *mql_result_event_table_create(mqi_event_type_t event,
+ mqi_handle_t table)
+{
+ result_event_table_t *rslt;
+
+ MDB_CHECKARG((event == mqi_table_created || event == mqi_table_dropped) &&
+ table != MQI_HANDLE_INVALID, NULL);
+
+ if (!(rslt = calloc(1, sizeof(result_event_table_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_event;
+ rslt->event = event;
+ rslt->table = table;
+
+ return (mql_result_t *)rslt;
+}
+
+
+mql_result_t *mql_result_event_transaction_create(mqi_event_type_t event)
+{
+ result_event_transact_t *rslt;
+
+ MDB_CHECKARG(event == mqi_transaction_start ||
+ event == mqi_transaction_end, NULL);
+
+ if (!(rslt = calloc(1, sizeof(result_event_transact_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_event;
+ rslt->event = event;
+
+ return (mql_result_t *)rslt;
+}
+
+
+
+mql_result_t *mql_result_columns_create(int ncol, mqi_column_def_t *defs)
+{
+ result_columns_t *rslt;
+ mqi_column_def_t *col;
+ size_t poollen;
+ size_t dlgh;
+ int namlen[MQI_COLUMN_MAX];
+ char *strpool;
+ int i;
+
+ MDB_CHECKARG(ncol > 0 && ncol < MQI_COLUMN_MAX && defs, NULL);
+
+ for (poollen = 0, i = 0; i < ncol; i++)
+ poollen += (namlen[i] = strlen(defs[i].name) + 1);
+
+ dlgh = sizeof(mqi_column_def_t) * ncol;
+
+ if (!(rslt = calloc(1, sizeof(result_columns_t) + dlgh + poollen))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_columns;
+ rslt->ncol = ncol;
+
+ memcpy(rslt->cols, defs, dlgh);
+
+ strpool = (char *)(rslt->cols + ncol);
+
+ for (i = 0; i < ncol; i++) {
+ col = rslt->cols + i;
+ col->name = memcpy(strpool, col->name, namlen[i]);
+ strpool += namlen[i];
+ }
+
+ return (mql_result_t *)rslt;
+}
+
+int mql_result_columns_get_column_count(mql_result_t *r)
+{
+ result_columns_t *rslt = (result_columns_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_columns, -1);
+
+ return rslt->ncol;
+}
+
+const char *mql_result_columns_get_name(mql_result_t *r, int colidx)
+{
+ result_columns_t *rslt = (result_columns_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_columns &&
+ colidx >= 0 && colidx < rslt->ncol, NULL);
+
+ return rslt->cols[colidx].name;
+}
+
+mqi_data_type_t mql_result_columns_get_type(mql_result_t *r, int colidx)
+{
+ result_columns_t *rslt = (result_columns_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_columns &&
+ colidx >= 0 && colidx < rslt->ncol, -1);
+
+ return rslt->cols[colidx].type;
+}
+
+int mql_result_columns_get_length(mql_result_t *r, int colidx)
+{
+ result_columns_t *rslt = (result_columns_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_columns &&
+ colidx >= 0 && colidx < rslt->ncol, -1);
+
+ return rslt->cols[colidx].length;
+}
+
+
+uint32_t mql_result_columns_get_flags(mql_result_t *r, int colidx)
+{
+ result_columns_t *rslt = (result_columns_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_columns &&
+ colidx >= 0 && colidx < rslt->ncol, -1);
+
+ return rslt->cols[colidx].flags;
+}
+
+
+mql_result_t *mql_result_rows_create(int ncol,
+ mqi_column_desc_t *coldescs,
+ mqi_data_type_t *coltypes,
+ int *colsizes,
+ int nrow,
+ int rowsize,
+ void *rows)
+{
+ result_rows_t *rslt;
+ column_desc_t *col;
+ mqi_column_desc_t *cd;
+ int offs;
+ size_t size;
+ size_t dlgh;
+ int i;
+
+ MDB_CHECKARG(ncol > 0 && coldescs && coltypes && colsizes &&
+ nrow >= 0 && rowsize > 0 && rows, NULL);
+
+ offs = sizeof(column_desc_t) * ncol;
+ dlgh = rowsize * nrow;
+ size = sizeof(result_rows_t) + offs + dlgh;
+
+
+ if (!(rslt = calloc(1, size))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_rows;
+ rslt->rowsize = rowsize;
+ rslt->ncol = ncol;
+ rslt->nrow = nrow;
+ rslt->data = rslt->cols + ncol;
+
+ for (i = 0; i < ncol; i++) {
+ col = rslt->cols + i;
+ cd = coldescs + i;
+
+ col->cindex = cd->cindex;
+ col->type = coltypes[i];
+ col->offset = cd->offset;
+ }
+
+ if (dlgh > 0)
+ memcpy(rslt->data, rows, dlgh);
+
+ return (mql_result_t *)rslt;
+}
+
+
+int mql_result_rows_get_row_column_count(mql_result_t *r)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows, -1);
+
+ return rslt->ncol;
+}
+
+mqi_data_type_t mql_result_rows_get_row_column_type(mql_result_t *r, int colidx)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows &&
+ rslt->ncol > colidx, -1);
+
+ return rslt->cols[colidx].type;
+}
+
+int mql_result_rows_get_row_column_index(mql_result_t *r, int colidx)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows &&
+ rslt->ncol > colidx, -1);
+
+ return rslt->cols[colidx].cindex;
+}
+
+int mql_result_rows_get_row_count(mql_result_t *r)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows, -1);
+
+ return rslt->nrow;
+}
+
+const char *mql_result_rows_get_string(mql_result_t *r, int colidx, int rowidx,
+ char *buf, int len)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+ void *addr;
+ char *v = NULL;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows &&
+ colidx >= 0 && colidx < rslt->ncol &&
+ rowidx >= 0 && rowidx < rslt->nrow &&
+ (!buf || (buf && len > 0)), NULL);
+
+ if ((v = buf))
+ *v = '\0';
+
+ if ((addr = get_column_address(rslt, colidx, rowidx))) {
+ switch (get_column_type(rslt, colidx)) {
+ case mqi_varchar:
+ if (!v)
+ v = *(char **)addr;
+ else {
+ strncpy(v, *(char **)addr, len);
+ v[len-1] = '\0';
+ }
+ break;
+
+ case mqi_integer:
+ if (!v)
+ v = "";
+ else
+ snprintf(v, len, "%d", *(int32_t *)addr);
+ break;
+
+ case mqi_unsignd:
+ if (!v)
+ v = "";
+ else
+ snprintf(v, len, "%u", *(uint32_t *)addr);
+ break;
+
+ case mqi_floating:
+ if (!v)
+ v = "";
+ else
+ snprintf(v, len, "%lf", *(double *)addr);
+ break;
+
+ default:
+ v = "";
+ break;
+ }
+ }
+
+ return v;
+}
+
+int32_t mql_result_rows_get_integer(mql_result_t *r, int colidx, int rowidx)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+ void *addr;
+ int32_t v = 0;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows &&
+ colidx >= 0 && colidx < rslt->ncol &&
+ rowidx >= 0 && rowidx < rslt->nrow, 0);
+
+ if ((addr = get_column_address(rslt, colidx, rowidx))) {
+ switch (get_column_type(rslt, colidx)) {
+ case mqi_varchar: v = strtol(*(char **)addr, NULL, 10); break;
+ case mqi_integer: v = *(int32_t *)addr; break;
+ case mqi_unsignd: v = *(uint32_t *)addr; break;
+ case mqi_floating: v = *(double *)addr; break;
+ default: v = 0; break;
+ }
+ }
+
+ return v;
+}
+
+uint32_t mql_result_rows_get_unsigned(mql_result_t *r, int colidx, int rowidx)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+ void *addr;
+ uint32_t v = 0;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows &&
+ colidx >= 0 && colidx < rslt->ncol &&
+ rowidx >= 0 && rowidx < rslt->nrow, 0);
+
+ if ((addr = get_column_address(rslt, colidx, rowidx))) {
+ switch (get_column_type(rslt, colidx)) {
+ case mqi_varchar: v = strtoul(*(char **)addr, NULL, 10); break;
+ case mqi_integer: v = *(int32_t *)addr; break;
+ case mqi_unsignd: v = *(uint32_t *)addr; break;
+ case mqi_floating: v = *(double *)addr; break;
+ default: v = 0; break;
+ }
+ }
+
+ return v;
+}
+
+double mql_result_rows_get_floating(mql_result_t *r, int colidx, int rowidx)
+{
+ result_rows_t *rslt = (result_rows_t *)r;
+ void *addr;
+ double v = 0;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_rows &&
+ colidx >= 0 && colidx < rslt->ncol &&
+ rowidx >= 0 && rowidx < rslt->nrow, 0.0);
+
+ if ((addr = get_column_address(rslt, colidx, rowidx))) {
+ switch (get_column_type(rslt, colidx)) {
+ case mqi_varchar: v = strtod(*(char **)addr, NULL); break;
+ case mqi_integer: v = *(int32_t *)addr; break;
+ case mqi_unsignd: v = *(uint32_t *)addr; break;
+ case mqi_floating: v = *(double *)addr; break;
+ default: v = 0; break;
+ }
+ }
+
+ return v;
+}
+
+
+mql_result_t *mql_result_string_create_table_list(int n, char **names)
+{
+ static const char *no_tables = "no tables\n";
+ result_string_t *rslt;
+ int nlgh[4096];
+ char *name;
+ char first_letter, upper;
+ int len;
+ char *p;
+ int i;
+
+ MDB_CHECKARG(n >= 0 && n < (int)MQI_DIMENSION(nlgh) && names, NULL);
+
+ len = 1; /* zero terminator */
+
+ if (!n)
+ len += strlen(no_tables) + 1;
+ else {
+ for (i = 0, first_letter = 0; i < n; i++) {
+ name = names[i];
+ upper = toupper(name[0]);
+
+ if (upper != first_letter) {
+ first_letter = upper;
+ len += 3; /* \n[A-Z]: */
+ }
+
+ len += (nlgh[i] = strlen(name)) + 1;
+ }
+ }
+
+ if (!(rslt = calloc(1, sizeof(result_string_t) + len))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_string;
+ rslt->length = len;
+
+ if (!n)
+ strcpy(rslt->string, no_tables);
+ else {
+ for (p = rslt->string, first_letter = 0, i = 0; i < n; i++) {
+ name = names[i];
+ len = nlgh[i];
+ upper = toupper(name[0]);
+
+ if (upper != first_letter) {
+ if (first_letter)
+ *p++ = '\n';
+
+ first_letter = upper;
+
+ *p++ = upper;
+ *p++ = ':';
+ }
+
+ *p++ = ' ';
+
+ memcpy(p, name, len);
+ p += len;
+ }
+
+ *p++ = '\n';
+ }
+
+ return (mql_result_t *)rslt;
+}
+
+mql_result_t *mql_result_string_create_column_change(const char *table,
+ const char *col,
+ mqi_change_value_t *value,
+ mql_result_t *rsel)
+{
+#define EVENT 0
+#define TABLE 1
+#define COLUMN 2
+#define VALUE 3
+#define FLDS 4
+
+ static const char *hstr[FLDS] = {" event", "table" , "column", "change"};
+ static int hlen[FLDS] = { 6 , 5 , 6 , 6 };
+
+
+ result_string_t *rs = (result_string_t *)rsel;
+ result_string_t *rslt;
+
+ char buf[1024];
+ int l;
+ int len[FLDS];
+ const char *cstr[FLDS];
+ int cw[FLDS];
+ int linlen;
+ size_t esiz;
+ size_t ssiz;
+ size_t size;
+ char *p;
+ int i;
+
+ MDB_CHECKARG(table && col && value &&
+ (!rsel || (rsel && rsel->type == mql_result_string)), NULL);
+
+ cstr[EVENT] = "'column changed'";
+ cstr[TABLE] = table;
+ cstr[COLUMN] = col;
+ cstr[VALUE] = buf;
+
+ for (i = 0; i < VALUE; i++)
+ len[i] = strlen(cstr[i]);
+
+#define PRINT_VALUE(fmt,t) \
+ snprintf(buf, sizeof(buf), fmt " => " fmt, value->old.t, value->new_.t)
+#define PRINT_UNKNOWN \
+ snprintf(buf, sizeof(buf), "<unknown> => <unknown>");
+
+ switch (value->type) {
+ case mqi_varchar: l = PRINT_VALUE("'%s'" , varchar ); break;
+ case mqi_integer: l = PRINT_VALUE("%d" , integer ); break;
+ case mqi_unsignd: l = PRINT_VALUE("%u" , unsignd ); break;
+ case mqi_floating: l = PRINT_VALUE("%.2lf", floating); break;
+ default: l = PRINT_UNKNOWN; break;
+ }
+
+#undef PRINT
+
+ len[VALUE] = l;
+
+
+ for (linlen = (FLDS-1) * 2 + 1, i = 0; i < FLDS; i++)
+ linlen += (cw[i] = len[i] > hlen[i] ? len[i] : hlen[i]);
+
+ esiz = linlen * 3;
+ ssiz = rs ? rs->length : 0;
+ size = esiz + ssiz + 2;
+
+ if (!(rslt = calloc(1, sizeof(result_string_t) + size))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ p = rslt->string;
+
+ for (i = 0; i < FLDS; i++)
+ p += sprintf(p, "%-*s%s", cw[i], hstr[i], i == FLDS-1 ? "\n" : " ");
+
+ memset(p, '-', linlen-1);
+ p[linlen-1] = '\n';
+ p += linlen;
+
+ for (i = 0; i < FLDS; i++)
+ p += sprintf(p, "%-*s%s", cw[i], cstr[i], i == FLDS-1 ? "\n" : " ");
+
+
+ if (ssiz > 0) {
+ *p++ = '\n';
+ memcpy(p, rs->string, ssiz);
+ }
+
+ rslt->type = mql_result_string;
+ rslt->length = p - rslt->string;
+
+ return (mql_result_t *)rslt;
+
+#undef FLDS
+#undef VALUE
+#undef COLUMN
+#undef TABLE
+#undef EVENT
+}
+
+mql_result_t *mql_result_string_create_row_change(mqi_event_type_t event,
+ const char *table,
+ mql_result_t *rsel)
+{
+#define EVENT 0
+#define TABLE 1
+#define FLDS 2
+
+ static const char *hstr[FLDS] = {" event", "table"};
+ static int hlen[FLDS] = { 6 , 5 };
+
+
+ result_string_t *rs = (result_string_t *)rsel;
+ result_string_t *rslt;
+
+ int len[FLDS];
+ const char *cstr[FLDS];
+ int cw[FLDS];
+ int linlen;
+ size_t esiz;
+ size_t ssiz;
+ size_t size;
+ char *p;
+ int i;
+
+ MDB_CHECKARG((event == mqi_row_inserted || event == mqi_row_deleted) &&
+ table && rsel && rsel->type == mql_result_string, NULL);
+
+ cstr[EVENT] = (event==mqi_row_inserted) ? "'row inserted'":"'row deleted'";
+ cstr[TABLE] = table;
+
+ for (i = 0; i < FLDS; i++)
+ len[i] = strlen(cstr[i]);
+
+ for (linlen = (FLDS-1) * 2 + 1, i = 0; i < FLDS; i++)
+ linlen += (cw[i] = len[i] > hlen[i] ? len[i] : hlen[i]);
+
+ esiz = linlen * 3;
+ ssiz = rs ? rs->length : 0;
+ size = esiz + ssiz + 2;
+
+ if (!(rslt = calloc(1, sizeof(result_string_t) + size))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ p = rslt->string;
+
+ for (i = 0; i < FLDS; i++)
+ p += sprintf(p, "%-*s%s", cw[i], hstr[i], i == FLDS-1 ? "\n" : " ");
+
+ memset(p, '-', linlen-1);
+ p[linlen-1] = '\n';
+ p += linlen;
+
+ for (i = 0; i < FLDS; i++)
+ p += sprintf(p, "%-*s%s", cw[i], cstr[i], i == FLDS-1 ? "\n" : " ");
+
+ *p++ = '\n';
+ memcpy(p, rs->string, ssiz);
+
+ rslt->type = mql_result_string;
+ rslt->length = p - rslt->string;
+
+ return (mql_result_t *)rslt;
+
+#undef FLDS
+#undef TABLE
+#undef EVENT
+}
+
+mql_result_t *mql_result_string_create_table_change(mqi_event_type_t event,
+ const char *table)
+{
+#define EVENT 0
+#define TABLE 1
+#define FLDS 2
+
+ static const char *hstr[FLDS] = {" event", "table"};
+ static int hlen[FLDS] = { 6 , 5 };
+
+ result_string_t *rslt;
+
+ int len[FLDS];
+ const char *cstr[FLDS];
+ int cw[FLDS];
+ int linlen;
+ char *p;
+ int i;
+
+ MDB_CHECKARG((event == mqi_table_created || event == mqi_table_dropped) &&
+ table, NULL);
+
+ cstr[EVENT] = (event == mqi_table_created) ?
+ "'table created'" : "'table dropped'";
+ cstr[TABLE] = table;
+
+ for (i = 0; i < FLDS; i++)
+ len[i] = strlen(cstr[i]);
+
+ for (linlen = (FLDS-1) * 2 + 1, i = 0; i < FLDS; i++)
+ linlen += (cw[i] = len[i] > hlen[i] ? len[i] : hlen[i]);
+
+
+ if (!(rslt = calloc(1, sizeof(result_string_t) + (linlen*3 + 1)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ p = rslt->string;
+
+ for (i = 0; i < FLDS; i++)
+ p += sprintf(p, "%-*s%s", cw[i], hstr[i], i == FLDS-1 ? "\n" : " ");
+
+ memset(p, '-', linlen-1);
+ p[linlen-1] = '\n';
+ p += linlen;
+
+ for (i = 0; i < FLDS; i++)
+ p += sprintf(p, "%-*s%s", cw[i], cstr[i], i == FLDS-1 ? "\n" : " ");
+
+ rslt->type = mql_result_string;
+ rslt->length = p - rslt->string;
+
+ return (mql_result_t *)rslt;
+
+#undef FLDS
+#undef TABLE
+#undef EVENT
+}
+
+mql_result_t *mql_result_string_create_transaction_change(mqi_event_type_t evt)
+{
+ static const char *hstr = " event";
+ static int hlen = 6;
+
+ result_string_t *rslt;
+
+ int len;
+ const char *cstr;
+ int cw;
+ int linlen;
+ char *p;
+
+ MDB_CHECKARG(evt == mqi_transaction_start || evt == mqi_transaction_end,
+ NULL);
+
+ cstr = (evt == mqi_transaction_start) ?
+ "'transaction started'" : "'transaction ended'";
+ len = strlen(cstr);
+ cw = len > hlen ? len : hlen;
+ linlen = cw + 1;
+
+ if (!(rslt = calloc(1, sizeof(result_string_t) + (linlen*3 + 1)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ p = rslt->string;
+ p += sprintf(p, "%-*s\n", cw, hstr);
+
+ memset(p, '-', cw);
+ p[cw] = '\n';
+ p += linlen;
+
+ p += sprintf(p, "%-*s", cw, cstr);
+
+ rslt->type = mql_result_string;
+ rslt->length = p - rslt->string;
+
+ return (mql_result_t *)rslt;
+}
+
+mql_result_t *mql_result_string_create_column_list(int ncol,
+ mqi_column_def_t *defs)
+{
+#define INDEX 0
+#define NAME 1
+#define TYPE 2
+#define LENGTH 3
+#define FLDS 4
+
+ static const char *hstr[FLDS] = {"index", " name" , "type", "length"};
+ static int hlen[FLDS] = { 5 , 5 , 4 , 6 };
+ static int align[FLDS] = { +1 , -1 , -1 , +1 };
+
+ result_string_t *rslt;
+ mqi_column_def_t *def;
+ const char *typstr[MQI_COLUMN_MAX];
+ int namlen[MQI_COLUMN_MAX];
+ int typlen[MQI_COLUMN_MAX];
+ int fldlen[FLDS];
+ int linlen;
+ int len;
+ int offs;
+ char *p, *q;
+ int i, j;
+
+ MDB_CHECKARG(ncol > 0 && ncol < MQI_COLUMN_MAX && defs, NULL);
+
+ memcpy(fldlen, hlen, sizeof(fldlen));
+
+ for (i = 0; i < ncol; i++) {
+ def = defs + i;
+
+ typstr[i] = mqi_data_type_str(def->type);
+
+ if ((len = (namlen[i] = strlen(def->name)) + 1) > fldlen[NAME])
+ fldlen[NAME] = len;
+
+ if ((len = (typlen[i] = strlen(typstr[i]))) > fldlen[TYPE])
+ fldlen[TYPE] = len;
+ }
+
+ for (linlen = FLDS, i = 0; i < FLDS; linlen += fldlen[i++])
+ ;
+
+ len = linlen * (ncol+2) + 1;
+
+ /*
+ * allocate and initialize the result structure
+ */
+ if (!(rslt = calloc(1, sizeof(result_string_t) + len))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_string;
+ rslt->length = len;
+
+ p = rslt->string;
+ memset(p, ' ', linlen * (ncol+2));
+
+ /*
+ * labels
+ */
+ for (q = p, j = 0; j < FLDS; q += (fldlen[j++] + 1)) {
+ offs = (align[j] < 0) ? 0 : fldlen[j] - hlen[j];
+ memcpy(q + offs, hstr[j], hlen[j]);
+ }
+
+ p += linlen;
+ *(p-1) = '\n';
+
+ /*
+ * separator line
+ */
+ memset(p, '-', linlen);
+ p += linlen;
+ *(p-1) = '\n';
+
+
+ /*
+ * data lines
+ */
+ for (i = 0; i < ncol; i++, p += linlen) {
+ def = defs + i;
+ snprintf(p, linlen+1, "%*d %s%-*s %-*s %*d\n",
+ fldlen[INDEX], i,
+ def->flags&MQI_COLUMN_KEY ? "*":" ", fldlen[NAME]-1,def->name,
+ fldlen[TYPE], typstr[i],
+ fldlen[LENGTH], def->length);
+ }
+
+ return (mql_result_t *)rslt;
+
+#undef FLDS
+#undef LENGTH
+#undef TYPE
+#undef NAME
+#undef INDEX
+}
+
+mql_result_t *mql_result_string_create_row_list(int ncol,
+ char **colnams,
+ mqi_column_desc_t *coldescs,
+ mqi_data_type_t *coltypes,
+ int *colsizes,
+ int nrow,
+ int rowsize,
+ void *rows)
+{
+ static const char *no_rows = "no rows\n";
+
+ result_string_t *rslt;
+ size_t len;
+ int rwidth;
+ int cwidth;
+ int cwidths[MQI_COLUMN_MAX + 1];
+ void *row;
+ void *column;
+ int i,j;
+ char *p;
+
+
+ MDB_CHECKARG(ncol > 0 && coldescs && coltypes && colsizes &&
+ nrow >= 0 && rowsize > 0 && rows, NULL);
+
+ /*
+ * calculate column widths and row width
+ */
+ rwidth = ncol; /* for each column a separating space or a \n at the end */
+
+ for (i = 0; i < ncol; i++) {
+ switch (coltypes[i]) {
+ case mqi_varchar: cwidth = colsizes[i] - 1; break;
+ case mqi_integer: cwidth = 11; break;
+ case mqi_unsignd: cwidth = 10; break;
+ case mqi_floating: cwidth = 10; break;
+ default: cwidth = 0; break;
+ }
+
+ rwidth += (cwidths[i] = cwidth);
+ }
+
+ if (!nrow)
+ len = rwidth * 2 + strlen(no_rows) + 1;
+ else
+ len = rwidth * (nrow + 2) + 1;
+
+ /*
+ * setup the result structure
+ */
+ if (!(rslt = calloc(1, sizeof(result_string_t) + len))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ rslt->type = mql_result_string;
+ rslt->length = len;
+
+ p = rslt->string;
+
+ /*
+ * labels
+ */
+ for (i = 0; i < ncol; i++) {
+ if ((cwidth = cwidths[i])) {
+ if (cwidth <= (int)(len = strlen(colnams[i]))) {
+ /* truncate */
+ memcpy(p, colnams[i], cwidth);
+ p[cwidth] = ' ';
+ }
+ else {
+ if (coltypes[i] == mqi_varchar) {
+ /* left align */
+ memcpy(p, colnams[i], len);
+ memset(p + len, ' ', (cwidth + 1) - len);
+ }
+ else {
+ /* right align */
+ memset(p, ' ', cwidth - len);
+ memcpy(p + (cwidth - len), colnams[i], len);
+ p[cwidth] = ' ';
+ }
+ }
+ p += cwidth + 1;
+ }
+ } /* for */
+
+ *(p-1) = '\n';
+
+ /*
+ * separator line
+ */
+ memset(p, '-', rwidth-1);
+ p[rwidth-1] = '\n';
+ p += rwidth;
+
+ /*
+ * data lines
+ */
+#define SPRINT(t,f) snprintf(p, cwidth+2, f " ", cwidth, *(t *)column)
+
+ if (!nrow)
+ strcpy(p, no_rows);
+ else {
+ for (i = 0, row = rows; i < nrow; i++, row += rowsize) {
+ for (j = 0; j < ncol; j++) {
+ column = row + coldescs[j].offset;
+ cwidth = cwidths[j];
+
+ switch (coltypes[j]) {
+ case mqi_varchar: SPRINT( char * , "%-*s" ); break;
+ case mqi_integer: SPRINT( int32_t , "%*d" ); break;
+ case mqi_unsignd: SPRINT( uint32_t, "%*u" ); break;
+ case mqi_floating: SPRINT( double , "%*.2lf" ); break;
+ default: memset(p, ' ', cwidth+1 ); break;
+ }
+
+ p += cwidth+1;
+ }
+
+ *(p-1) = '\n';
+ }
+ *p = '\0';
+ }
+
+#undef SPRINT
+
+ return (mql_result_t *)rslt;
+}
+
+const char *mql_result_string_get(mql_result_t *r)
+{
+ result_string_t *rslt = (result_string_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_string, "");
+
+ return rslt->string;
+}
+
+
+mql_result_t *mql_result_list_create(mqi_data_type_t type,
+ int length,
+ void *values)
+{
+ result_list_t *rslt;
+ size_t datalen;
+ char **strs;
+ int *slen = NULL;
+ char *strpool;
+ int poollen = 0;
+ int i;
+
+ MDB_CHECKARG(length > 0 && values, NULL);
+
+ switch (type) {
+ case mqi_varchar:
+ slen = alloca(sizeof(int) * length);
+ for (strs = (char **)values, i = 0; i < length; i++)
+ poollen += (slen[i] = strlen(strs[i]) + 1);
+ datalen = sizeof(char *) * length + poollen;
+ break;
+ case mqi_integer:
+ datalen = sizeof(int32_t) * length;
+ break;
+ case mqi_unsignd:
+ datalen = sizeof(uint32_t) * length;
+ break;
+ case mqi_floating:
+ datalen = sizeof(double) * length;
+ break;
+ default:
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((rslt = calloc(1, sizeof(result_list_t) + datalen))) {
+ rslt->type = mql_result_list;
+ rslt->length = length;
+ rslt->value.type = type;
+
+ if (type != mqi_varchar)
+ memcpy(rslt->value.v.generic, values, datalen);
+ else {
+ strs = (char **)values;
+ strpool = (char *)&rslt->value.v.varchar[length];
+ for (i = 0; i < length; i++) {
+ rslt->value.v.varchar[i] = memcpy(strpool, strs[i], slen[i]);
+ strpool += slen[i];
+ }
+ }
+ }
+
+ return (mql_result_t *)rslt;
+}
+
+int mql_result_list_get_length(mql_result_t *r)
+{
+ result_list_t *rslt = (result_list_t *)r;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_list, -1);
+
+ return rslt->length;
+}
+
+const char *mql_result_list_get_string(mql_result_t *r, int idx,
+ char *buf, int len)
+{
+ result_list_t *rslt = (result_list_t *)r;
+ char *v = NULL;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_list &&
+ idx >= 0 && idx < rslt->length, NULL);
+
+ if ((v = buf))
+ *v = '\0';
+
+ switch (rslt->value.type) {
+
+ case mqi_varchar:
+ if (!v)
+ v = rslt->value.v.varchar[idx];
+ else {
+ strncpy(v, rslt->value.v.varchar[idx], len);
+ v[len-1] = '\0';
+ }
+ break;
+
+ case mqi_integer:
+ if (!v)
+ v = "";
+ else
+ snprintf(v, len, "%d", rslt->value.v.integer[idx]);
+ break;
+
+ case mqi_unsignd:
+ if (!v)
+ v = "";
+ else
+ snprintf(v, len, "%u", rslt->value.v.unsignd[idx]);
+ break;
+
+ case mqi_floating:
+ if (!v)
+ v = "";
+ else
+ snprintf(v, len, "%lf", rslt->value.v.floating[idx]);
+ break;
+
+ default:
+ v = "";
+ break;
+ }
+
+ return v;
+}
+
+int32_t mql_result_list_get_integer(mql_result_t *r, int idx)
+{
+ result_list_t *rslt = (result_list_t *)r;
+ int32_t v = 0;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_list &&
+ idx >= 0 && idx < rslt->length, 0);
+
+ switch (rslt->value.type) {
+ case mqi_varchar: v = strtol(rslt->value.v.varchar[idx], NULL, 10); break;
+ case mqi_integer: v = rslt->value.v.integer[idx]; break;
+ case mqi_unsignd: v = rslt->value.v.unsignd[idx]; break;
+ case mqi_floating: v = rslt->value.v.floating[idx]; break;
+ default: v = 0; break;
+ }
+
+ return v;
+}
+
+int32_t mql_result_list_get_unsigned(mql_result_t *r, int idx)
+{
+ result_list_t *rslt = (result_list_t *)r;
+ uint32_t v = 0;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_list &&
+ idx >= 0 && idx < rslt->length, 0);
+
+ switch (rslt->value.type) {
+ case mqi_varchar: v = strtoul(rslt->value.v.varchar[idx], NULL, 10);break;
+ case mqi_integer: v = rslt->value.v.integer[idx]; break;
+ case mqi_unsignd: v = rslt->value.v.unsignd[idx]; break;
+ case mqi_floating: v = rslt->value.v.floating[idx]; break;
+ default: v = 0; break;
+ }
+
+ return v;
+}
+
+double mql_result_list_get_floating(mql_result_t *r, int idx)
+{
+ result_list_t *rslt = (result_list_t *)r;
+ double v = 0;
+
+ MDB_CHECKARG(rslt && rslt->type == mql_result_list &&
+ idx >= 0 && idx < rslt->length, 0);
+
+ switch (rslt->value.type) {
+ case mqi_varchar: v = strtod(rslt->value.v.varchar[idx], NULL); break;
+ case mqi_integer: v = rslt->value.v.integer[idx]; break;
+ case mqi_unsignd: v = rslt->value.v.unsignd[idx]; break;
+ case mqi_floating: v = rslt->value.v.floating[idx]; break;
+ default: v = 0; break;
+ }
+
+ return v;
+}
+
+void mql_result_free(mql_result_t *r)
+{
+ result_event_colchg_t *colchg = (result_event_colchg_t *)r;
+ mql_result_t *select;
+
+ if (r) {
+ if (r->type == mql_result_event) {
+ if (colchg->event == mqi_column_changed) {
+ select = colchg->select;
+
+ if (select && select->type == mql_result_rows)
+ mql_result_free(colchg->select);
+ }
+ }
+
+ free(r);
+ }
+}
+
+
+static mqi_data_type_t get_column_type(result_rows_t *rslt, int cx)
+{
+ return rslt->cols[cx].type;
+}
+
+static void *get_column_address(result_rows_t *rslt, int cx, int rx)
+{
+ return rslt->data + (rslt->rowsize * rx + rslt->cols[cx].offset);
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <alloca.h>
+#include <errno.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/mql.h>
+#include "mql-parser.h"
+
+typedef struct {
+ mqi_data_type_t type;
+ union {
+ const char *varchar;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+ void *generic;
+ } v;
+} value_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ uint32_t flags;
+} shtable_statement_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ mqi_handle_t table;
+} describe_statement_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ char trnam[0];
+} transact_statement_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ mqi_handle_t table;
+ int ignore;
+ int ncolumn;
+ mqi_column_desc_t *columns;
+ void *rows[2];
+ int nbind;
+ value_t values[0];
+} insert_statement_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ mqi_handle_t table;
+ mqi_column_desc_t *columns;
+ mqi_cond_entry_t *cond;
+ int nbind;
+ value_t values[0];
+} update_statement_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ mqi_handle_t table;
+ mqi_cond_entry_t *cond;
+ int nbind;
+ value_t values[0];
+} delete_statement_t;
+
+typedef struct {
+ mql_statement_type_t type;
+ mqi_handle_t table;
+ int rowsize;
+ int ncolumn;
+ mqi_column_desc_t *columns;
+ char **colnames;
+ mqi_data_type_t *coltypes;
+ int *colsizes;
+ mqi_cond_entry_t *cond;
+ int nbind;
+ value_t values[0];
+} select_statement_t;
+
+
+
+static void count_condition_values(int, mqi_cond_entry_t *, int*, int*, int*);
+static void count_column_values(mqi_column_desc_t *, mqi_data_type_t*, void*,
+ int *, int *, int *);
+static void copy_column_values(int, mqi_data_type_t *, mqi_column_desc_t *,
+ mqi_column_desc_t *, value_t **, value_t **,
+ char **, void *, void *);
+static void copy_conditions_and_values(int,mqi_cond_entry_t*,mqi_cond_entry_t*,
+ value_t**, value_t**, char**);
+
+static mql_result_t *exec_show_tables(mql_result_type_t, shtable_statement_t*);
+static mql_result_t *exec_describe(mql_result_type_t, describe_statement_t *);
+static mql_result_t *exec_begin(transact_statement_t *);
+static mql_result_t *exec_commit(transact_statement_t *);
+static mql_result_t *exec_rollback(transact_statement_t *);
+static mql_result_t *exec_insert(insert_statement_t *);
+static mql_result_t *exec_update(update_statement_t *);
+static mql_result_t *exec_delete(delete_statement_t *);
+static mql_result_t *exec_select(mql_result_type_t, select_statement_t *);
+
+static int bind_update_value(update_statement_t *,int,mqi_data_type_t,va_list);
+static int bind_delete_value(delete_statement_t *,int,mqi_data_type_t,va_list);
+static int bind_select_value(select_statement_t *,int,mqi_data_type_t,va_list);
+static int bind_value(value_t *, mqi_data_type_t, va_list);
+
+mql_statement_t *mql_make_show_tables_statement(uint32_t flags)
+{
+ shtable_statement_t *st;
+
+ if (!(st = calloc(1, sizeof(shtable_statement_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ st->type = mql_statement_show_tables;
+ st->flags = flags;
+
+ return (mql_statement_t *)st;
+}
+
+mql_statement_t *mql_make_describe_statement(mqi_handle_t table)
+{
+ describe_statement_t *dis;
+
+ MDB_CHECKARG(table != MQI_HANDLE_INVALID, NULL);
+
+ if (!(dis = calloc(1, sizeof(describe_statement_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ dis->type = mql_statement_describe;
+ dis->table = table;
+
+ return (mql_statement_t *)dis;
+}
+
+mql_statement_t *mql_make_transaction_statement(mql_statement_type_t type,
+ char *trnam)
+{
+ transact_statement_t *tra;
+
+ MDB_CHECKARG((type == mql_statement_begin ||
+ type == mql_statement_commit ||
+ type == mql_statement_rollback)
+ && trnam && trnam[0], NULL);
+
+ if (!(tra = calloc(1, sizeof(transact_statement_t) + strlen(trnam) + 1))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ tra->type = type;
+ strcpy(tra->trnam, trnam);
+
+ return (mql_statement_t *)tra;
+}
+
+
+mql_statement_t *mql_make_insert_statement(mqi_handle_t table,
+ int ignore,
+ int ncolumn,
+ mqi_data_type_t *coltypes,
+ mqi_column_desc_t *columns,
+ void *data)
+{
+ insert_statement_t *ins;
+ value_t *bindv;
+ value_t *constv;
+ char *strpool;
+ int vallgh;
+ int cdsclgh;
+ int datalgh;
+ int nbind = 0;
+ int nconst = 0;
+ int poollen = 0;
+
+ MDB_CHECKARG(table != MQI_HANDLE_INVALID &&
+ ncolumn > 0 && columns && data, NULL);
+
+ /*
+ * calculate the number of constant and bindable values
+ */
+ count_column_values(columns, coltypes, data, &nbind,&nconst,&poollen);
+
+ /*
+ * set up the statement structure
+ */
+ vallgh = sizeof( value_t ) * (nbind + nconst);
+ cdsclgh = sizeof(mqi_column_desc_t) * (ncolumn + 1);
+
+ datalgh = vallgh + cdsclgh + poollen;
+
+ if (!(ins = calloc(1, sizeof(insert_statement_t) + datalgh))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ ins->type = mql_statement_insert;
+ ins->table = table;
+ ins->ignore = ignore;
+ ins->columns = (mqi_column_desc_t *)(ins->values + (nbind + nconst));
+ ins->rows[0] = ins->values;
+ ins->nbind = nbind;
+
+ strpool = (void *)(ins->columns + (ncolumn + 1));
+
+ /*
+ * copy column values
+ */
+ bindv = ins->values;
+ constv = bindv + nbind;
+
+ copy_column_values(ncolumn, coltypes, columns, ins->columns,
+ &bindv, &constv, &strpool, data, ins->values);
+
+ return (mql_statement_t *)ins;
+}
+
+
+mql_statement_t *mql_make_update_statement(mqi_handle_t table,
+ int ncond,
+ mqi_cond_entry_t *conds,
+ int ncolumn,
+ mqi_data_type_t *coltypes,
+ mqi_column_desc_t *columns,
+ void *data)
+{
+ update_statement_t *upd;
+ value_t *bindv;
+ value_t *constv;
+ char *strpool;
+ int vallgh;
+ int cdsclgh;
+ int cndlgh;
+ int datalgh;
+ int nbind = 0;
+ int nconst = 0;
+ int poollen = 0;
+
+ MDB_CHECKARG(table != MQI_HANDLE_INVALID &&
+ (!ncond || (ncond > 0 && conds)) &&
+ ncolumn > 0 && coltypes && columns && data, NULL);
+
+ /*
+ * calculate the number of constant and bindable values
+ */
+ count_column_values(columns, coltypes, data, &nbind,&nconst,&poollen);
+ count_condition_values(ncond, conds, &nbind, &nconst, &poollen);
+
+ /*
+ * set up the statement structure
+ */
+ vallgh = sizeof( value_t ) * (nbind + nconst);
+ cdsclgh = sizeof(mqi_column_desc_t) * (ncolumn + 1);
+ cndlgh = sizeof(mqi_cond_entry_t ) * ncond;
+
+ datalgh = vallgh + cdsclgh + cndlgh + poollen;
+
+ if (!(upd = calloc(1, sizeof(update_statement_t) + datalgh))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ upd->type = mql_statement_update;
+ upd->table = table;
+ upd->columns = (mqi_column_desc_t *)(upd->values + (nbind + nconst));
+ upd->cond = (mqi_cond_entry_t *)(upd->columns + (ncolumn + 1));
+ upd->nbind = nbind;
+
+ strpool = (void *)(upd->cond + ncond);
+
+ /*
+ * copy column values, conditions and their values
+ */
+ bindv = upd->values;
+ constv = bindv + nbind;
+
+ copy_column_values(ncolumn, coltypes, columns, upd->columns,
+ &bindv, &constv, &strpool, data, upd->values);
+
+ copy_conditions_and_values(ncond, conds, upd->cond,
+ &bindv, &constv, &strpool);
+
+ return (mql_statement_t *)upd;
+}
+
+
+mql_statement_t *mql_make_delete_statement(mqi_handle_t table,
+ int ncond,
+ mqi_cond_entry_t *conds)
+{
+ delete_statement_t *del;
+ value_t *bindv;
+ value_t *constv;
+ char *strpool;
+ int vallgh;
+ int cndlgh;
+ int datalgh;
+ int nbind = 0;
+ int nconst = 0;
+ int poollen = 0;
+
+ MDB_CHECKARG(table != MQI_HANDLE_INVALID &&
+ (!ncond || (ncond > 0 && conds)), NULL);
+
+ /*
+ * calculate the number of constant and bindable values
+ */
+ count_condition_values(ncond, conds, &nbind, &nconst, &poollen);
+
+ /*
+ * set up the statement structure
+ */
+ vallgh = sizeof( value_t ) * (nbind + nconst);
+ cndlgh = sizeof(mqi_cond_entry_t ) * ncond;
+
+ datalgh = vallgh + cndlgh + poollen;
+
+ if (!(del = calloc(1, sizeof(delete_statement_t) + datalgh))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ del->type = mql_statement_delete;
+ del->table = table;
+ del->cond = (mqi_cond_entry_t *)(del->values + (nbind + nconst));
+ del->nbind = nbind;
+
+ strpool = (void *)(del->cond + ncond);
+
+ /*
+ * copy column values, conditions and their values
+ */
+ bindv = del->values;
+ constv = bindv + nbind;
+
+ copy_conditions_and_values(ncond, conds, del->cond,
+ &bindv, &constv, &strpool);
+
+ return (mql_statement_t *)del;
+}
+
+mql_statement_t *mql_make_select_statement(mqi_handle_t table,
+ int rowsize,
+ int ncond,
+ mqi_cond_entry_t *conds,
+ int ncolumn,
+ char **colnames,
+ mqi_data_type_t *coltypes,
+ int *colsizes,
+ mqi_column_desc_t *columns)
+{
+ select_statement_t *sel;
+ value_t *bindv;
+ value_t *constv;
+ char *strpool;
+ int vallgh;
+ int cdsclgh;
+ int cnamlgh;
+ int ctyplgh;
+ int csizlgh;
+ int cndlgh;
+ int datalgh;
+ int colnamlgh[MQI_COLUMN_MAX];
+ int nbind = 0;
+ int nconst = 0;
+ int poollen = 0;
+ int i;
+
+ MDB_CHECKARG(table != MQI_HANDLE_INVALID &&
+ (!ncond || (ncond >= 0 && conds)) &&
+ ncolumn > 0 && columns, NULL);
+
+ /*
+ * calculate the number of constant and bindable values
+ */
+ count_condition_values(ncond, conds, &nbind, &nconst, &poollen);
+
+ for (i = 0; i < ncolumn; i++)
+ poollen += (colnamlgh[i] = strlen(colnames[i]) + 1);
+
+
+ /*
+ * set up the statement structure
+ */
+ vallgh = sizeof( value_t ) * (nbind + nconst);
+ cdsclgh = sizeof(mqi_column_desc_t) * (ncolumn + 1);
+ cnamlgh = sizeof( char * ) * ncolumn;
+ ctyplgh = sizeof( mqi_data_type_t ) * ncolumn;
+ csizlgh = sizeof( int ) * ncolumn;
+ cndlgh = sizeof(mqi_cond_entry_t ) * ncond;
+
+ datalgh = vallgh + cdsclgh + cnamlgh + ctyplgh + csizlgh + cndlgh +poollen;
+
+ if (!(sel = calloc(1, sizeof(select_statement_t) + datalgh))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ sel->type = mql_statement_select;
+ sel->table = table;
+ sel->rowsize = rowsize;
+ sel->ncolumn = ncolumn;
+ sel->columns = (mqi_column_desc_t *)(sel->values + (nbind + nconst));
+ sel->colnames = (char **)(sel->columns + (ncolumn+1));
+ sel->coltypes = (mqi_data_type_t *)(sel->colnames + ncolumn);
+ sel->colsizes = (int *)(sel->coltypes + ncolumn);
+ sel->cond = (mqi_cond_entry_t *)(sel->colsizes + ncolumn);
+ sel->nbind = nbind;
+
+ strpool = (char *)(sel->cond + ncond);
+
+ if (!ncond)
+ sel->cond = NULL;
+
+ /*
+ * copy conditions and values
+ */
+ bindv = sel->values;
+ constv = bindv + nbind;
+
+ copy_conditions_and_values(ncond, conds, sel->cond,
+ &bindv, &constv, &strpool);
+ /*
+ * copy column descriptors, types and sizes
+ */
+ memcpy(sel->columns, columns, cdsclgh);
+ memcpy(sel->coltypes, coltypes, ctyplgh);
+ memcpy(sel->colsizes, colsizes, csizlgh);
+
+ /*
+ * copy column names
+ */
+ for (i = 0; i < ncolumn; i++) {
+ sel->colnames[i] = (char*)memcpy(strpool, colnames[i], colnamlgh[i]);
+ strpool += colnamlgh[i];
+ }
+
+ return (mql_statement_t *)sel;
+}
+
+int
+mql_bind_value(mql_statement_t *s, int id, mqi_data_type_t type, ...)
+{
+ int idx = id - 1;
+ va_list data;
+ int sts;
+
+ MDB_CHECKARG(s && id > 0, -1);
+
+ va_start(data, type);
+
+ switch (s->type) {
+
+ case mql_statement_update:
+ sts = bind_update_value((update_statement_t *)s, idx, type, data);
+ break;
+
+ case mql_statement_delete:
+ sts = bind_delete_value((delete_statement_t *)s, idx, type, data);
+ break;
+
+ case mql_statement_select:
+ sts = bind_select_value((select_statement_t *)s, idx, type, data);
+ break;
+
+ default:
+ errno = EBADRQC;
+ sts = -1;
+ break;
+ }
+
+ va_end(data);
+
+ return sts;
+}
+
+mql_result_t *mql_exec_statement(mql_result_type_t type, mql_statement_t *s)
+{
+ mql_result_t *result;
+
+ MDB_CHECKARG(s, NULL);
+
+ switch (s->type) {
+
+ case mql_statement_show_tables:
+ result = exec_show_tables(type, (shtable_statement_t *)s);
+ break;
+
+ case mql_statement_describe:
+ result = exec_describe(type, (describe_statement_t *)s);
+ break;
+
+ case mql_statement_begin:
+ result = exec_begin((transact_statement_t *)s);
+ break;
+
+ case mql_statement_commit:
+ result = exec_commit((transact_statement_t *)s);
+ break;
+
+ case mql_statement_rollback:
+ result = exec_rollback((transact_statement_t *)s);
+ break;
+
+ case mql_statement_insert:
+ result = exec_insert((insert_statement_t *)s);
+ break;
+
+ case mql_statement_update:
+ result = exec_update((update_statement_t *)s);
+ break;
+
+ case mql_statement_delete:
+ result = exec_delete((delete_statement_t *)s);
+ break;
+
+ case mql_statement_select:
+ result = exec_select(type, (select_statement_t *)s);
+ break;
+
+ default:
+ result = mql_result_error_create(EBADRQC, "statement execution failed:"
+ " %s", strerror(EBADRQC));
+ break;
+ }
+
+ return result;
+}
+
+
+void mql_statement_free(mql_statement_t *s)
+{
+ free(s);
+}
+
+
+static void count_column_values(mqi_column_desc_t *cds,
+ mqi_data_type_t *coltypes,
+ void *data,
+ int *nbind_ret,
+ int *nconst_ret,
+ int *poollen_ret)
+{
+ mqi_column_desc_t *cd;
+ int offs;
+ int bidx;
+ char *str;
+ int nbind = *nbind_ret;
+ int nconst = 0;
+ int poollen = 0;
+ int i;
+
+ for (i = 0; (cd = cds + i)->cindex >= 0; i++) {
+
+ if ((offs = cd->offset) >= 0) {
+ if (coltypes[i] == mqi_varchar && (str = *(char**)(data + offs)))
+ poollen += strlen(str) + 1;
+ nconst++;
+ }
+ else {
+ if ((bidx = -(cd->offset - 1)) + 1 > nbind)
+ nbind = bidx + 1;
+ }
+ }
+
+ *nbind_ret = nbind;
+ *nconst_ret += nconst;
+ *poollen_ret += poollen;
+}
+
+static void count_condition_values(int ncond,
+ mqi_cond_entry_t *conds,
+ int *nbind_ret,
+ int *nconst_ret,
+ int *poollen_ret)
+{
+ mqi_cond_entry_t *ce;
+ mqi_variable_t *var;
+ uint32_t flags;
+ int bidx;
+ char *str;
+ int nbind = *nbind_ret;
+ int nconst = 0;
+ int poollen = 0;
+ int i;
+
+ for (i = 0; i < ncond; i++) {
+ ce = conds + i;
+
+ if (ce->type == mqi_variable) {
+ var = &ce->u.variable;
+ flags = var->flags;
+
+ if (!(flags & MQL_BINDABLE)) {
+ if (var->type == mqi_varchar && (str = *(var->v.varchar)))
+ poollen += strlen(str) + 1;
+ nconst++;
+ }
+ else {
+ if ((bidx = MQL_BIND_INDEX(flags)) + 1 > nbind)
+ nbind = bidx + 1;
+ }
+
+ }
+ }
+
+ *nbind_ret = nbind;
+ *nconst_ret += nconst;
+ *poollen_ret += poollen;
+}
+
+static void copy_column_values(int ncol,
+ mqi_data_type_t *coltypes,
+ mqi_column_desc_t *src_cols,
+ mqi_column_desc_t *dst_cols,
+ value_t **bindv_ptr,
+ value_t **constv_ptr,
+ char **strpool_ptr,
+ void *data,
+ void *values)
+{
+ value_t *bindv = *bindv_ptr;
+ value_t *constv = *constv_ptr;
+ void *strpool = *strpool_ptr;
+ mqi_column_desc_t *col;
+ value_t *val;
+ mqi_data_type_t type;
+ int offs;
+ void *vptr;
+ char *str;
+ int len;
+ int i;
+
+ for (i = 0; i < ncol; i++) {
+ type = coltypes[i];
+ *(col = dst_cols + i) = src_cols[i];
+
+ if ((offs = col->offset) < 0)
+ val = bindv - (offs + 1);
+ else {
+ val = constv++;
+ vptr = data + offs;
+
+ switch (type) {
+ case mqi_varchar:
+ str = *(char **)vptr;
+ len = strlen(str) + 1;
+ val->v.varchar = (char *)memcpy(strpool, str, len);
+ strpool += len;
+ break;
+ case mqi_integer:
+ val->v.integer = *(int32_t *)vptr;
+ break;
+ case mqi_unsignd:
+ val->v.unsignd = *(uint32_t *)vptr;
+ break;
+ case mqi_floating:
+ val->v.floating = *(double *)vptr;
+ break;
+ default:
+ break;
+ }
+ }
+
+ val->type = type;
+ col->offset = (void *)&val->v.generic - values;
+ }
+
+ col = dst_cols + i;
+ col->cindex = -1;
+ col->offset = -1;
+
+ *bindv_ptr = bindv;
+ *constv_ptr = constv;
+ *strpool_ptr = strpool;
+}
+
+static void copy_conditions_and_values(int ncond,
+ mqi_cond_entry_t *src_conds,
+ mqi_cond_entry_t *dst_conds,
+ value_t **bindv_ptr,
+ value_t **constv_ptr,
+ char **strpool_ptr)
+{
+ value_t *bindv = *bindv_ptr;
+ value_t *constv = *constv_ptr;
+ char *strpool = *strpool_ptr;
+ mqi_cond_entry_t *cond;
+ mqi_variable_t *var;
+ mqi_data_type_t type;
+ uint32_t flags;
+ value_t *val;
+ char *str;
+ int len;
+ int i;
+
+ for (i = 0; i < ncond; i++) {
+ *(cond = dst_conds + i) = src_conds[i];
+
+ if (cond->type == mqi_variable) {
+ var = &cond->u.variable;
+ type = var->type;
+ flags = var->flags;
+
+ if ((flags & MQL_BINDABLE))
+ val = bindv + MQL_BIND_INDEX(flags);
+ else {
+ val = constv++;
+
+ switch (type) {
+ case mqi_varchar:
+ str = *(var->v.varchar);
+ len = strlen(str) + 1;
+ val->v.varchar = (char *)memcpy(strpool, str, len);
+ strpool += len;
+ break;
+ case mqi_integer:
+ val->v.integer = *(var->v.integer);
+ break;
+ case mqi_unsignd:
+ val->v.unsignd = *(var->v.unsignd);
+ break;
+ case mqi_floating:
+ val->v.floating = *(var->v.floating);
+ break;
+ default:
+ break;
+ }
+ }
+
+ val->type = type;
+ var->v.generic = (void *)&val->v.generic;
+ }
+ }
+
+ *bindv_ptr = bindv;
+ *constv_ptr = constv;
+ *strpool_ptr = strpool;
+}
+
+static mql_result_t *exec_show_tables(mql_result_type_t type,
+ shtable_statement_t *st)
+{
+ mql_result_t *rslt;
+ char *names[4096];
+ int n;
+
+ MQI_UNUSED(type);
+
+ if ((n = mqi_show_tables(st->flags, names, MQI_DIMENSION(names))) < 0) {
+ rslt = mql_result_error_create(errno, "can't show tables: %s",
+ strerror(errno));
+ }
+ else {
+ if (!n)
+ rslt = mql_result_error_create(0, "no tables");
+ else
+ rslt = mql_result_list_create(mqi_string, n, names);
+ }
+
+ return rslt;
+}
+
+static mql_result_t *exec_describe(mql_result_type_t type,
+ describe_statement_t *d)
+{
+ mql_result_t *rslt;
+ mqi_column_def_t defs[MQI_COLUMN_MAX];
+ int n;
+
+ if ((n = mqi_describe(d->table, defs, MQI_COLUMN_MAX)) < 0) {
+ rslt = mql_result_error_create(errno, "describe failed: %s",
+ strerror(errno));
+ }
+ else {
+ switch (type) {
+ case mql_result_columns:
+ rslt = mql_result_columns_create(n, defs);
+ break;
+ case mql_result_string:
+ rslt = mql_result_string_create_column_list(n, defs);
+ break;
+ default:
+ rslt = mql_result_error_create(EINVAL, "describe failed: invalid"
+ " result type %d", type);
+ break;
+ }
+ }
+
+ return rslt;
+}
+
+static mql_result_t *exec_begin(transact_statement_t *b)
+{
+ mql_result_t *rslt;
+
+ if (mql_begin_transaction(b->trnam) == 0)
+ rslt = mql_result_success_create();
+ else {
+ rslt = mql_result_error_create(errno, "begin failed: %s",
+ strerror(errno));
+ }
+
+ return rslt;
+}
+
+
+static mql_result_t *exec_commit(transact_statement_t *c)
+{
+ mql_result_t *rslt;
+
+ if (mql_commit_transaction(c->trnam) == 0)
+ rslt = mql_result_success_create();
+ else {
+ rslt = mql_result_error_create(errno, "commit failed: %s",
+ strerror(errno));
+ }
+
+ return rslt;
+}
+
+
+static mql_result_t *exec_rollback(transact_statement_t *r)
+{
+ mql_result_t *rslt;
+
+ if (mql_rollback_transaction(r->trnam) == 0)
+ rslt = mql_result_success_create();
+ else {
+ rslt = mql_result_error_create(errno, "rollback failed: %s",
+ strerror(errno));
+ }
+
+ return rslt;
+}
+
+
+static mql_result_t *exec_insert(insert_statement_t *i)
+{
+ mql_result_t *rslt;
+ int n;
+
+ if ((n = mqi_insert_into(i->table, i->ignore, i->columns, i->rows)) >= 0)
+ rslt = mql_result_error_create(0, "inserted %d rows", n);
+ else {
+ rslt = mql_result_error_create(errno, "insert error: %s",
+ strerror(errno));
+ }
+
+ return rslt;
+}
+
+static mql_result_t *exec_update(update_statement_t *u)
+{
+ mql_result_t *rslt;
+ int n;
+
+ if ((n = mqi_update(u->table, u->cond, u->columns, u->values)) >= 0)
+ rslt = mql_result_error_create(0, "updated %d rows", n);
+ else {
+ rslt = mql_result_error_create(errno, "update error: %s",
+ strerror(errno));
+ }
+
+ return rslt;
+}
+
+static mql_result_t *exec_delete(delete_statement_t *d)
+{
+ mql_result_t *rslt;
+ int n;
+
+ if ((n = mqi_delete_from(d->table, d->cond)) >= 0)
+ rslt = mql_result_error_create(0, "deleted %d rows", n);
+ else {
+ rslt = mql_result_error_create(errno, "delete error: %s",
+ strerror(errno));
+ }
+
+ return rslt;
+}
+
+static mql_result_t *exec_select(mql_result_type_t type, select_statement_t *s)
+{
+ mql_result_t *rslt;
+ int maxrow;
+ int nrow;
+ void *rows;
+
+ if ((maxrow = mqi_get_table_size(s->table)) < 0)
+ rslt = mql_result_error_create(ENOENT, "can't access table");
+ else {
+ if (!maxrow) {
+ rows = alloca(s->rowsize);
+ nrow = 0;
+ }
+ else {
+ rows = alloca(maxrow * s->rowsize);
+ nrow = mqi_select(s->table, s->cond, s->columns,
+ rows, s->rowsize, maxrow);
+ }
+
+ if (nrow < 0) {
+ rslt = mql_result_error_create(errno, "select error: %s",
+ strerror(errno));
+ }
+ else {
+ switch (type) {
+ case mql_result_rows:
+ rslt = mql_result_rows_create(s->ncolumn, s->columns,
+ s->coltypes, s->colsizes,
+ nrow, s->rowsize, rows);
+ break;
+ case mql_result_string:
+ rslt = mql_result_string_create_row_list(
+ s->ncolumn, s->colnames, s->columns,
+ s->coltypes, s->colsizes,
+ nrow, s->rowsize, rows);
+ break;
+ default:
+ rslt = mql_result_error_create(EINVAL, "select failed: invalid"
+ " result type %d", type);
+ break;
+ }
+ }
+ }
+
+ return rslt;
+}
+
+static int bind_update_value(update_statement_t *u,
+ int idx,
+ mqi_data_type_t type,
+ va_list data)
+{
+ if (idx >= u->nbind) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return bind_value(u->values + idx, type, data);
+}
+
+
+static int bind_delete_value(delete_statement_t *d,
+ int idx,
+ mqi_data_type_t type,
+ va_list data)
+{
+ if (idx >= d->nbind) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return bind_value(d->values + idx, type, data);
+}
+
+
+static int bind_select_value(select_statement_t *s,
+ int idx,
+ mqi_data_type_t type,
+ va_list data)
+{
+ if (idx >= s->nbind) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ return bind_value(s->values + idx, type, data);
+}
+
+
+static int bind_value(value_t *v, mqi_data_type_t type, va_list data)
+{
+ if (type == v->type) {
+ switch (type) {
+ case mqi_varchar: v->v.varchar = va_arg(data, char *); return 0;
+ case mqi_integer: v->v.integer = va_arg(data, int32_t); return 0;
+ case mqi_unsignd: v->v.unsignd = va_arg(data, uint32_t); return 0;
+ case mqi_floating: v->v.floating = va_arg(data, double); return 0;
+ default: break;
+ }
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <alloca.h>
+#include <errno.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/mqi.h>
+#include <murphy-db/hash.h>
+#include "mql-parser.h"
+
+/* Note: HANDLE_TO_PTR(MQI_HANDLE_INVALID) == NULL */
+#define HANDLE_TO_PTR(h) (((void *)1) + (h))
+#define PTR_TO_HANDLE(p) (mqi_handle_t)((p) - ((void *)1))
+
+static mdb_hash_t *transact_handles;
+
+static int init(void);
+static int add_handle(char *, mqi_handle_t);
+static mqi_handle_t delete_handle(char *);
+
+
+int mql_begin_transaction(char *name)
+{
+ mqi_handle_t h;
+
+ MDB_CHECKARG(name, -1);
+
+ if ((h = mqi_begin_transaction()) == MQI_HANDLE_INVALID)
+ return -1;
+
+ if (add_handle(name, h) < 0) {
+ mqi_rollback_transaction(h);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int mql_rollback_transaction(char *name)
+{
+ mqi_handle_t h;
+
+ MDB_CHECKARG(name, -1);
+
+ if ((h = delete_handle(name)) == MQI_HANDLE_INVALID)
+ return -1;
+
+ if (mqi_rollback_transaction(h) < 0)
+ return -1;
+
+ return 0;
+}
+
+int mql_commit_transaction(char *name)
+{
+ mqi_handle_t h;
+
+ MDB_CHECKARG(name, -1);
+
+ if ((h = delete_handle(name)) == MQI_HANDLE_INVALID)
+ return -1;
+
+ if (mqi_commit_transaction(h) < 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int init(void)
+{
+ static bool done = false;
+
+ int sts = 0;
+
+ if (!done) {
+ if (!(transact_handles = MDB_HASH_TABLE_CREATE(string, 16)))
+ sts = -1;
+
+ done = true;
+ }
+
+ return sts;
+}
+
+static int add_handle(char *name, mqi_handle_t handle)
+{
+ if (init() < 0)
+ return -1;
+
+ if (mdb_hash_add(transact_handles, 0,name, HANDLE_TO_PTR(handle)) < 0)
+ return -1;
+
+ return 0;
+}
+
+static mqi_handle_t delete_handle(char *name)
+{
+ void *ptr;
+
+ if (init() < 0)
+ return MQI_HANDLE_INVALID;
+
+ if (!(ptr = mdb_hash_delete(transact_handles, 0,name)))
+ return MQI_HANDLE_INVALID;
+
+ return PTR_TO_HANDLE(ptr);
+}
+
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <alloca.h>
+#include <errno.h>
+
+#include <murphy-db/assert.h>
+#include <murphy-db/mql.h>
+#include <murphy-db/hash.h>
+#include "mql-parser.h"
+
+#ifndef MQL_CALLBACK_HASH_CHAINS
+#define MQL_CALLBACK_HASH_CHAINS 128
+#endif
+
+#ifndef MQL_TRIGGER_HASH_CHAINS
+#define MQL_TRIGGER_HASH_CHAINS 128
+#endif
+
+
+typedef enum trigger_type_e trigger_type_t;
+typedef struct select_s select_t;
+typedef struct column_s column_t;
+typedef struct trigger_s trigger_t;
+typedef struct trigger_s transact_trigger_t;
+typedef struct trigger_s table_trigger_t;
+typedef struct row_trigger_s row_trigger_t;
+typedef struct column_trigger_s column_trigger_t;
+
+
+struct mql_callback_s {
+ int refcnt;
+ char *name;
+ mql_result_type_t rtype;
+ mql_trigger_cb_t function;
+ void *user_data;
+};
+
+enum trigger_type_e {
+ trigger_unknown = 0,
+ trigger_first = trigger_unknown,
+
+ trigger_transaction,
+ trigger_table,
+ trigger_row,
+ trigger_column,
+
+ trigger_last
+};
+
+#define TRIGGER_COMMON \
+ char *name; \
+ trigger_type_t type; \
+ mql_callback_t *callback
+
+struct select_s {
+ struct {
+ int ncol;
+ char **names;
+ mqi_column_desc_t *descs;
+ mqi_data_type_t *types;
+ int *sizes;
+ } column;
+ int rowsize;
+ struct {
+ char *addr;
+ size_t size;
+ } strpool;
+};
+
+struct column_s {
+ int index;
+ mqi_data_type_t type;
+};
+
+struct trigger_s {
+ TRIGGER_COMMON;
+};
+
+struct row_trigger_s {
+ TRIGGER_COMMON;
+ mqi_handle_t table;
+ select_t select;
+ uint8_t data[0];
+};
+
+struct column_trigger_s {
+ TRIGGER_COMMON;
+ mqi_handle_t table;
+ column_t column;
+ select_t select;
+ uint8_t data[0];
+};
+
+
+static mdb_hash_t *callbacks;
+static mdb_hash_t *triggers;
+
+static int unref_callback(mql_callback_t *);
+static mql_callback_t *ref_callback(mql_callback_t *);
+
+static void column_event_callback(mqi_event_t *, void *);
+static void row_event_callback(mqi_event_t *, void *);
+static void table_event_callback(mqi_event_t *, void *);
+static void transaction_event_callback(mqi_event_t *, void *);
+
+
+int mql_register_callback(const char *name,
+ mql_result_type_t rtype,
+ mql_trigger_cb_t function,
+ void *user_data)
+{
+ mql_callback_t *cb;
+
+ MDB_CHECKARG(name && *name && function &&
+ (rtype == mql_result_event ||
+ rtype == mql_result_string ||
+ rtype == mql_result_dontcare), -1);
+
+ if (!callbacks) {
+ callbacks = MDB_HASH_TABLE_CREATE(string, MQL_CALLBACK_HASH_CHAINS);
+ MDB_PREREQUISITE(callbacks, -1);
+ }
+
+ if (rtype == mql_result_dontcare)
+ rtype = mql_result_event;
+
+ if (!(cb = calloc(1, sizeof(mql_callback_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ cb->refcnt = 0;
+ cb->name = strdup(name);
+ cb->rtype = rtype;
+ cb->function = function;
+ cb->user_data = user_data;
+
+ if (!cb->name || mdb_hash_add(callbacks, 0,cb->name, cb) < 0) {
+ free(cb->name);
+ free(cb);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int mql_unregister_callback(const char *name)
+{
+ mql_callback_t *cb;
+
+ MDB_CHECKARG(name, -1);
+
+ if (!(cb = mdb_hash_delete(callbacks, 0,(void *)name)))
+ return -1;
+
+ return unref_callback(cb);
+}
+
+
+mql_callback_t *mql_find_callback(char *name)
+{
+ mql_callback_t *cb;
+
+ MDB_CHECKARG(name, NULL);
+ MDB_PREREQUISITE(callbacks, NULL);
+
+ cb = mdb_hash_get_data(callbacks, 0,name);
+
+ return cb;
+}
+
+int mql_create_column_trigger(char *name,
+ mqi_handle_t table,
+ int colidx,
+ mqi_data_type_t coltyp,
+ mql_callback_t *callback,
+ int nselcol,
+ char **selcolnams,
+ mqi_column_desc_t *selcoldscs,
+ mqi_data_type_t *selcoltypes,
+ int *selcolsizes,
+ int rowsize)
+{
+ column_trigger_t *tr;
+ size_t nlens[MQI_COLUMN_MAX];
+ size_t asiz;
+ size_t nsiz;
+ size_t dsiz;
+ size_t tsiz;
+ size_t ssiz;
+ size_t size;
+ uint8_t *data;
+ int sts;
+ int i;
+
+ MDB_CHECKARG(name && table != MQI_HANDLE_INVALID && callback &&
+ (!nselcol || (nselcol > 0 && nselcol < MQI_COLUMN_MAX &&
+ selcoldscs && selcolsizes && rowsize > 0)), -1);
+
+ if (!triggers) {
+ triggers = MDB_HASH_TABLE_CREATE(string, MQL_TRIGGER_HASH_CHAINS);
+ MDB_PREREQUISITE(triggers, -1);
+ }
+
+ if (!nselcol) {
+ nsiz = asiz = dsiz = tsiz = ssiz = 0;
+ size = sizeof(column_trigger_t);
+ }
+ else {
+ nsiz = asiz = sizeof(char *) * nselcol;
+
+ for (i = 0; i < nselcol; i++)
+ nsiz += (nlens[i] = strlen(selcolnams[i]) + 1);
+
+ dsiz = sizeof(mqi_column_desc_t) * (nselcol ? nselcol + 1 : 0);
+ tsiz = sizeof(mqi_data_type_t) * nselcol;
+ ssiz = sizeof(int) * nselcol;
+ size = sizeof(column_trigger_t) + nsiz + dsiz + tsiz + ssiz;
+ }
+
+ if (!(tr = calloc(1, size))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ tr->name = strdup(name);
+ tr->type = trigger_column;
+ tr->callback = ref_callback(callback);
+
+ tr->table = table;
+
+ tr->column.index = colidx;
+ tr->column.type = coltyp;
+
+ if (nselcol > 0) {
+ data = tr->data;
+
+ tr->select.column.ncol = nselcol;
+ tr->select.column.names = (char **)data;
+ tr->select.column.descs = (mqi_column_desc_t *)(data += asiz);
+ tr->select.column.types = (mqi_data_type_t *)(data += dsiz);
+ tr->select.column.sizes = (int *)(data += tsiz);
+
+ tr->select.strpool.addr = (char *)(data += ssiz);
+ tr->select.strpool.size = nsiz - asiz;
+
+ tr->select.rowsize = rowsize;
+
+ memcpy(tr->select.column.descs, selcoldscs , dsiz);
+ memcpy(tr->select.column.types, selcoltypes, tsiz);
+ memcpy(tr->select.column.sizes, selcolsizes, ssiz);
+
+ for (i = 0; i < nselcol; i++) {
+ tr->select.column.names[i] = (char *)data;
+ memcpy(data, selcolnams[i], nlens[i]);
+ data += nlens[i];
+ }
+ }
+
+ if (!tr->name || mdb_hash_add(triggers, 0,tr->name, tr) < 0) {
+ free(tr->name);
+ free(tr);
+ return -1;
+ }
+
+ sts = mqi_create_column_trigger(table, colidx, column_event_callback, tr,
+ tr->select.column.descs);
+ return sts;
+}
+
+
+
+int mql_create_row_trigger(char *name,
+ mqi_handle_t table,
+ mql_callback_t *callback,
+ int nselcol,
+ char **selcolnams,
+ mqi_column_desc_t *selcoldscs,
+ mqi_data_type_t *selcoltypes,
+ int *selcolsizes,
+ int rowsize)
+{
+ row_trigger_t *tr;
+ size_t nlens[MQI_COLUMN_MAX];
+ size_t asiz;
+ size_t nsiz;
+ size_t dsiz;
+ size_t tsiz;
+ size_t ssiz;
+ size_t size;
+ uint8_t *data;
+ int sts;
+ int i;
+
+ MDB_CHECKARG(name && table != MQI_HANDLE_INVALID && callback &&
+ nselcol > 0 && nselcol < MQI_COLUMN_MAX &&
+ selcoldscs && selcolsizes && rowsize > 0, -1);
+
+ if (!triggers) {
+ triggers = MDB_HASH_TABLE_CREATE(string, MQL_TRIGGER_HASH_CHAINS);
+ MDB_PREREQUISITE(triggers, -1);
+ }
+
+ nsiz = asiz = sizeof(char *) * nselcol;
+
+ for (i = 0; i < nselcol; i++)
+ nsiz += (nlens[i] = strlen(selcolnams[i]) + 1);
+
+ dsiz = sizeof(mqi_column_desc_t) * (nselcol ? nselcol + 1 : 0);
+ tsiz = sizeof(mqi_data_type_t) * nselcol;
+ ssiz = sizeof(int) * nselcol;
+ size = sizeof(row_trigger_t) + nsiz + dsiz + tsiz + ssiz;
+
+ if (!(tr = calloc(1, size))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ tr->name = strdup(name);
+ tr->type = trigger_row;
+ tr->callback = ref_callback(callback);
+
+ tr->table = table;
+
+ data = tr->data;
+
+ tr->select.column.ncol = nselcol;
+ tr->select.column.names = (char **)data;
+ tr->select.column.descs = (mqi_column_desc_t *)(data += asiz);
+ tr->select.column.types = (mqi_data_type_t *)(data += dsiz);
+ tr->select.column.sizes = (int *)(data += tsiz);
+
+ tr->select.strpool.addr = (char *)(data += ssiz);
+ tr->select.strpool.size = nsiz - asiz;
+
+ tr->select.rowsize = rowsize;
+
+ memcpy(tr->select.column.descs, selcoldscs , dsiz);
+ memcpy(tr->select.column.types, selcoltypes, tsiz);
+ memcpy(tr->select.column.sizes, selcolsizes, ssiz);
+
+ for (i = 0; i < nselcol; i++) {
+ tr->select.column.names[i] = (char *)data;
+ memcpy(data, selcolnams[i], nlens[i]);
+ data += nlens[i];
+ }
+
+ if (!tr->name || mdb_hash_add(triggers, 0,tr->name, tr) < 0) {
+ free(tr->name);
+ free(tr);
+ return -1;
+ }
+
+ sts = mqi_create_row_trigger(table, row_event_callback, tr,
+ tr->select.column.descs);
+ return sts;
+}
+
+
+int mql_create_table_trigger(char *name, mql_callback_t *callback)
+{
+ table_trigger_t *tr;
+ int sts;
+
+ MDB_CHECKARG(name && callback, -1);
+
+ if (!triggers) {
+ triggers = MDB_HASH_TABLE_CREATE(string, MQL_TRIGGER_HASH_CHAINS);
+ MDB_PREREQUISITE(triggers, -1);
+ }
+
+ if (!(tr = calloc(1, sizeof(table_trigger_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ tr->name = strdup(name);
+ tr->type = trigger_table;
+ tr->callback = ref_callback(callback);
+
+ sts = mqi_create_table_trigger(table_event_callback, tr);
+
+ return sts;
+}
+
+
+int mql_create_transaction_trigger(char *name, mql_callback_t *callback)
+{
+ transact_trigger_t *tr;
+ int sts;
+
+ MDB_CHECKARG(name && callback, -1);
+
+ if (!triggers) {
+ triggers = MDB_HASH_TABLE_CREATE(string, MQL_TRIGGER_HASH_CHAINS);
+ MDB_PREREQUISITE(triggers, -1);
+ }
+
+ if (!(tr = calloc(1, sizeof(transact_trigger_t)))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ tr->name = strdup(name);
+ tr->type = trigger_transaction;
+ tr->callback = ref_callback(callback);
+
+ sts = mqi_create_transaction_trigger(transaction_event_callback, tr);
+
+ return sts;
+}
+
+
+static mql_callback_t *ref_callback(mql_callback_t *cb)
+{
+ cb->refcnt++;
+ return cb;
+}
+
+static int unref_callback(mql_callback_t *cb)
+{
+ if (cb->refcnt > 0)
+ cb->refcnt--;
+ else {
+ free(cb->name);
+ free(cb);
+ }
+
+ return 0;
+}
+
+
+static void column_event_callback(mqi_event_t *evt, void *user_data)
+{
+ mqi_column_event_t *ce;
+ column_trigger_t *tr;
+ mql_callback_t *cb;
+ select_t *s;
+ mql_result_t *rsel;
+ mql_result_t *rslt;
+
+ if (!evt || !user_data)
+ return;
+
+ ce = &evt->column;
+ tr = (column_trigger_t *)user_data;
+ cb = tr->callback;
+
+ if (ce->event != mqi_column_changed ||
+ tr->type != trigger_column ||
+ (cb->rtype != mql_result_event &&
+ cb->rtype != mql_result_string))
+ {
+ return;
+ }
+
+ rsel = rslt = NULL;
+
+ if (tr->select.column.ncol <= 0) {
+ if (cb->rtype == mql_result_event) {
+ rslt = mql_result_event_column_change_create(ce->table.handle,
+ ce->column.index,
+ &ce->value,
+ NULL);
+ }
+ else {
+ rslt = mql_result_string_create_column_change(ce->table.name,
+ ce->column.name,
+ &ce->value,
+ NULL);
+ }
+ }
+ else {
+ s = &tr->select;
+
+ if (cb->rtype == mql_result_event) {
+
+ rsel = mql_result_rows_create(s->column.ncol,
+ s->column.descs,
+ s->column.types,
+ s->column.sizes,
+ 1,
+ s->rowsize,
+ ce->select.data);
+
+ if (mql_result_is_success(rsel)) {
+ rslt = mql_result_event_column_change_create(ce->table.handle,
+ ce->column.index,
+ &ce->value,
+ rsel);
+ }
+ }
+ else {
+ rsel = mql_result_string_create_row_list(s->column.ncol,
+ s->column.names,
+ s->column.descs,
+ s->column.types,
+ s->column.sizes,
+ 1,
+ s->rowsize,
+ ce->select.data);
+
+ if (mql_result_is_success(rsel)) {
+ rslt = mql_result_string_create_column_change(ce->table.name,
+ ce->column.name,
+ &ce->value,
+ rsel);
+ }
+ }
+ }
+
+ if (!rslt)
+ free(rsel);
+ else {
+ cb->function(rslt, cb->user_data);
+ free(rsel);
+ free(rslt);
+ }
+}
+
+
+
+static void row_event_callback(mqi_event_t *evt, void *user_data)
+{
+ mqi_row_event_t *re;
+ row_trigger_t *tr;
+ mql_callback_t *cb;
+ select_t *s;
+ mql_result_t *rsel;
+ mql_result_t *rslt;
+
+ if (!evt || !user_data)
+ return;
+
+ re = &evt->row;
+ tr = (row_trigger_t *)user_data;
+ cb = tr->callback;
+ s = &tr->select;
+
+ if ((re->event != mqi_row_inserted && re->event != mqi_row_deleted) ||
+ tr->type != trigger_row ||
+ (cb->rtype != mql_result_event && cb->rtype != mql_result_string))
+ {
+ return;
+ }
+
+
+ rsel = rslt = NULL;
+
+
+ if (cb->rtype == mql_result_event) {
+ rsel = mql_result_rows_create(s->column.ncol,
+ s->column.descs,
+ s->column.types,
+ s->column.sizes,
+ 1,
+ s->rowsize,
+ re->select.data);
+
+ if (mql_result_is_success(rsel)) {
+ rslt = mql_result_event_row_change_create(re->event,
+ re->table.handle,
+ rsel);
+ }
+ }
+ else {
+ rsel = mql_result_string_create_row_list(s->column.ncol,
+ s->column.names,
+ s->column.descs,
+ s->column.types,
+ s->column.sizes,
+ 1,
+ s->rowsize,
+ re->select.data);
+
+ if (mql_result_is_success(rsel)) {
+ rslt = mql_result_string_create_row_change(re->event,
+ re->table.name,
+ rsel);
+ }
+ }
+
+ if (!rslt)
+ free(rsel);
+ else {
+ cb->function(rslt, cb->user_data);
+ free(rsel);
+ free(rslt);
+ }
+}
+
+
+
+static void table_event_callback(mqi_event_t *evt, void *user_data)
+{
+ mqi_table_event_t *te;
+ table_trigger_t *tr;
+ mql_callback_t *cb;
+ mql_result_t *rslt;
+
+ if (!evt || !user_data)
+ return;
+
+ te = &evt->table;
+ tr = (table_trigger_t *)user_data;
+ cb = tr->callback;
+
+ if ((te->event != mqi_table_created && te->event != mqi_table_dropped) ||
+ tr->type != trigger_table ||
+ (cb->rtype != mql_result_event && cb->rtype != mql_result_string))
+ {
+ return;
+ }
+
+ if (cb->rtype == mql_result_event)
+ rslt = mql_result_event_table_create(te->event, te->table.handle);
+ else
+ rslt = mql_result_string_create_table_change(te->event,te->table.name);
+
+ if (rslt) {
+ cb->function(rslt, cb->user_data);
+ free(rslt);
+ }
+}
+
+
+
+static void transaction_event_callback(mqi_event_t *evt, void *user_data)
+{
+ mqi_transact_event_t *te;
+ table_trigger_t *tr;
+ mql_callback_t *cb;
+ mql_result_t *rslt;
+
+ if (!evt || !user_data)
+ return;
+
+ te = &evt->transact;
+ tr = (transact_trigger_t *)user_data;
+ cb = tr->callback;
+
+ if ((te->event != mqi_transaction_start &&
+ te->event != mqi_transaction_end ) ||
+ tr->type != trigger_transaction ||
+ (cb->rtype != mql_result_event &&
+ cb->rtype != mql_result_string ) )
+ {
+ return;
+ }
+
+ if (cb->rtype == mql_result_event)
+ rslt = mql_result_event_transaction_create(te->event);
+ else
+ rslt = mql_result_string_create_transaction_change(te->event);
+
+ if (rslt) {
+ cb->function(rslt, cb->user_data);
+ free(rslt);
+ }
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+includedir=@includedir@/murphy-db
+libdir=@libdir@/murphy
+
+Name: Murphy DB
+Description: Database for the Murphy policy engine
+URL: github/otcshare/murphy
+Version: @PACKAGE_VERSION@
+Cflags: -I${includedir}
+Libs: -L${libdir} -lmqi -lmql -lmdb
+Libs.private:
--- /dev/null
+CHECK_LIBMDB_LOG = check-libmdb.log
+CHECK_LIBMQI_LOG = check-libmqi.log
+CHECK_LIBMQL_LOG = check-libmql.log
+
+MDB_LIBS = ../mdb/libmdb.la
+MQI_LIBS = ../mqi/libmqi.la
+MQL_LIBS = ../mql/libmql.la
+
+if HAVE_CHECK
+TESTS = check-libmdb check-libmqi check-libmql
+else
+TESTS =
+endif
+
+noinst_PROGRAMS = $(TESTS)
+
+#
+# MDB tests
+#
+check_libmdb_SOURCES = check-libmdb.c
+check_libmdb_CFLAGS = @CHECK_CFLAGS@ -I../include \
+ -DLOGFILE=\"$(CHECK_LIBMDB_LOG)\"
+check_libmdb_LDADD = @CHECK_LIBS@ $(MDB_LIBS)
+
+AM_CFLAGS = -g3 -O0
+
+#
+# MQI tests
+#
+check_libmqi_SOURCES = check-libmqi.c
+check_libmqi_CFLAGS = @CHECK_CFLAGS@ -I../include \
+ -DLOGFILE=\"$(CHECK_LIBMQI_LOG)\"
+check_libmqi_LDADD = @CHECK_LIBS@ $(MQI_LIBS) $(MDB_LIBS)
+
+
+#
+# MQL tests
+#
+check_libmql_SOURCES = check-libmql.c
+check_libmql_CFLAGS = @CHECK_CFLAGS@ -I../include \
+ -DLOGFILE=\"$(CHECK_LIBMQL_LOG)\"
+check_libmql_LDADD = @CHECK_LIBS@ $(MQL_LIBS) $(MQI_LIBS) $(MDB_LIBS)
+
+
+clean-local:
+ rm -f $(CHECK_LIBMDB_LOG) $(CHECK_LIBMQI_LOG) $(CHECK_LIBMQL_LOG) \
+ $(TESTS) *~
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <check.h>
+
+#ifndef LOGFILE
+#define LOGFILE "check_libmdb.log"
+#endif
+
+#define ADD_TEST_CASE(s,t) \
+ do { \
+ TCase *tc = tcase_create(#t); \
+ tcase_add_test(tc, t); \
+ suite_add_tcase(s, tc); \
+ } while (0)
+
+
+static Suite *libmdb_suite(void);
+
+
+int main()
+{
+ Suite *s = libmdb_suite();
+ SRunner *sr = srunner_create(s);
+ int nf;
+
+ srunner_set_log(sr, LOGFILE);
+
+ srunner_run_all(sr, CK_NORMAL);
+
+ nf = srunner_ntests_failed(sr);
+
+ srunner_free(sr);
+ // suite_free(s);
+
+ return (nf == 0) ? 0 : 1;
+}
+
+START_TEST(create_table)
+{
+ fail_unless(1==1, "create table test");
+}
+END_TEST
+
+
+static Suite *libmdb_suite(void)
+{
+ Suite *s = suite_create("Memory Database - libmdb");
+
+ ADD_TEST_CASE(s, create_table);
+
+ return s;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libgen.h>
+
+#include <check.h>
+
+#include <murphy-db/mqi.h>
+
+#ifndef LOGFILE
+#define LOGFILE "check_libmqi.log"
+#endif
+
+#define PREREQUISITE(t) t(_i)
+
+#define TRIGGER_DATA(idx) (void *)0xdeadbeef##idx
+#define TRANSACT_TRIGGER_DATA TRIGGER_DATA(1)
+#define TABLE_TRIGGER_DATA TRIGGER_DATA(2)
+#define ROW_TRIGGER_DATA TRIGGER_DATA(3)
+#define COLUMN_TRIGGER_DATA TRIGGER_DATA(4)
+
+typedef struct {
+ mqi_event_type_t event;
+ struct {
+ mqi_handle_t handle;
+ char name[256];
+ } table;
+ struct {
+ uint32_t id;
+ char first_name[14];
+ char family_name[14];
+ } row;
+ struct {
+ int index;
+ char name[14];
+ char value[32];
+ } col;
+} trigger_t;
+
+typedef struct {
+ const char *sex;
+ const char *first_name;
+ const char *family_name;
+ uint32_t id;
+ const char *email;
+} record_t;
+
+typedef struct {
+ uint32_t id;
+ const char *family_name;
+ const char *first_name;
+} query_t;
+
+
+MQI_COLUMN_DEFINITION_LIST(persons_coldefs,
+ MQI_COLUMN_DEFINITION( "sex" , MQI_VARCHAR(6) ),
+ MQI_COLUMN_DEFINITION( "family_name", MQI_VARCHAR(12) ),
+ MQI_COLUMN_DEFINITION( "first_name" , MQI_VARCHAR(12) ),
+ MQI_COLUMN_DEFINITION( "id" , MQI_UNSIGNED ),
+ MQI_COLUMN_DEFINITION( "email" , MQI_VARCHAR(24) )
+);
+
+MQI_INDEX_DEFINITION(persons_indexdef,
+ MQI_INDEX_COLUMN("first_name")
+ MQI_INDEX_COLUMN("family_name")
+);
+
+MQI_COLUMN_SELECTION_LIST(persons_insert_columns,
+ MQI_COLUMN_SELECTOR( 0, record_t, sex ),
+ MQI_COLUMN_SELECTOR( 2, record_t, first_name ),
+ MQI_COLUMN_SELECTOR( 1, record_t, family_name ),
+ MQI_COLUMN_SELECTOR( 3, record_t, id ),
+ MQI_COLUMN_SELECTOR( 4, record_t, email )
+);
+
+MQI_COLUMN_SELECTION_LIST(persons_select_columns,
+ MQI_COLUMN_SELECTOR( 3, query_t, id ),
+ MQI_COLUMN_SELECTOR( 1, query_t, family_name ),
+ MQI_COLUMN_SELECTOR( 2, query_t, first_name )
+);
+
+static record_t chuck = {"male" , "Chuck", "Norris" , 1100, "cno@texas.us" };
+static record_t gary = {"male" , "Gary", "Cooper" , 700, "gco@heaven.org"};
+static record_t elvis = {"male" , "Elvis", "Presley", 600, "epr@heaven.org"};
+static record_t tom = {"male" , "Tom", "Cruise" , 500, "tcr@foo.com" };
+static record_t greta = {"female", "Greta", "Garbo" , 2000, "gga@heaven.org"};
+static record_t rita = {"female", "Rita", "Hayworth", 44, "rha@heaven.org"};
+
+static record_t *artists[] = {&chuck, &gary, &elvis, &tom, &greta, &rita,NULL};
+
+
+
+static int verbose;
+static mqi_handle_t transactions[MQI_TXDEPTH_MAX - 1];
+static int txdepth;
+static mqi_handle_t persons = MQI_HANDLE_INVALID;
+static int columns_no_in_persons = -1;
+static int rows_no_in_persons = -1;
+
+static int ntrigger;
+static trigger_t triggers[256];
+static int nseq = 32;
+static int nnest = MQI_TXDEPTH_MAX - 1;
+
+
+static Suite *libmqi_suite(void);
+static TCase *basic_tests(void);
+static void print_rows(int, query_t *);
+static void print_triggers(void);
+static void transaction_event_cb(mqi_event_t *, void *);
+static void table_event_cb(mqi_event_t *, void *);
+static void row_event_cb(mqi_event_t *, void *);
+static void column_event_cb(mqi_event_t *, void *);
+
+
+int main(int argc, char **argv)
+{
+ Suite *s = libmqi_suite();
+ SRunner *sr = srunner_create(s);
+ int nf;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp("-v", argv[i]))
+ verbose = 1;
+ else if (!strcmp("-f", argv[i]))
+ srunner_set_fork_status(sr, CK_NOFORK);
+ else if (!strcmp("-nseq", argv[i]) && i < argc - 1) {
+ nseq = atoi(argv[i + 1]);
+ i++;
+ }
+ else if (!strcmp("-nnest", argv[i]) && i < argc - 1) {
+ nnest = atoi(argv[i + 1]);
+ i++;
+ }
+ else {
+ printf("Usage: %s [-h] [-v] [-f]\n"
+ " -h prints this message\n"
+ " -v sets verbose mode\n"
+ " -f forces no-forking mode\n"
+ " -nseq number of sequential transactions\n"
+ " -nnest number of nested transactions (1 - %d)\n",
+ basename(argv[0]), MQI_TXDEPTH_MAX - 1);
+ exit(strcmp("-h", argv[i]) ? 1 : 0);
+ }
+ }
+
+ srunner_set_log(sr, LOGFILE);
+
+ srunner_run_all(sr, CK_NORMAL);
+
+ nf = srunner_ntests_failed(sr);
+
+ srunner_free(sr);
+
+ return (nf == 0) ? 0 : 1;
+}
+
+START_TEST(open_db)
+{
+ int sts = mqi_open();
+
+ fail_if(sts, "db open test");
+}
+END_TEST
+
+
+
+START_TEST(create_table_persons)
+{
+ if (persons == MQI_HANDLE_INVALID) {
+ PREREQUISITE(open_db);
+
+ persons = MQI_CREATE_TABLE("persons", MQI_TEMPORARY,
+ persons_coldefs, persons_indexdef);
+
+ fail_if(persons == MQI_HANDLE_INVALID, "errno (%s)", strerror(errno));
+
+ columns_no_in_persons = MQI_DIMENSION(persons_coldefs) - 1;
+ }
+}
+END_TEST
+
+
+
+START_TEST(table_handle)
+{
+ mqi_handle_t handle = MQI_HANDLE_INVALID;
+
+ PREREQUISITE(create_table_persons);
+
+ handle = mqi_get_table_handle("persons");
+
+ fail_if(handle == MQI_HANDLE_INVALID, "failed to obtain handle for "
+ "'persons' (%s)", strerror(errno));
+
+ fail_if(handle != persons, "handle mismatch (0x%x vs. 0x%x)",
+ persons, handle);
+}
+END_TEST
+
+
+START_TEST(describe_persons)
+{
+ mqi_column_def_t cols[32];
+ mqi_column_def_t *def, *col;
+ int deflgh;
+ int i,ncolumn;
+
+ PREREQUISITE(create_table_persons);
+
+ ncolumn = MQI_DESCRIBE(persons, cols);
+
+ fail_if(ncolumn < 0, "errno (%s)", strerror(errno));
+
+ fail_if(ncolumn != columns_no_in_persons, "mismatching column number "
+ "(%d vs. %d)", columns_no_in_persons, ncolumn);
+
+ if (verbose) {
+ printf("-----------------------------\n");
+ printf("name type length\n");
+ printf("-----------------------------\n");
+ for (i = 0; i < ncolumn; i++) {
+ col = cols + i;
+ printf("%-12s %-9s %2d\n", col->name,
+ mqi_data_type_str(col->type), col->length);
+ }
+ printf("-----------------------------\n");
+ }
+
+ for (i = 0; i < ncolumn; i++) {
+ def = persons_coldefs + i;
+ col = cols + i;
+
+ fail_if(strcmp(def->name, col->name), "mismatching column names @ "
+ "column %d ('%s' vs. '%s')", i, def->name, col->name);
+
+ fail_if(def->type != col->type, "mismatching column types @ "
+ "column %d (%d/'%s' vs. %d/'%s')", i,
+ def->type, mqi_data_type_str(def->type),
+ col->type, mqi_data_type_str(col->type));
+
+ switch (def->type) {
+ case mqi_varchar: deflgh = def->length; break;
+ case mqi_integer: deflgh = sizeof(int32_t); break;
+ case mqi_unsignd: deflgh = sizeof(uint32_t); break;
+ case mqi_floating: deflgh = sizeof(double); break;
+ case mqi_blob: deflgh = def->length; break;
+ default: deflgh = -1; break;
+ };
+
+ fail_if(deflgh != col->length, "mismatching column length @ "
+ "column %d (%d vs. %d)", i, deflgh, col->length);
+ }
+}
+END_TEST
+
+
+START_TEST(insert_into_persons)
+{
+ int n;
+
+ PREREQUISITE(create_table_persons);
+
+ n = MQI_INSERT_INTO(persons, persons_insert_columns, artists);
+
+ fail_if(n < 0, "errno (%s)", strerror(errno));
+
+ fail_if(n != MQI_DIMENSION(artists)-1, "some insertion failed. "
+ "Attempted %d succeeded %d", MQI_DIMENSION(artists)-1, n);
+
+ rows_no_in_persons = n;
+}
+END_TEST
+
+
+START_TEST(row_count_in_persons)
+{
+ int n;
+
+ PREREQUISITE(insert_into_persons);
+
+ n = mqi_get_table_size(persons);
+
+ fail_if(n < 0, "error (%s)", strerror(errno));
+
+ fail_if(n != rows_no_in_persons, "mismatch in row numbers: "
+ "Inserted %d reported %d", rows_no_in_persons, n);
+}
+END_TEST
+
+START_TEST(insert_duplicate_into_persons)
+{
+ static record_t gary = {"male", "Gary","Cooper", 200, "gary@att.com"};
+ static record_t *duplicate[] = {&gary, NULL};
+
+ int n;
+
+ PREREQUISITE(insert_into_persons);
+
+ n = MQI_INSERT_INTO(persons, persons_insert_columns, duplicate);
+
+ fail_if(n == 1, "managed to insert a duplicate");
+
+ fail_if(n < 0 && errno != EEXIST, "error (%s)", strerror(errno));
+}
+END_TEST
+
+START_TEST(transaction_begin)
+{
+ mqi_handle_t tx;
+
+ fail_if(txdepth >= (int)MQI_DIMENSION(transactions), "too many nested "
+ "transactions. Only %d allowed", MQI_DIMENSION(transactions));
+
+ tx = MQI_BEGIN;
+
+ fail_if(tx == MQI_HANDLE_INVALID, "error (%d)", strerror(errno));
+
+ transactions[txdepth++] = tx;
+}
+END_TEST
+
+
+START_TEST(replace_in_persons)
+{
+ static record_t gary = {"male", "Gary","Cooper", 200, "gary@att.com"};
+ static record_t *duplicate[] = {&gary, NULL};
+
+ int n;
+
+ PREREQUISITE(insert_into_persons);
+ PREREQUISITE(transaction_begin);
+
+ n = MQI_REPLACE(persons, persons_insert_columns, duplicate);
+
+ fail_if(n < 0, "error (%s)", strerror(errno));
+
+ fail_if(n == 1, "duplicate was inserted instead of replacement");
+}
+END_TEST
+
+START_TEST(filtered_select_from_persons)
+{
+ static char *initial = "G";
+ static uint32_t idlimit = 200;
+
+ MQI_WHERE_CLAUSE(where,
+ MQI_GREATER( MQI_COLUMN(1), MQI_STRING_VAR(initial) ) MQI_AND
+ MQI_GREATER( MQI_COLUMN(3), MQI_UNSIGNED_VAR(idlimit) )
+ );
+
+ query_t rows[32];
+ int n;
+
+ PREREQUISITE(replace_in_persons);
+
+ n = MQI_SELECT(persons_select_columns, persons, where, rows);
+
+ fail_if(n < 0, "error (%s)", strerror(errno));
+
+ if (verbose)
+ print_rows(n, rows);
+
+ fail_if(n != 3, "selcted %d rows but the right number would be 3", n);
+}
+END_TEST
+
+
+START_TEST(full_select_from_persons)
+{
+ query_t *r, rows[32];
+ int i, n;
+
+ PREREQUISITE(replace_in_persons);
+
+ n = MQI_SELECT(persons_select_columns, persons, MQI_ALL, rows);
+
+ fail_if(n < 0, "error (%s)", strerror(errno));
+
+ if (verbose) {
+ printf(" id first name family name \n");
+ printf("--------------------------------------\n");
+
+ if (!n)
+ printf("no rows\n");
+ else {
+ for (i = 0; i < n; i++) {
+ r = rows + i;
+ printf("%5d %-15s %-15s\n", r->id,
+ r->first_name, r->family_name);
+ }
+ }
+
+ printf("--------------------------------------\n");
+ }
+
+ fail_if(n != 6, "selcted %d rows but the right number would be 3", n);
+}
+END_TEST
+
+
+
+START_TEST(select_from_persons_by_index)
+{
+ MQI_INDEX_VALUE(index,
+ MQI_STRING_VAL(elvis.family_name)
+ MQI_STRING_VAL(elvis.first_name)
+ );
+
+ query_t row;
+ int n;
+
+ PREREQUISITE(replace_in_persons);
+
+ n = MQI_SELECT_BY_INDEX(persons_select_columns, persons, index, &row);
+
+ fail_if(n < 0, "errno (%s)", strerror(errno));
+
+ fail_if(!n, "could not select %s %s", elvis.first_name, elvis.family_name);
+
+ fail_if(strcmp(row.first_name, elvis.first_name), "mismatching first "
+ "name ('%s' vs. '%s')", elvis.first_name, row.first_name);
+
+ fail_if(strcmp(row.family_name, elvis.family_name), "mismatching family "
+ "name ('%s' vs. '%s')", elvis.family_name, row.family_name);
+
+ fail_if(row.id != elvis.id, "mismatching id (%u vs. %u)",
+ elvis.id, row.id);
+}
+END_TEST
+
+
+
+START_TEST(update_in_persons)
+{
+ MQI_WHERE_CLAUSE(where,
+ MQI_EQUAL( MQI_COLUMN(1), MQI_STRING_VAR(elvis.family_name) ) MQI_AND
+ MQI_EQUAL( MQI_COLUMN(2), MQI_STRING_VAR(elvis.first_name ) )
+ );
+
+ static query_t kalle = {1, "Korhonen", "Kalle"};
+
+ query_t *r, rows[32];
+ int i,n;
+ int found;
+
+ PREREQUISITE(replace_in_persons);
+
+ n = MQI_UPDATE(persons, persons_select_columns, &kalle, where);
+
+ fail_if(n < 0, "errno (%s)", strerror(errno));
+ fail_if(n != 1, "updated %d row but supposed to just 1", n);
+
+ n = MQI_SELECT(persons_select_columns, persons, MQI_ALL, rows);
+
+ fail_if(n < 0, "select for checking failed (%s)", strerror(errno));
+
+ if (verbose)
+ print_rows(n, rows);
+
+ for (found = 0, i = 0; i < n; i++) {
+ r = rows + i;
+
+ fail_if(r->id == elvis.id, "found the original id %u what supposed "
+ "to change to %u", elvis.id, kalle.id);
+
+ fail_if(!strcmp(r->first_name, elvis.first_name), "found the original "
+ "first name '%s' what supposed to change to '%s'",
+ elvis.first_name, kalle.first_name);
+
+ fail_if(!strcmp(r->family_name, elvis.family_name),"found the original"
+ " family name '%s' what supposed to change to '%s'",
+ elvis.family_name, kalle.family_name);
+
+ if (r->id == kalle.id &&
+ !strcmp(r->first_name, kalle.first_name) &&
+ !strcmp(r->family_name, kalle.family_name))
+ {
+ found = 1;
+ }
+ }
+
+ fail_unless(found, "could not find the updated row");
+}
+END_TEST
+
+
+
+START_TEST(delete_from_persons)
+{
+ static uint32_t idlimit = 200;
+
+ MQI_WHERE_CLAUSE(where,
+ MQI_LESS( MQI_COLUMN(3), MQI_UNSIGNED_VAR(idlimit) )
+ );
+
+ query_t *r, rows[32];
+ int i,n;
+
+ PREREQUISITE(update_in_persons);
+
+ n = MQI_DELETE(persons, where);
+
+ fail_if(n < 0, "errno (%s)", strerror(errno));
+ fail_if(n != 2, "deleted %d rows but sopposed to 2", n);
+
+ n = MQI_SELECT(persons_select_columns, persons, MQI_ALL, rows);
+
+ fail_if(n < 0, "verification select failed (%s)", strerror(errno));
+
+ if (verbose)
+ print_rows(n, rows);
+
+ for (i = 0; i < n; i++) {
+ r = rows + i;
+
+ fail_if(r->id < idlimit, "found row with id %u what is smaller than "
+ "the limit %u", r->id, idlimit);
+ }
+}
+END_TEST
+
+
+START_TEST(delete_all_persons)
+{
+ query_t rows[32];
+ int nrow, n;
+
+ nrow = MQI_SELECT(persons_select_columns, persons, MQI_ALL, rows);
+ fail_if(nrow < 0, "select for checking failed (%s)", strerror(errno));
+
+ n = MQI_DELETE(persons, MQI_ALL);
+ fail_if(n != nrow, "deleted %d rows instead of the expected %d", n, nrow);
+
+ n = MQI_SELECT(persons_select_columns, persons, MQI_ALL, rows);
+ fail_if(n != 0, "verification select failed (%s)", strerror(errno));
+}
+END_TEST
+
+
+START_TEST(transaction_rollback)
+{
+ record_t *a;
+ query_t *r, rows[32];
+ int i,j,n;
+ int sts;
+ int found;
+
+ PREREQUISITE(delete_from_persons);
+
+ fail_unless(txdepth > 0, "actually there is no transaction");
+
+ sts = MQI_ROLLBACK(transactions[--txdepth]);
+
+ fail_if(sts < 0, "errno (%s)", strerror(errno));
+
+ n = MQI_SELECT(persons_select_columns, persons, MQI_ALL, rows);
+
+ fail_if(n < 0, "verification select failed (%s)", strerror(errno));
+
+ if (verbose)
+ print_rows(n, rows);
+
+ fail_if(n != MQI_DIMENSION(artists)-1, "mismatching row numbers: currently"
+ " %d supposed to be %d", n, MQI_DIMENSION(artists)-1);
+
+
+ for (i = 0; i < n; i++) {
+ r = rows + i;
+
+ for (found = 0, j = 0; j < (int)MQI_DIMENSION(artists)-1; j++) {
+ a = artists[j];
+
+ if (a->id == r->id &&
+ !strcmp(a->first_name, r->first_name) &&
+ !strcmp(a->family_name, r->family_name))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ fail_unless(found, "after rolling back can't find %s %s (id %u) "
+ "any more", r->first_name, r->family_name, r->id);
+ }
+}
+END_TEST
+
+START_TEST(table_trigger)
+{
+ int sts;
+
+ PREREQUISITE(open_db);
+
+ sts = mqi_create_table_trigger(table_event_cb, TABLE_TRIGGER_DATA);
+
+ fail_if(sts < 0, "errno (%s)", strerror(errno));
+
+ PREREQUISITE(create_table_persons);
+
+ if (verbose)
+ print_triggers();
+
+ fail_unless(ntrigger == 1, "no callback after table creation");
+ fail_unless(triggers->event == mqi_table_created,
+ "wrong event type %d", triggers->event);
+ fail_unless(triggers->table.handle == persons,
+ "wrong table handle (0x%x vs. 0x%x)",
+ triggers->table.handle, persons);
+ fail_unless(!strcmp(triggers->table.name, "persons"),
+ "wrong table name ('%s' vs. 'persons')",
+ triggers->table.name);
+}
+END_TEST
+
+START_TEST(row_trigger)
+{
+ mqi_handle_t trh;
+ record_t *rec;
+ trigger_t *trig;
+ int sts;
+ int i;
+
+ PREREQUISITE(create_table_persons);
+
+ sts = mqi_create_transaction_trigger(transaction_event_cb,
+ TRANSACT_TRIGGER_DATA);
+
+ fail_if(sts < 0, "create transaction trigger failed: errno (%s)",
+ strerror(errno));
+
+ sts = mqi_create_row_trigger(persons, row_event_cb, ROW_TRIGGER_DATA,
+ persons_select_columns);
+
+ fail_if(sts < 0, "create row trigger failed: errno (%s)", strerror(errno));
+
+ trh = mqi_begin_transaction();
+
+ fail_if(trh == MQI_HANDLE_INVALID, "begin failed: errno(%s)",
+ strerror(errno));
+
+ PREREQUISITE(insert_into_persons);
+
+ sts = mqi_commit_transaction(trh);
+
+ fail_if(sts < 0, "commit failed: errno (%s)", strerror(errno));
+
+ if (verbose)
+ print_triggers();
+
+ fail_unless(ntrigger == rows_no_in_persons + 2,
+ "wrong number of callbacks (%d vs. %d)",
+ ntrigger, rows_no_in_persons);
+
+ for (i = 0; i < ntrigger-2; i++) {
+ trig = triggers + (i + 1);
+ rec = artists[i];
+
+ fail_unless(trig->event == mqi_row_inserted,
+ "wrong event type (%d vs %d) @ callback %d",
+ trig->event, mqi_row_inserted, i);
+ fail_unless(trig->table.handle == persons,
+ "wrong table handle (0x%x vs. 0x%x) @ callback %d",
+ trig->table.handle, persons, i);
+ fail_unless(!strcmp(trig->table.name, "persons"),
+ "wrong table name ('%s' vs. 'persons') @ callback %d",
+ trig->table.name, persons, i);
+ fail_unless(trig->row.id == rec->id,
+ "id column mismatch (%d vs %s) @ callback %d",
+ trig->row.id, rec->id, i);
+ fail_unless(!strcmp(trig->row.first_name, rec->first_name),
+ "first name mismatch ('%s' vs. '%s') @ callback %d",
+ trig->row.first_name, rec->first_name);
+ fail_unless(!strcmp(trig->row.family_name, rec->family_name),
+ "first name mismatch ('%s' vs. '%s') @ callback %d",
+ trig->row.family_name, rec->family_name);
+ }
+
+}
+END_TEST
+
+
+
+START_TEST(column_trigger)
+{
+ MQI_WHERE_CLAUSE(where,
+ MQI_EQUAL( MQI_COLUMN(1), MQI_STRING_VAR(elvis.family_name) ) MQI_AND
+ MQI_EQUAL( MQI_COLUMN(2), MQI_STRING_VAR(elvis.first_name ) )
+ );
+
+ static query_t kalle = {1, "Korhonen", "Kalle"};
+
+ mqi_handle_t trh;
+ trigger_t *trig;
+ int sts;
+ int i, n;
+
+ PREREQUISITE(insert_into_persons);
+
+ sts = mqi_create_column_trigger(persons, 1, column_event_cb,
+ COLUMN_TRIGGER_DATA,
+ persons_select_columns);
+
+ fail_if(sts < 0, "create column trigger failed: errno (%s)",
+ strerror(errno));
+
+ sts = mqi_create_column_trigger(persons, 2, column_event_cb,
+ COLUMN_TRIGGER_DATA,
+ persons_select_columns);
+
+ fail_if(sts < 0, "create column trigger failed: errno (%s)",
+ strerror(errno));
+
+ trh = mqi_begin_transaction();
+
+ fail_if(trh == MQI_HANDLE_INVALID, "begin failed: errno(%s)",
+ strerror(errno));
+
+ n = MQI_UPDATE(persons, persons_select_columns, &kalle, where);
+
+ fail_if(n < 0, "update failed: errno (%s)", strerror(errno));
+ fail_if(n != 1, "updated %d row but supposed to just 1", n);
+
+ sts = mqi_commit_transaction(trh);
+
+ fail_if(sts < 0, "commit failed: errno (%s)", strerror(errno));
+
+ if (verbose)
+ print_triggers();
+
+ fail_unless(ntrigger == 2,
+ "wrong number of callbacks (%d vs. 2)",
+ ntrigger);
+
+ for (i = 0; i < ntrigger; i++) {
+ trig = triggers + i;
+
+ fail_unless(trig->event == mqi_column_changed,
+ "wrong event type (%d vs %d) @ callback %d",
+ trig->event, mqi_column_changed, i);
+ fail_unless(trig->table.handle == persons,
+ "wrong table handle (0x%x vs. 0x%x) @ callback %d",
+ trig->table.handle, persons, i);
+ fail_unless(!strcmp(trig->table.name, "persons"),
+ "wrong table name ('%s' vs. 'persons') @ callback %d",
+ trig->table.name, persons, i);
+ fail_unless(trig->row.id == kalle.id,
+ "id column mismatch (%d vs %d) @ callback %d",
+ trig->row.id, kalle.id, i);
+ fail_unless(!strcmp(trig->row.first_name, kalle.first_name),
+ "first name mismatch ('%s' vs. '%s') @ callback %d",
+ trig->row.first_name, kalle.first_name);
+ fail_unless(!strcmp(trig->row.family_name, kalle.family_name),
+ "first name mismatch ('%s' vs. '%s') @ callback %d",
+ trig->row.family_name, kalle.family_name);
+ }
+}
+END_TEST
+
+START_TEST(sequential_transactions)
+{
+ mqi_handle_t trh;
+ int sts, i;
+ const char *kind;
+
+ PREREQUISITE(create_table_persons);
+
+ for (i = 0; i < nseq; i++) {
+ trh = mqi_begin_transaction();
+
+ fail_if(trh == MQI_HANDLE_INVALID,
+ "failed to create %d. transaction : errno (%s)",
+ i + 1, strerror(errno));
+
+ if (i & 0x1)
+ PREREQUISITE(delete_all_persons);
+ else
+ PREREQUISITE(insert_into_persons);
+
+ if (!(i & 0x3)) {
+ kind = "rollback";
+ sts = mqi_rollback_transaction(trh);
+ }
+ else {
+ kind = "commit";
+ sts = mqi_commit_transaction(trh);
+ }
+
+ fail_if(sts < 0, "%s failed: errno (%s)", kind, strerror(errno));
+ }
+}
+END_TEST
+
+
+START_TEST(nested_transactions)
+{
+ mqi_handle_t txids[MQI_TXDEPTH_MAX - 1];
+ mqi_handle_t trh;
+ int sts, tx, i, cnt;
+ const char *kind;
+
+ PREREQUISITE(create_table_persons);
+
+ if (nnest > (int)(sizeof(txids) / sizeof(txids[0])))
+ nnest = sizeof(txids) / sizeof(txids[0]);
+
+ for (cnt = 0; cnt < 16; cnt++) {
+ for (tx = 0; tx < nnest; tx++) {
+ trh = txids[tx] = mqi_begin_transaction();
+
+ fail_if(trh == MQI_HANDLE_INVALID,
+ "couldn't create transaction: errno (%s)", strerror(errno));
+
+ for (i = 0; i < nseq; i++) {
+ if (i & 0x1)
+ PREREQUISITE(delete_all_persons);
+ else
+ PREREQUISITE(insert_into_persons);
+ }
+ }
+
+ for (tx = nnest - 1; tx >= 0; tx--) {
+ trh = txids[tx];
+
+ if (!(tx & 0x1) && 0) {
+ kind = "rollback";
+ sts = mqi_rollback_transaction(trh);
+ }
+ else {
+ kind = "commit";
+ sts = mqi_commit_transaction(trh);
+ }
+
+ fail_if(sts < 0, "%s %u failed: errno (%s)", kind, trh,
+ strerror(errno));
+ }
+ }
+}
+END_TEST
+
+
+
+static Suite *libmqi_suite(void)
+{
+ Suite *s = suite_create("Murphy Query Interface - libmqi");
+ TCase *tc_basic = basic_tests();
+
+ suite_add_tcase(s, tc_basic);
+
+ return s;
+}
+
+static TCase *basic_tests(void)
+{
+ TCase *tc = tcase_create("basic tests");
+
+ tcase_add_test(tc, open_db);
+ tcase_add_test(tc, create_table_persons);
+ tcase_add_test(tc, table_handle);
+ tcase_add_test(tc, describe_persons);
+ tcase_add_test(tc, insert_into_persons);
+ tcase_add_test(tc, row_count_in_persons);
+ tcase_add_test(tc, insert_duplicate_into_persons);
+ tcase_add_test(tc, replace_in_persons);
+ tcase_add_test(tc, filtered_select_from_persons);
+ tcase_add_test(tc, full_select_from_persons);
+ tcase_add_test(tc, select_from_persons_by_index);
+ tcase_add_test(tc, update_in_persons);
+ tcase_add_test(tc, delete_from_persons);
+ tcase_add_test(tc, transaction_rollback);
+ tcase_add_test(tc, table_trigger);
+ tcase_add_test(tc, row_trigger);
+ tcase_add_test(tc, column_trigger);
+ tcase_add_test(tc, sequential_transactions);
+ tcase_add_test(tc, nested_transactions);
+
+ return tc;
+}
+
+static void print_rows(int n, query_t *rows)
+{
+ query_t *r;
+ int i;
+
+ printf(" id first name family name \n");
+ printf("--------------------------------------\n");
+
+ if (!n)
+ printf("no rows\n");
+ else {
+ for (i = 0; i < n; i++) {
+ r = rows + i;
+ printf("%5d %-15s %-15s\n", r->id,
+ r->first_name, r->family_name);
+ }
+ }
+
+ printf("--------------------------------------\n");
+}
+
+
+static void print_triggers(void)
+{
+ static char *separator = "+---------------+-------------------+"
+ "-------------------------------------+"
+ "--------------------------------------"
+ "--------+\n";
+ trigger_t *trig;
+ enum {err, tra, tbl, row, col} t;
+ char *ev;
+ int i;
+
+ printf(separator);
+ printf("| trigger | table |"
+ " selected columns in row |"
+ " altered column |\n");
+ printf("| event | handle name |"
+ " id first_name family_name |"
+ " idx name value |\n");
+ printf(separator);
+
+ if (!ntrigger) {
+ printf("|-<no events>---|-------------------|"
+ "-------------------------------------|"
+ "----------------------------------------------|\n");
+ }
+ else {
+ for (i = 0; i < ntrigger; i++) {
+ trig = triggers + i;
+
+ switch (trig->event) {
+ case mqi_column_changed: t = col; ev = "column_changed"; break;
+ case mqi_row_inserted: t = row; ev = "row_inserted"; break;
+ case mqi_row_deleted: t = row; ev = "row_deleted"; break;
+ case mqi_table_created: t = tbl; ev = "table_created"; break;
+ case mqi_table_dropped: t = tbl; ev = "table_dropped"; break;
+ case mqi_transaction_start: t = tra; ev = "transact start"; break;
+ case mqi_transaction_end: t = tra; ev = "transact end"; break;
+ default: t = err; ev = "<unknown>"; break;
+ }
+
+
+ printf("| %-14s", ev);
+
+ if (t == tbl || t == row || t == col)
+ printf("|%8x %-10s", trig->table.handle, trig->table.name);
+ else
+ printf("| ");
+
+ if (t == row || t == col)
+ printf("|%5d %-15s %-15s", trig->row.id, trig->row.first_name,
+ trig->row.family_name);
+ else
+ printf("| ");
+
+ if (t == col)
+ printf("| %3d %-12s %-28s", trig->col.index, trig->col.name,
+ trig->col.value);
+ else
+ printf("| ");
+
+ printf("|\n");
+ }
+ }
+
+ printf(separator);
+}
+
+static void transaction_event_cb(mqi_event_t *evt, void *user_data)
+{
+ mqi_event_type_t event = evt->event;
+ /* mqi_transact_event_t *te = &evt->transact; */
+ trigger_t *trig;
+
+ if (ntrigger >= (int)MQI_DIMENSION(triggers)) {
+ if (verbose)
+ printf("test framework error: trigger log overflow\n");
+ return;
+ }
+
+ trig = triggers + ntrigger++;
+
+ if (event != mqi_transaction_start && event != mqi_transaction_end) {
+ if (verbose)
+ printf("invalid event %d for transaction trigger\n", event);
+ return;
+ }
+
+ if (user_data != TRANSACT_TRIGGER_DATA) {
+ if (verbose)
+ printf("invalid user_data %p for transaction trigger\n",
+ user_data);
+ return;
+ }
+
+ trig->event = event;
+}
+
+static void table_event_cb(mqi_event_t *evt, void *user_data)
+{
+ mqi_event_type_t event = evt->event;
+ mqi_table_event_t *te = &evt->table;
+ trigger_t *trig;
+
+ if (ntrigger >= (int)MQI_DIMENSION(triggers)) {
+ if (verbose)
+ printf("test framework error: trigger log overflow\n");
+ return;
+ }
+
+ trig = triggers + ntrigger++;
+
+ if (event != mqi_table_created && event != mqi_table_dropped) {
+ if (verbose)
+ printf("invalid event %d for table trigger\n", event);
+ return;
+ }
+
+ if (user_data != TABLE_TRIGGER_DATA) {
+ if (verbose)
+ printf("invalid user_data %p for table trigger\n", user_data);
+ return;
+ }
+
+
+ trig->event = event;
+ trig->table.handle = te->table.handle;
+ strncpy(trig->table.name, te->table.name,
+ MQI_DIMENSION(trig->table.name) - 1);
+}
+
+static void row_event_cb(mqi_event_t *evt, void *user_data)
+{
+ mqi_event_type_t event = evt->event;
+ mqi_row_event_t *re = &evt->row;
+ trigger_t *trig;
+ query_t *row;
+
+ if (ntrigger >= (int)MQI_DIMENSION(triggers)) {
+ if (verbose)
+ printf("test framework error: trigger log overflow\n");
+ return;
+ }
+
+ trig = triggers + ntrigger++;
+
+ if (event != mqi_row_inserted && event != mqi_row_deleted) {
+ if (verbose)
+ printf("invalid event %d for row trigger\n", event);
+ return;
+ }
+
+ if (user_data != ROW_TRIGGER_DATA) {
+ if (verbose)
+ printf("invalid user_data %p for row trigger\n", user_data);
+ return;
+ }
+
+ if (!(row = (query_t *)re->select.data)) {
+ if (verbose)
+ printf("no selected data\n");
+ return;
+ }
+
+
+ trig->event = event;
+ trig->table.handle = re->table.handle;
+ strncpy(trig->table.name, re->table.name,
+ MQI_DIMENSION(trig->table.name) - 1);
+ trig->row.id = row->id;
+ strncpy(trig->row.first_name, row->first_name,
+ MQI_DIMENSION(trig->row.first_name) - 1);
+ strncpy(trig->row.family_name, row->family_name,
+ MQI_DIMENSION(trig->row.family_name) - 1);
+}
+
+static void column_event_cb(mqi_event_t *evt, void *user_data)
+{
+ mqi_event_type_t event = evt->event;
+ mqi_column_event_t *ce = &evt->column;
+ trigger_t *trig;
+ query_t *row;
+
+ if (ntrigger >= (int)MQI_DIMENSION(triggers)) {
+ if (verbose)
+ printf("test framework error: trigger log overflow\n");
+ return;
+ }
+
+ trig = triggers + ntrigger++;
+
+ if (event != mqi_column_changed) {
+ if (verbose)
+ printf("invalid event %d for column trigger\n", event);
+ return;
+ }
+
+ if (user_data != COLUMN_TRIGGER_DATA) {
+ if (verbose)
+ printf("invalid user_data %p for column trigger\n", user_data);
+ return;
+ }
+
+ if (!(row = (query_t *)ce->select.data)) {
+ if (verbose)
+ printf("no selected data\n");
+ return;
+ }
+
+
+ trig->event = event;
+ trig->table.handle = ce->table.handle;
+ strncpy(trig->table.name, ce->table.name,
+ MQI_DIMENSION(trig->table.name) - 1);
+ trig->row.id = row->id;
+ strncpy(trig->row.first_name, row->first_name,
+ MQI_DIMENSION(trig->row.first_name) - 1);
+ strncpy(trig->row.family_name, row->family_name,
+ MQI_DIMENSION(trig->row.family_name) - 1);
+ trig->col.index = ce->column.index;
+ strncpy(trig->col.name, ce->column.name,
+ MQI_DIMENSION(trig->col.name) - 1);
+
+#define PRINT_VALUE(fmt,t) \
+ snprintf(trig->col.value, MQI_DIMENSION(trig->col.value) - 1, \
+ fmt " => " fmt, ce->value.old.t, ce->value.new_.t)
+#define PRINT_INVALID \
+ snprintf(trig->col.value, MQI_DIMENSION(trig->col.value) - 1, \
+ "<invalid> => <invalid>")
+
+ switch(ce->value.type) {
+ case mqi_varchar: PRINT_VALUE("'%s'" , varchar ); break;
+ case mqi_integer: PRINT_VALUE("%d" , integer ); break;
+ case mqi_unsignd: PRINT_VALUE("%u" , unsignd ); break;
+ case mqi_floating: PRINT_VALUE("%.2lf", floating); break;
+ case mqi_blob: PRINT_INVALID; break;
+ default: PRINT_INVALID; break;
+ }
+
+#undef PRINT_INVALID
+#undef PRINT_VALUE
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <libgen.h>
+
+#include <check.h>
+
+#include <murphy-db/mqi.h>
+#include <murphy-db/mql.h>
+
+
+#ifndef LOGFILE
+#define LOGFILE "check_libmql.log"
+#endif
+
+#define PREREQUISITE(t) t(_i)
+
+#define TRIGGER_DATA(idx) (void *)0xdeadbeef##idx
+#define TRANSACT_TRIGGER_DATA TRIGGER_DATA(1)
+#define TABLE_TRIGGER_DATA TRIGGER_DATA(2)
+#define ROW_TRIGGER_DATA TRIGGER_DATA(3)
+#define COLUMN_TRIGGER_DATA TRIGGER_DATA(4)
+
+
+typedef struct {
+ const char *sex;
+ const char *first_name;
+ const char *family_name;
+ uint32_t id;
+ const char *email;
+} record_t;
+
+static mqi_column_def_t persons_columns[] = {
+ {"sex" , mqi_varchar, 6, 0},
+ {"family_name", mqi_varchar, 12, 0},
+ {"first_name" , mqi_varchar, 12, 0},
+ {"id" , mqi_unsignd, 4, 0},
+ {"email" , mqi_varchar, 24, 0}
+};
+static int persons_ncolumn = MQI_DIMENSION(persons_columns);
+
+static record_t persons_rows[] = {
+ {"male" , "Chuck", "Norris" , 1100, "cno@texas.us" },
+ {"male" , "Gary", "Cooper" , 700, "gco@heaven.org"},
+ {"male" , "Elvis", "Presley", 600, "epr@heaven.org"},
+ {"male" , "Tom", "Cruise" , 500, "tcr@foo.com" },
+ {"female", "Greta", "Garbo" , 2000, "gga@heaven.org"},
+ {"female", "Rita", "Hayworth", 44, "rha@heaven.org"}
+};
+static int persons_nrow = MQI_DIMENSION(persons_rows);
+
+
+static int verbose;
+static struct {
+ mql_statement_t *begin;
+ mql_statement_t *commit;
+ mql_statement_t *rollback;
+ mql_statement_t *filtered_select;
+ mql_statement_t *full_select;
+ mql_statement_t *update;
+ mql_statement_t *delete;
+ mql_statement_t *insert;
+} persons;
+
+
+static Suite *libmql_suite(void);
+static TCase *basic_tests(void);
+
+static void transaction_event_cb(mql_result_t *, void *);
+static void table_event_cb(mql_result_t *, void *);
+static void row_event_cb(mql_result_t *, void *);
+static void column_event_cb(mql_result_t *, void *);
+
+
+
+int main(int argc, char **argv)
+{
+ Suite *s = libmql_suite();
+ SRunner *sr = srunner_create(s);
+ int nf;
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (!strcmp("-v", argv[i]))
+ verbose = 1;
+ else if (!strcmp("-f", argv[i]))
+ srunner_set_fork_status(sr, CK_NOFORK);
+ else {
+ printf("Usage: %s [-h] [-v] [-f]\n"
+ " -h prints this message\n"
+ " -v sets verbose mode\n"
+ " -f forces no-forking mode\n",
+ basename(argv[0]));
+ exit(strcmp("-h", argv[i]) ? 1 : 0);
+ }
+ }
+
+ srunner_set_log(sr, LOGFILE);
+
+ srunner_run_all(sr, CK_NORMAL);
+
+ nf = srunner_ntests_failed(sr);
+
+ srunner_free(sr);
+ // suite_free(s);
+
+ return (nf == 0) ? 0 : 1;
+}
+
+
+START_TEST(open_db)
+{
+ int sts = mqi_open();
+
+ fail_if(sts, "db open test");
+}
+END_TEST
+
+
+
+START_TEST(create_table_persons)
+{
+ mql_result_t *r;
+
+ PREREQUISITE(open_db);
+
+ r = mql_exec_string(mql_result_string,
+ "CREATE TEMPORARY TABLE persons ("
+ " sex VARCHAR(6), "
+ " family_name VARCHAR(12),"
+ " first_name VARCHAR(12),"
+ " id UNSIGNED, "
+ " email VARCHAR(24) "
+ ")"
+ );
+
+ fail_unless(mql_result_is_success(r), "error: %s",
+ mql_result_error_get_message(r));
+
+ mql_result_free(r);
+}
+END_TEST
+
+
+
+START_TEST(describe_persons)
+{
+ mql_result_type_t rt = verbose ? mql_result_string : mql_result_columns;
+ mqi_column_def_t *cd;
+ mql_result_t *r;
+ mqi_data_type_t type;
+ const char *name;
+ int length;
+ int i,n;
+
+ PREREQUISITE(create_table_persons);
+
+ r = mql_exec_string(rt, "DESCRIBE persons");
+
+ fail_unless(mql_result_is_success(r), "error: %s",
+ mql_result_error_get_message(r));
+
+ if (verbose)
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ n = mql_result_columns_get_column_count(r);
+
+ fail_if(n < 1, "invalid column count %d", n);
+ fail_if(n != persons_ncolumn, "coulumn count is %d but "
+ "it supposed to be %d", n, persons_ncolumn);
+
+ for (i = 0; i < n; i++) {
+ cd = persons_columns + i;
+ name = mql_result_columns_get_name(r, i);
+ type = mql_result_columns_get_type(r, i);
+ length = mql_result_columns_get_length(r, i);
+
+ fail_if(strcmp(name, cd->name), "column%d name mismatch "
+ "('%s' vs. '%s')", i, cd->name, name);
+
+ fail_if(type != cd->type, "column%d type mismatch (%s vs. %s)",
+ i, mqi_data_type_str(cd->type), mqi_data_type_str(type));
+
+ fail_if(length != cd->length, "column%d length mismatch "
+ "(%d vs. %d)", i, cd->length, length);
+ }
+ }
+
+ mql_result_free(r);
+}
+END_TEST
+
+
+
+
+START_TEST(create_index_on_persons)
+{
+ static bool done;
+
+ mql_result_t *r;
+
+ if (!done) {
+ PREREQUISITE(create_table_persons);
+
+ r = mql_exec_string(mql_result_string,
+ "CREATE INDEX ON persons (family_name, first_name)");
+
+ fail_unless(mql_result_is_success(r), "error: %s",
+ mql_result_error_get_message(r));
+
+ done = true;
+ }
+}
+END_TEST
+
+
+
+START_TEST(insert_into_persons)
+{
+ mql_result_t *r;
+ record_t *p;
+ char statement[512];
+ int i;
+
+ PREREQUISITE(create_index_on_persons);
+
+ for (i = 0; i < persons_nrow; i++) {
+ p = persons_rows + i;
+
+ snprintf(statement, sizeof(statement),
+ "INSERT INTO persons VALUES ('%s', '%s', '%s', %u, '%s')",
+ p->sex, p->family_name, p->first_name, p->id, p->email);
+
+ r = mql_exec_string(mql_result_string, statement);
+
+ fail_unless(mql_result_is_success(r), "error @ row%d: %s",
+ i, mql_result_error_get_message(r));
+ }
+}
+END_TEST
+
+
+START_TEST(make_persons)
+{
+ static int done;
+
+ if (!done) {
+ PREREQUISITE(insert_into_persons);
+ done = 1;
+ }
+}
+END_TEST
+
+START_TEST(precompile_transaction_statements)
+{
+#define TRID "transaction_1"
+
+ static char *string[] = {
+ "BEGIN " TRID,
+ "COMMIT " TRID,
+ "ROLLBACK " TRID
+ };
+
+ static mql_statement_t **stmnt[] = {
+ &persons.begin,
+ &persons.commit,
+ &persons.rollback
+ };
+
+ static int done;
+
+ int i;
+
+ if (!done) {
+ fail_unless(MQI_DIMENSION(string) == MQI_DIMENSION(stmnt),
+ "internal error: dimension mismatch in %s()", __FILE__);
+
+ for (i = 0; i < (int)MQI_DIMENSION(string); i++) {
+ if (!(*(stmnt[i]) = mql_precompile(string[i]))) {
+ fail("precompilation error of '%s' (%s)",
+ string[i], strerror(errno));
+ }
+ }
+ }
+
+#undef TRID
+}
+END_TEST
+
+
+START_TEST(precompile_filtered_person_select)
+{
+ mql_statement_t *stmnt;
+
+ PREREQUISITE(make_persons);
+
+ stmnt = mql_precompile("SELECT id, first_name, family_name FROM persons"
+ " WHERE id > %u & id <= %u");
+
+ fail_if(!stmnt, "precompilation error (%s)", strerror(errno));
+
+ persons.filtered_select = stmnt;
+}
+END_TEST
+
+
+
+START_TEST(precompile_full_person_select)
+{
+ mql_statement_t *stmnt;
+
+ PREREQUISITE(make_persons);
+
+ if (!persons.full_select) {
+ stmnt = mql_precompile("SELECT id, first_name, family_name"
+ " FROM persons");
+
+ fail_if(!stmnt, "precompilation error (%s)", strerror(errno));
+
+ persons.full_select = stmnt;
+ }
+}
+END_TEST
+
+
+
+START_TEST(precompile_update_persons)
+{
+ mql_statement_t *stmnt;
+
+ PREREQUISITE(make_persons);
+
+ if (!persons.update) {
+ stmnt = mql_precompile("UPDATE persons "
+ " SET family_name = %s,"
+ " first_name = %s"
+ " WHERE id = %u");
+
+ fail_if(!stmnt, "precompilation error (%s)", strerror(errno));
+
+ persons.update = stmnt;
+ }
+}
+END_TEST
+
+
+
+START_TEST(precompile_delete_from_persons)
+{
+ mql_statement_t *stmnt;
+
+ PREREQUISITE(make_persons);
+
+ if (!persons.delete) {
+ stmnt = mql_precompile("DELETE FROM persons WHERE family_name = %s");
+
+ fail_if(!stmnt, "precompilation error (%s)", strerror(errno));
+
+ persons.delete = stmnt;
+ }
+}
+END_TEST
+
+
+
+START_TEST(precompile_insert_into_persons)
+{
+ mql_statement_t *stmnt;
+
+ PREREQUISITE(make_persons);
+
+ if (!persons.insert) {
+ stmnt = mql_precompile("INSERT INTO persons VALUES ("
+ " 'male', 'Baltzar','Veijo', 855, 'vba@pdf.org'"
+ ")");
+
+ fail_if(!stmnt, "precompilation error (%s)", strerror(errno));
+
+ persons.insert = stmnt;
+ }
+}
+END_TEST
+
+
+
+START_TEST(exec_precompiled_filtered_select_from_persons)
+{
+ mql_result_type_t rt = verbose ? mql_result_string : mql_result_rows;
+ mql_result_t *r;
+ int n;
+
+ PREREQUISITE(precompile_filtered_person_select);
+
+ if (mql_bind_value(persons.filtered_select, 1, mqi_unsignd, 200) < 0 ||
+ mql_bind_value(persons.filtered_select, 2, mqi_unsignd, 1100) < 0 )
+ {
+ fail("bind error (%s)", strerror(errno));
+ }
+
+ r = mql_exec_statement(rt, persons.filtered_select);
+
+ fail_unless(mql_result_is_success(r), "exec error: %s",
+ mql_result_error_get_message(r));
+
+ if (verbose)
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ if ((n = mql_result_rows_get_row_count(r)) != 4)
+ fail("row number mismatch (4 vs. %d)", n);
+ }
+
+ mql_result_free(r);
+
+ mql_statement_free(persons.filtered_select);
+ persons.filtered_select = NULL;
+}
+END_TEST
+
+
+
+START_TEST(exec_precompiled_full_select_from_persons)
+{
+ mql_result_type_t rt = verbose ? mql_result_string : mql_result_rows;
+ mql_result_t *r;
+ int n;
+
+ PREREQUISITE(precompile_full_person_select);
+
+ r = mql_exec_statement(rt, persons.full_select);
+
+ fail_unless(mql_result_is_success(r), "exec error: %s",
+ mql_result_error_get_message(r));
+
+ if (verbose)
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ if ((n = mql_result_rows_get_row_count(r)) != persons_nrow)
+ fail("row number mismatch (%d vs. %d)", persons_nrow, n);
+ }
+
+ mql_result_free(r);
+
+ mql_statement_free(persons.full_select);
+ persons.full_select = NULL;
+}
+END_TEST
+
+START_TEST(exec_precompiled_update_persons)
+{
+ static uint32_t id = 2000;
+ static const char *new_first = "Marilyn";
+ static const char *new_family = "Monroe";
+
+ PREREQUISITE(precompile_update_persons);
+
+ mql_result_type_t rt = verbose ? mql_result_string : mql_result_rows;
+ mql_result_t *r;
+ record_t *p;
+ const char *first;
+ const char *family;
+ int updated;
+ int i, n;
+
+ /* 2000: Greta Garbo => Marilyn Monroe */
+ if (mql_bind_value(persons.update, 1, mqi_string , new_family) < 0 ||
+ mql_bind_value(persons.update, 2, mqi_string , new_first) < 0 ||
+ mql_bind_value(persons.update, 3, mqi_unsignd, id) < 0 )
+ {
+ fail("bind error (%s)", strerror(errno));
+ }
+
+
+ r = mql_exec_statement(mql_result_string, persons.update);
+
+ fail_unless(mql_result_is_success(r), "exec error: %s",
+ mql_result_error_get_message(r));
+
+ mql_result_free(r);
+
+ /* verification */
+ r = mql_exec_string(rt, "SELECT id, first_name, family_name FROM persons");
+
+ fail_unless(mql_result_is_success(r), "exec error @ verifying select: %s",
+ mql_result_error_get_message(r));
+
+ if (verbose)
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ for (p = NULL, i = 0; i < persons_nrow; i++) {
+ if (persons_rows[i].id == id) {
+ p = persons_rows + i;
+ break;
+ }
+ }
+
+ n = mql_result_rows_get_row_count(r);
+
+ for (updated = 0, i = 0; i < n; i++) {
+ family = mql_result_rows_get_string(r, 1, i, NULL,0);
+ first = mql_result_rows_get_string(r, 2, i, NULL,0);
+
+ if (p) {
+ fail_if(!strcmp(first, p->first_name), "found original "
+ "first name '%s'", p->first_name);
+ fail_if(!strcmp(family, p->family_name), "found original "
+ "family name '%s'", p->family_name);
+ }
+ else {
+ fail_if(!strcmp(first, new_first), "found new "
+ "first name '%s'", first);
+ fail_if(!strcmp(family, new_family), "found new "
+ "family name '%s'", family);
+ }
+
+ if (id == mql_result_rows_get_unsigned(r, 0, i)) {
+ if (strcmp(first, new_first) || strcmp(family, new_family)) {
+ updated = 1;
+ }
+ }
+ }
+
+ if (p)
+ fail_unless(updated, "result is success but no actual update");
+ else
+ fail_unless(!updated, "update happened but it not supposed to");
+ }
+
+ mql_result_free(r);
+
+
+ mql_statement_free(persons.update);
+ persons.update = NULL;
+}
+END_TEST
+
+
+START_TEST(exec_precompiled_delete_from_persons)
+{
+ const char *del_family = "Cruise";
+
+ mql_result_type_t rt = verbose ? mql_result_string : mql_result_rows;
+ mql_result_t *r;
+ record_t *p;
+ uint32_t id;
+ const char *first;
+ const char *family;
+ int i,n;
+
+ PREREQUISITE(precompile_delete_from_persons);
+
+ /* delete Tom Cruise */
+ if (mql_bind_value(persons.delete, 1, mqi_string , del_family) < 0)
+ fail("bind error (%s)", strerror(errno));
+
+ r = mql_exec_statement(mql_result_string, persons.delete);
+
+ fail_unless(mql_result_is_success(r), "exec error: %s",
+ mql_result_error_get_message(r));
+
+ mql_result_free(r);
+
+
+ /* verification */
+ r = mql_exec_string(rt, "SELECT id, first_name, family_name FROM persons");
+
+ fail_unless(mql_result_is_success(r), "exec error @ verifying select: %s",
+ mql_result_error_get_message(r));
+
+ if (verbose)
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ for (p = NULL, i = 0; i < persons_nrow; i++) {
+ if (!strcmp(persons_rows[i].family_name, del_family)) {
+ p = persons_rows + i;
+ break;
+ }
+ }
+
+ n = mql_result_rows_get_row_count(r);
+
+ for (i = 0; i < n; i++) {
+ id = mql_result_rows_get_unsigned(r, 0, i);
+ first = mql_result_rows_get_string(r, 1, i, NULL,0);
+ family = mql_result_rows_get_string(r, 2, i, NULL,0);
+
+ if (p) {
+ /* supposed to be deleted */
+ fail_if(id == p->id, "found id %u of the presumably "
+ "deleted row", id);
+ fail_if(!strcmp(first, p->first_name), "found first name '%s' "
+ "of the presumably deleted row", first);
+ fail_if(!strcmp(family, p->family_name), "found family name "
+ "'%s' of the presumably deleted row", family);
+ }
+ else {
+ /* nothing supposed to be deleted */
+ fail_if(!strcmp(family, del_family), "found family name '%s'"
+ "what not supposed to be there", family);
+ }
+ }
+ }
+
+ mql_result_free(r);
+
+ mql_statement_free(persons.delete);
+ persons.delete = NULL;
+}
+END_TEST
+
+
+
+START_TEST(exec_precompiled_insert_into_persons)
+{
+ mql_result_type_t rt = verbose ? mql_result_string : mql_result_rows;
+ mql_result_t *r;
+ record_t *p;
+ const char *first;
+ const char *family;
+ int inserted;
+ int i,n;
+
+ PREREQUISITE(precompile_insert_into_persons);
+
+
+ for (p = NULL, i = 0; i < persons_nrow; i++) {
+ if (!strcmp(persons_rows[i].family_name, "Baltzar") &&
+ !strcmp(persons_rows[i].first_name , "Veijo") )
+ {
+ p = persons_rows + i;
+ break;
+ }
+ }
+
+ /* insert Veijo Baltzar */
+ r = mql_exec_statement(mql_result_string, persons.insert);
+
+ if (p)
+ fail_if(mql_result_is_success(r), "manage to insert a duplicate");
+ else {
+ fail_unless(mql_result_is_success(r), "exec error: %s",
+ mql_result_error_get_message(r));
+ }
+
+ mql_result_free(r);
+
+
+ /* verification */
+ r = mql_exec_string(rt, "SELECT id, first_name, family_name FROM persons");
+
+ fail_unless(mql_result_is_success(r), "exec error @ verifying select: %s",
+ mql_result_error_get_message(r));
+
+ if (verbose)
+ printf("%s\n", mql_result_string_get(r));
+ else {
+ if (!p) {
+ n = mql_result_rows_get_row_count(r);
+
+ for (inserted = 0, i = 0; i < n; i++) {
+ first = mql_result_rows_get_string(r, 1, i, NULL,0);
+ family = mql_result_rows_get_string(r, 2, i, NULL,0);
+
+ if (!strcmp(first, "Veijo") && !strcmp(family, "Baltzar")) {
+ inserted = 1;
+ break;
+ }
+ }
+
+ fail_unless(inserted, "Veijo does not seem to be an the artist");
+ }
+ }
+
+
+
+
+ mql_result_free(r);
+
+
+ mql_statement_free(persons.insert);
+ persons.insert = NULL;
+}
+END_TEST
+
+START_TEST(register_transaction_event_cb)
+{
+ int sts;
+
+ PREREQUISITE(open_db);
+
+ sts = mql_register_callback("transaction_event_cb", mql_result_string,
+ transaction_event_cb, TRANSACT_TRIGGER_DATA);
+
+ fail_if(sts < 0, "failed to create 'table_event_cb': %s",
+ strerror(errno));
+}
+END_TEST
+
+
+START_TEST(register_table_event_cb)
+{
+ int sts;
+
+ sts = mql_register_callback("table_event_cb", mql_result_string,
+ table_event_cb, TABLE_TRIGGER_DATA);
+
+ fail_if(sts < 0, "failed to create 'table_event_cb': %s",
+ strerror(errno));
+}
+END_TEST
+
+START_TEST(register_row_event_cb)
+{
+ int sts;
+
+ PREREQUISITE(make_persons);
+
+ sts = mql_register_callback("row_event_cb", mql_result_string,
+ row_event_cb, ROW_TRIGGER_DATA);
+
+ fail_if(sts < 0, "failed to create 'row_event_cb': %s",
+ strerror(errno));
+}
+END_TEST
+
+START_TEST(register_column_event_cb)
+{
+ int sts;
+
+ PREREQUISITE(make_persons);
+
+ sts = mql_register_callback("column_event_cb", mql_result_string,
+ column_event_cb, COLUMN_TRIGGER_DATA);
+
+ fail_if(sts < 0, "failed to create 'column_event_cb': %s",
+ strerror(errno));
+}
+END_TEST
+
+START_TEST(table_trigger)
+{
+ static char *mqlstr = "CREATE TRIGGER table_trigger"
+ " ON TABLES CALLBACK table_event_cb";
+
+ mql_result_t *r;
+
+ PREREQUISITE(open_db);
+ PREREQUISITE(register_table_event_cb);
+
+ r = mql_exec_string(mql_result_dontcare, mqlstr);
+
+ fail_unless(mql_result_is_success(r),"failed to exec '%s': (%d) %s",mqlstr,
+ mql_result_error_get_code(r), mql_result_error_get_message(r));
+
+ PREREQUISITE(make_persons);
+}
+END_TEST
+
+START_TEST(row_trigger)
+{
+ static char *mqlstr = "CREATE TRIGGER row_trigger"
+ " ON ROWS IN persons"
+ " CALLBACK row_event_cb"
+ " SELECT id, first_name, family_name";
+
+
+ mql_result_t *r;
+
+ PREREQUISITE(register_row_event_cb);
+ PREREQUISITE(precompile_transaction_statements);
+
+ r = mql_exec_statement(mql_result_string, persons.begin);
+
+ fail_unless(mql_result_is_success(r), "failed to begin transaction: %s",
+ strerror(errno));
+
+ r = mql_exec_string(mql_result_dontcare, mqlstr);
+
+ fail_unless(mql_result_is_success(r),"failed to exec '%s': (%d) %s",mqlstr,
+ mql_result_error_get_code(r), mql_result_error_get_message(r));
+
+ PREREQUISITE(exec_precompiled_insert_into_persons);
+ PREREQUISITE(exec_precompiled_delete_from_persons);
+
+ r = mql_exec_statement(mql_result_string, persons.commit);
+
+ fail_unless(mql_result_is_success(r), "failed to commit transaction: %s",
+ strerror(errno));
+}
+END_TEST
+
+START_TEST(column_trigger)
+{
+ static char *mqlstr = "CREATE TRIGGER column_trigger"
+ " ON COLUMN first_name IN persons"
+ " CALLBACK column_event_cb"
+ " SELECT id, first_name, family_name";
+
+ mql_result_t *r;
+
+ PREREQUISITE(register_column_event_cb);
+ PREREQUISITE(precompile_transaction_statements);
+
+ r = mql_exec_statement(mql_result_string, persons.begin);
+
+ fail_unless(mql_result_is_success(r), "failed to begin transaction: %s",
+ strerror(errno));
+
+ r = mql_exec_string(mql_result_dontcare, mqlstr);
+
+ fail_unless(mql_result_is_success(r),"failed to exec '%s': (%d) %s",mqlstr,
+ mql_result_error_get_code(r), mql_result_error_get_message(r));
+
+ PREREQUISITE(exec_precompiled_update_persons);
+
+ r = mql_exec_statement(mql_result_string, persons.commit);
+
+ fail_unless(mql_result_is_success(r), "failed to commit transaction: %s",
+ strerror(errno));
+}
+END_TEST
+
+
+START_TEST(transaction_trigger)
+{
+ static char *mqlstr = "CREATE TRIGGER transaction_trigger ON TRANSACTIONS"
+ " CALLBACK transaction_event_cb";
+
+ mql_result_t *r;
+
+ PREREQUISITE(register_transaction_event_cb);
+
+ r = mql_exec_string(mql_result_dontcare, mqlstr);
+
+ fail_unless(mql_result_is_success(r),"failed to exec '%s': (%d) %s",mqlstr,
+ mql_result_error_get_code(r), mql_result_error_get_message(r));
+
+ PREREQUISITE(column_trigger);
+}
+END_TEST
+
+static Suite *libmql_suite(void)
+{
+ Suite *s = suite_create("Murphy Query Language - libmql");
+ TCase *tc_basic = basic_tests();
+
+ suite_add_tcase(s, tc_basic);
+
+ return s;
+}
+
+
+static TCase *basic_tests(void)
+{
+ TCase *tc = tcase_create("basic tests");
+
+ tcase_add_test(tc, open_db);
+ tcase_add_test(tc, create_table_persons);
+ tcase_add_test(tc, describe_persons);
+ tcase_add_test(tc, create_index_on_persons);
+ tcase_add_test(tc, insert_into_persons);
+ tcase_add_test(tc, precompile_transaction_statements);
+ tcase_add_test(tc, precompile_filtered_person_select);
+ tcase_add_test(tc, precompile_full_person_select);
+ tcase_add_test(tc, precompile_update_persons);
+ tcase_add_test(tc, precompile_delete_from_persons);
+ tcase_add_test(tc, precompile_insert_into_persons);
+ tcase_add_test(tc, exec_precompiled_filtered_select_from_persons);
+ tcase_add_test(tc, exec_precompiled_full_select_from_persons);
+ tcase_add_test(tc, exec_precompiled_update_persons);
+ tcase_add_test(tc, exec_precompiled_delete_from_persons);
+ tcase_add_test(tc, exec_precompiled_insert_into_persons);
+ tcase_add_test(tc, register_transaction_event_cb);
+ tcase_add_test(tc, register_table_event_cb);
+ tcase_add_test(tc, register_row_event_cb);
+ tcase_add_test(tc, register_column_event_cb);
+ tcase_add_test(tc, table_trigger);
+ tcase_add_test(tc, row_trigger);
+ tcase_add_test(tc, column_trigger);
+ tcase_add_test(tc, transaction_trigger);
+
+ return tc;
+}
+
+
+static void transaction_event_cb(mql_result_t *result, void *user_data)
+{
+ MQI_UNUSED(user_data);
+
+ if (result->type == mql_result_string) {
+ if (verbose)
+ printf("---\n%s\n", mql_result_string_get(result));
+ }
+ else if (result->type == mql_result_event) {
+ }
+ else {
+ if (verbose)
+ printf("%s: invalid result type %d\n", __FUNCTION__, result->type);
+ }
+}
+
+static void table_event_cb(mql_result_t *result, void *user_data)
+{
+ MQI_UNUSED(user_data);
+
+ if (result->type == mql_result_string) {
+ if (verbose)
+ printf("---\n%s\n", mql_result_string_get(result));
+ }
+ else if (result->type == mql_result_event) {
+ }
+ else {
+ if (verbose)
+ printf("%s: invalid result type %d\n", __FUNCTION__, result->type);
+ }
+}
+
+static void row_event_cb(mql_result_t *result, void *user_data)
+{
+ MQI_UNUSED(user_data);
+
+ if (result->type == mql_result_string) {
+ if (verbose)
+ printf("---\n%s\n", mql_result_string_get(result));
+ }
+ else if (result->type == mql_result_event) {
+ }
+ else {
+ if (verbose)
+ printf("%s: invalid result type %d\n", __FUNCTION__, result->type);
+ }
+}
+
+static void column_event_cb(mql_result_t *result, void *user_data)
+{
+ MQI_UNUSED(user_data);
+
+ if (result->type == mql_result_string) {
+ if (verbose)
+ printf("---\n%s\n", mql_result_string_get(result));
+ }
+ else if (result->type == mql_result_event) {
+ }
+ else {
+ if (verbose)
+ printf("%s: invalid result type %d\n", __FUNCTION__, result->type);
+ }
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CONSOLE_PROTOCOL_H__
+#define __MURPHY_CONSOLE_PROTOCOL_H__
+
+#define MRP_CONSOLE_INPUT 0x1
+#define MRP_CONSOLE_OUTPUT 0x2
+#define MRP_CONSOLE_PROMPT 0x3
+#define MRP_CONSOLE_BYE 0x4
+
+
+#endif /* __MURPHY_CONSOLE_PROTOCOL_H__ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
+
+%.crt:
+ cert="$@"; \
+ make -f /etc/ssl/certs/Makefile $@ && \
+ mv $${cert%.crt}.key $${cert%.crt}.key.protected && \
+ openssl rsa -in $${cert%.crt}.key.protected -out $${cert%.crt}.key
--- /dev/null
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-15">
+
+<style type="text/css">
+textarea.console_input {
+ font-size: 12pt;
+ font-family: monospace;
+ background-color: black;
+ padding: 2px;
+ margin: 0 0 0 0;
+ color: white;
+ resize: none;
+ overflow: visible;
+}
+
+textarea.console_output {
+ font-size: 12pt;
+ font-family: monospace;
+ background-color: black;
+ padding: 2px;
+ margin: 0 0 0 0;
+ color: white;
+ resize: none;
+}
+</style>
+
+<script type="text/javascript" src="console.js"></script>
+
+<title>Murphy Console (disconnected)</title>
+</head>
+
+
+<body onLoad="setupConsole();">
+
+<script type="text/javascript">
+
+var mrpc = null;
+
+function setupConsole () {
+ var cmds = {
+ connect: cmd_connect,
+ disconnect: cmd_disconnect,
+ resize: cmd_resize
+ };
+
+ mrpc = new MrpConsole("console_div", cmds);
+ var addr = mrpc.socketUri(document.URL);
+
+ console.log("Trying to connect to Murphy @ " + addr);
+
+ mrpc.connect(addr, "murphy");
+ mrpc.focus();
+
+ mrpc.onconnected = function () {
+ document.title = "Murphy Console @ " + addr;
+ mrpc.append("Connection to Murphy console established.\n");
+ };
+
+ mrpc.onclosed = function () {
+ document.title = "Murphy Console (disconnected)";
+ mrpc.append("Murphy console connection closed.\n");
+ mrpc.append("Use 'connect' to try to reconnect.\n");
+ };
+}
+
+
+function cmd_connect(args) {
+ var addr;
+
+ addr = mrpc.socketUri(document.URL);
+ console.log("Trying to reconnect...");
+ mrpc.connect(addr, "murphy");
+}
+
+
+function cmd_disconnect() {
+
+ console.log("Disconnecting...");
+ addr = mrpc.disconnect();
+}
+
+
+function cmd_resize(args) {
+ mrpc.resize(args[0], args[1]);
+}
+
+
+</script>
+
+<div id="console_div"></div>
+</body></html>
--- /dev/null
+/*
+ * An Ode to My Suckage in Javascript...
+ *
+ * It'd be nice if someone wrote a relatively simple readline-like + output
+ * javascript package (ie. not a full VT100 terminal emulator like termlib).
+ */
+
+
+/*
+ * custom console error type
+ */
+
+function MrpConsoleError(message) {
+ this.name = "Murphy Console Error";
+ this.message = message;
+}
+
+
+/** Create a Murphy console, put it after next_elem_id. */
+function MrpConsole(next_elem_id, user_commands) {
+ var sck, input, output, div, next_elem;
+
+ this.reset();
+ this.commands = user_commands;
+
+ /* create output text area */
+ output = document.createElement("textarea");
+ output.console = this;
+ this.output = output;
+
+ output.setAttribute("class" , "console_output");
+ output.setAttribute("cols" , 80);
+ output.setAttribute("rows" , 25);
+ output.setAttribute("readonly" , true);
+ output.setAttribute("disabled" , true);
+ output.setAttribute("spellcheck", false);
+ output.nline = 0;
+ output.onfocus = function () { return false; }
+
+ /* create input text area, hook up input handler */
+ input = document.createElement("textarea");
+ input.console = this;
+ this.input = input;
+
+ input.setAttribute("class" , "console_input");
+ input.setAttribute("cols" , 81);
+ input.setAttribute("rows" , 1);
+ input.setAttribute("spellcheck", false);
+ input.setAttribute("autofocus" , "autofocus");
+
+ input.onkeyup = this.onkeyup;
+ input.onkeypress = this.onkeypress;
+
+ next_elem = document.getElementById(next_elem_id);
+
+ if (!next_elem)
+ throw new MrpConsoleError("element " + next_elem_id + " not found");
+
+ div = document.createElement("div");
+ div.appendChild(output);
+ div.appendChild(input);
+
+ /* insert console div to document */
+ document.body.insertBefore(div, next_elem);
+
+ this.setInput("");
+}
+
+
+/** Reset/initialize internal state to the disconnected defaults. */
+MrpConsole.prototype.reset = function () {
+ this.connected = false;
+ this.server = null;
+ this.sck = null;
+
+ this.history = new Array ();
+ this.histidx = 0;
+ if (!this.input)
+ this.prompt = "disconnected";
+ else
+ this.setPrompt("disconnected");
+}
+
+
+/** Resize the console. */
+MrpConsole.prototype.resize = function (width, height) {
+ if (width && width > 0) {
+ this.output.cols = width;
+ this.input.cols = width;
+ }
+ if (height && height > 0)
+ this.output.rows = height;
+}
+
+
+/** Get the focus to the console. */
+MrpConsole.prototype.focus = function () {
+ if (this.input)
+ this.input.focus();
+}
+
+
+/** Write output to the console, replacing its current contents. */
+MrpConsole.prototype.write = function (text, noscroll) {
+ var out = this.output;
+
+ out.value = text;
+ out.nline = text.split("\n").length;
+
+ if (!noscroll)
+ this.scrollBottom();
+}
+
+
+/** Append output to the console. */
+MrpConsole.prototype.append = function (text, noscroll) {
+ var out = this.output;
+
+ out.value += text;
+ out.nline += text.split("\n").length;
+
+ if (!noscroll)
+ this.scrollBottom();
+}
+
+
+/** Set the content of the input field to 'prompt> text'. */
+MrpConsole.prototype.setInput = function (text) {
+ this.input.value = this.prompt + "> " + text;
+}
+
+
+/** Get the current input value (without the prompt). */
+MrpConsole.prototype.getInput = function () {
+ if (this.input)
+ return this.input.value.slice(this.prompt.length + 2).split("\n")[0];
+ else
+ return "";
+}
+
+
+/** Set the input prompt to the given value. */
+MrpConsole.prototype.setPrompt = function (prompt) {
+ var value = this.getInput();
+
+ if (!this.input)
+ this.prompt = prompt;
+ else {
+ this.prompt = prompt;
+ this.input.value = this.prompt + "> " + value;
+ }
+}
+
+
+/** Scroll the output window up or down the given amount of lines. */
+MrpConsole.prototype.scroll = function (amount) {
+ var out = this.output;
+ var pxl = (out.nline ? (out.scrollHeight / out.nline) : 0);
+ var top = out.scrollTop + (amount * pxl);
+
+ if (top < 0)
+ top = 0;
+ if (top > out.scrollHeight)
+ top = out.scrollHeight;
+
+ out.scrollTop = top;
+}
+
+
+/** Scroll the output window up or down by a 'page'. */
+MrpConsole.prototype.scrollPage = function (dir) {
+ var out = this.output;
+ var nline = 2 * 25 / 3;
+
+ if (dir < 0)
+ dir = -1;
+ else
+ dir = +1;
+
+ this.scroll(dir * nline);
+}
+
+
+/** Scroll to the bottom. */
+MrpConsole.prototype.scrollBottom = function () {
+ this.output.scrollTop = this.output.scrollHeight;
+}
+
+
+/** Add a new entry to the history. */
+MrpConsole.prototype.historyAppend = function (entry) {
+ if (entry.length > 0) {
+ this.history.push(entry);
+ this.histidx = this.history.length;
+
+ this.setInput("");
+ }
+}
+
+
+/** Go to the previous history entry. */
+MrpConsole.prototype.historyShow = function (dir) {
+ var idx = this.histidx + dir;
+
+ if (0 <= idx && idx < this.history.length) {
+ if (this.histidx == this.history.length &&
+ this.input.value.length > 0) {
+ /* Hmm... autoinsert to history, not the Right Thing To Do... */
+ this.historyAppend(this.getInput());
+ }
+
+ this.histidx = idx;
+ this.setInput(this.history[this.histidx]);
+ }
+ else if (idx >= this.history.length) {
+ this.histidx = this.history.length;
+ this.setInput("");
+ }
+}
+
+
+/** Make sure the input position never enters the prompt. */
+MrpConsole.prototype.checkInputPosition = function () {
+ var pos = this.input.selectionStart;
+
+ if (pos <= this.prompt.length + 2) {
+ this.input.selectionStart = this.prompt.length + 2;
+ this.input.selectionEnd = this.prompt.length + 2;
+ return false;
+ }
+ else
+ return true;
+}
+
+
+/** Key up handler. */
+MrpConsole.prototype.onkeyup = function (e) {
+ var c = this.console;
+ var l;
+
+ /*console.log("got key " + e.which);*/
+
+ switch (e.keyCode) {
+ case e.DOM_VK_RETURN:
+ if (c.input.value.length > c.prompt.length + 2) {
+ l = c.getInput();
+ c.historyAppend(l);
+ c.processCmd(l);
+ c.setInput("");
+ }
+ break;
+ case e.DOM_VK_PAGE_UP:
+ if (e.shiftKey)
+ c.scrollPage(-1);
+ break;
+ case e.DOM_VK_PAGE_DOWN:
+ if (e.shiftKey)
+ c.scrollPage(+1);
+ break;
+
+ case e.DOM_VK_UP:
+ if (!e.shiftKey)
+ c.historyShow(-1);
+ else
+ c.scroll(-1);
+ break;
+ case e.DOM_VK_DOWN:
+ if (!e.shiftKey)
+ c.historyShow(+1);
+ else
+ c.scroll(+1);
+ break;
+ case e.DOM_VK_LEFT:
+ case e.DOM_VK_BACK_SPACE:
+ if (!c.checkInputPosition())
+ return false;
+ break;
+ }
+
+ return true;
+}
+
+
+/** Key-press handler. */
+MrpConsole.prototype.onkeypress = function (e) {
+ var c = this.console;
+ var rows;
+
+ switch (e.which) {
+ case e.DOM_VK_LEFT:
+ case e.DOM_VK_BACK_SPACE:
+ if (!c.checkInputPosition())
+ return false;
+ }
+
+ rows = Math.floor(1 + (c.input.value.length / c.input.cols));
+
+ if (c.input.rows < rows)
+ c.input.rows = rows;
+ else if (c.input.rows > rows)
+ c.input.rows = rows;
+
+ return true;
+}
+
+
+/** Connect to the Murphy daemon running at the given address. */
+MrpConsole.prototype.connect = function (address) {
+ var c = this.console;
+
+ if (this.connected)
+ throw new MrpConsoleError("already connected to " + this.address);
+ else {
+ this.server = address;
+ this.connected = false;
+
+ this.setPrompt("connecting");
+
+ if (typeof MozWebSocket != "undefined")
+ sck = new MozWebSocket(this.server, "murphy");
+ else
+ sck = new WebSocket(this.server, "murphy");
+
+ this.sck = sck;
+ sck.console = this;
+ sck.onopen = this.sckconnect;
+ sck.onclose = this.sckclosed;
+ sck.onerror = this.sckerror;
+ sck.onmessage = this.sckmessage;
+ }
+}
+
+
+/** Close the console connection. */
+MrpConsole.prototype.disconnect = function () {
+ if (this.connected) {
+ this.sck.close();
+ }
+}
+
+
+/** Connection established event handler. */
+MrpConsole.prototype.sckconnect = function () {
+ var c = this.console;
+
+ c.connected = true;
+ c.setPrompt("connected");
+
+ if (c.onconnected)
+ c.onconnected();
+}
+
+
+/** Connection shutdown event handler. */
+MrpConsole.prototype.sckclosed = function () {
+ var c = this.console;
+
+ console.log("socket closed");
+
+ c.reset();
+
+ if (c.onclosed)
+ c.onclosed();
+}
+
+
+/** Socket error event handler. */
+MrpConsole.prototype.sckerror = function (e) {
+ var c = this.console;
+
+ c.reset();
+
+ if (c.onerror)
+ c.onerror();
+
+ if (c.onclosed)
+ c.onclosed();
+}
+
+
+/** Socket message event handler. */
+MrpConsole.prototype.sckmessage = function (message) {
+ var c = this.console;
+ var msg = JSON.parse(message.data);
+
+ if (msg.prompt)
+ c.setPrompt(msg.prompt);
+ else {
+ if (msg.output) {
+ c.append(msg.output);
+ }
+ }
+}
+
+
+/** Send a request to the server. */
+MrpConsole.prototype.send_request = function (req) {
+ var sck = this.sck;
+
+ sck.send(JSON.stringify(req));
+}
+
+
+/** Process a command entered by the user. */
+MrpConsole.prototype.processCmd = function (cmd) {
+ var c, l, cb, args;
+
+ for (c in this.commands) {
+ l = c.length;
+ cb = this.commands[c];
+ if (cmd.substring(0, l) == c && (cmd.length == l ||
+ cmd.substring(l, l + 1) == ' ')) {
+ args = cmd.split(' ').splice(1);
+
+ this.commands[c](args);
+
+ return;
+ }
+ }
+
+ this.append(this.prompt + "> " + cmd + "\n");
+ this.send_request({ input: cmd });
+
+ if (cmd == 'help') {
+ if (this.commands && Object.keys(this.commands).length > 0) {
+ this.append("Web console commands:\n");
+ for (c in this.commands) {
+ this.append(" " + c + "\n");
+ }
+ }
+ else
+ this.append("No Web console commands.");
+ }
+}
+
+
+/** Determine a WebSocket URI based on an HTTP URI. */
+MrpConsole.prototype.socketUri = function (http_uri) {
+ var proto, colon, rest;
+
+ colon = http_uri.indexOf(':'); /* get first colon */
+ proto = http_uri.substring(0, colon); /* get protocol */
+ rest = http_uri.substring(colon + 3); /* get URI sans protocol:// */
+ addr = rest.split("/")[0]; /* strip URI path from address */
+
+ switch (proto) {
+ case "http": return "ws://" + addr;
+ case "https": return "wss://" + addr;
+ default: return null;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <murphy/common.h>
+#include <murphy/core.h>
+
+#include "config.h"
+
+#ifdef WEBSOCKETS_ENABLED
+# include <murphy/common/wsck-transport.h>
+# include <murphy/common/json.h>
+#endif
+
+#include <murphy/plugins/console-protocol.h>
+
+#define DEFAULT_ADDRESS "unxs:@murphy-console" /* default console address */
+
+#ifdef MURPHY_DATADIR /* default content dir */
+# define DEFAULT_HTTPDIR MURPHY_DATADIR"/webconsole"
+#else
+# define DEFAULT_HTTPDIR "/usr/share/murphy/webconsole"
+#endif
+
+enum {
+ DEBUG_NONE = 0x0,
+ DEBUG_FUNC = 0x1,
+ DEBUG_FILE = 0x2,
+ DEBUG_LINE = 0x4,
+ DEBUG_DEFAULT = DEBUG_FUNC
+};
+
+
+/*
+ * an active console instance
+ */
+
+typedef struct {
+ mrp_console_t *mc; /* associated murphy console */
+ mrp_transport_t *t; /* associated transport */
+ mrp_sockaddr_t addr; /* for temp. datagram 'connection' */
+ socklen_t alen; /* address length if any */
+ int id; /* console ID for log redirection */
+ int dbgmeta; /* debug metadata to show */
+} console_t;
+
+
+/*
+ * console plugin data
+ */
+
+typedef struct {
+ const char *address; /* console address */
+ mrp_transport_t *t; /* transport we're listening on */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_list_hook_t clients; /* active console clients */
+ mrp_sockaddr_t addr; /* resolved transport address */
+ socklen_t alen; /* address length */
+ console_t *c; /* datagram console being served */
+ const char *httpdir; /* WRT console agent directory */
+ const char *sslcert; /* path to SSL certificate */
+ const char *sslpkey; /* path to SSL private key */
+ const char *sslca; /* path to SSL CA */
+} data_t;
+
+
+
+static int next_id = 1;
+
+static ssize_t write_req(mrp_console_t *mc, void *buf, size_t size)
+{
+ console_t *c = (console_t *)mc->backend_data;
+ mrp_msg_t *msg;
+ uint16_t tag, type;
+ uint32_t len;
+
+ tag = MRP_CONSOLE_OUTPUT;
+ type = MRP_MSG_FIELD_BLOB;
+ len = size;
+ msg = mrp_msg_create(tag, type, len, buf, NULL);
+
+ if (msg != NULL) {
+ mrp_transport_send(c->t, msg);
+ mrp_msg_unref(msg);
+
+ return size;
+ }
+ else
+ return -1;
+}
+
+
+static void logger(void *data, mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, va_list ap)
+{
+ console_t *c = (console_t *)data;
+ va_list cp;
+ const char *prefix;
+ char buf[256], lnstr[64];
+
+ MRP_UNUSED(file);
+ MRP_UNUSED(line);
+ MRP_UNUSED(func);
+
+ switch (level) {
+ case MRP_LOG_ERROR: prefix = "[log] E: "; break;
+ case MRP_LOG_WARNING: prefix = "[log] W: "; break;
+ case MRP_LOG_INFO: prefix = "[log] I: "; break;
+ case MRP_LOG_DEBUG:
+ if (c->dbgmeta & DEBUG_LINE)
+ snprintf(lnstr, sizeof(lnstr), ":%d", line);
+ else
+ lnstr[0] = '\0';
+ snprintf(buf, sizeof(buf), "[log] D: %s%s%s%s%s%s%s",
+ c->dbgmeta ? "[" : "",
+ c->dbgmeta & DEBUG_FUNC ? func : "",
+ c->dbgmeta & DEBUG_FILE ? "@" : "",
+ c->dbgmeta & DEBUG_FILE ? file : "",
+ c->dbgmeta & DEBUG_LINE ? lnstr : "",
+ c->dbgmeta ? "]" : "",
+ c->dbgmeta ? " " : "");
+ prefix = buf;
+ break;
+ default: prefix = "[log] ?: ";
+ }
+
+ va_copy(cp, ap);
+ mrp_console_printf(c->mc, "%s", prefix);
+ mrp_console_vprintf(c->mc, format, cp);
+ mrp_console_printf(c->mc, "\n");
+ va_end(cp);
+}
+
+
+static void debug_cb(mrp_console_t *mc, void *user_data, int argc, char **argv)
+{
+ console_t *c = (console_t *)mc->backend_data;
+ int debug;
+ const char *p, *n;
+ int i, l;
+
+ MRP_UNUSED(user_data);
+
+ debug = 0;
+ for (i = 2; i < argc; i++) {
+ p = argv[i];
+ while (p && *p) {
+ if ((n = strchr(p, ',')) != NULL)
+ l = n - p;
+ else
+ l = strlen(p);
+
+ if (!strncmp(p, "function", l) ||
+ !strncmp(p, "func" , l)) debug |= DEBUG_FUNC;
+ else if (!strncmp(p, "file" , l)) debug |= DEBUG_FILE;
+ else if (!strncmp(p, "line" , l)) debug |= DEBUG_LINE;
+ else
+ mrp_log_warning("Unknown console debug flag '%*.*s'.", l, l, p);
+
+ if ((p = n) != NULL)
+ p++;
+ }
+ }
+
+ c->dbgmeta = debug & ((debug & ~DEBUG_LINE) ? -1 : ~DEBUG_LINE);
+
+ if (c->dbgmeta != debug)
+ mrp_log_warning("Orphan console debug flag 'line' forced off.");
+}
+
+
+static void register_logger(console_t *c)
+{
+ char name[32];
+
+ if (!c->id)
+ return;
+
+ snprintf(name, sizeof(name), "console/%d", c->id);
+ mrp_log_register_target(name, logger, c);
+}
+
+
+static void unregister_logger(console_t *c)
+{
+ char name[32];
+
+ if (!c->id)
+ return;
+
+ snprintf(name, sizeof(name), "console/%d", c->id);
+ mrp_log_unregister_target(name);
+}
+
+
+static void set_prompt_req(mrp_console_t *mc, const char *prompt)
+{
+ console_t *c = (console_t *)mc->backend_data;
+ mrp_msg_t *msg;
+ uint16_t tag, type;
+
+ tag = MRP_CONSOLE_PROMPT;
+ type = MRP_MSG_FIELD_STRING;
+ msg = mrp_msg_create(tag, type, prompt, NULL);
+
+ if (msg != NULL) {
+ mrp_transport_send(c->t, msg);
+ mrp_msg_unref(msg);
+ }
+}
+
+
+static void free_req(void *backend_data)
+{
+ mrp_free(backend_data);
+}
+
+
+static void recv_cb(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ console_t *c = (console_t *)user_data;
+ mrp_msg_field_t *f;
+ char *input;
+ size_t size;
+
+ MRP_UNUSED(t);
+
+ if ((f = mrp_msg_find(msg, MRP_CONSOLE_INPUT)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_BLOB) {
+ input = f->str;
+ size = f->size[0];
+
+ if (size > 0) {
+ MRP_CONSOLE_BUSY(c->mc, {
+ c->mc->evt.input(c->mc, input, size);
+ });
+
+ c->mc->check_destroy(c->mc);
+ }
+
+ return;
+ }
+ }
+
+ mrp_log_warning("Ignoring malformed message from console/%d...", c->id);
+}
+
+
+static void recvfrom_cb(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t alen, void *user_data)
+{
+ console_t *c = (console_t *)user_data;
+ mrp_sockaddr_t obuf;
+ socklen_t olen;
+ mrp_msg_field_t *f;
+ char *input;
+ size_t size;
+
+ MRP_UNUSED(t);
+
+ if ((f = mrp_msg_find(msg, MRP_CONSOLE_INPUT)) != NULL) {
+ if (f->type == MRP_MSG_FIELD_BLOB) {
+ input = f->str;
+ size = f->size[0];
+
+ if (size > 0) {
+ mrp_sockaddr_cpy(&obuf, &c->addr, olen = c->alen);
+ mrp_sockaddr_cpy(&c->addr, addr, c->alen = alen);
+ mrp_transport_connect(t, addr, alen);
+
+ MRP_CONSOLE_BUSY(c->mc, {
+ c->mc->evt.input(c->mc, input, size);
+ });
+
+ c->mc->check_destroy(c->mc);
+
+
+ mrp_transport_disconnect(t);
+
+ if (olen) {
+ mrp_transport_connect(t, &obuf, olen);
+ mrp_sockaddr_cpy(&c->addr, &obuf, c->alen = olen);
+ }
+
+ return;
+ }
+ }
+ }
+
+ mrp_log_warning("Ignoring malformed message from console/%d...", c->id);
+}
+
+
+/*
+ * generic stream transport
+ */
+
+#define stream_write_req write_req
+#define stream_set_prompt_req set_prompt_req
+#define stream_free_req free_req
+#define stream_recv_cb recv_cb
+
+static void stream_close_req(mrp_console_t *mc)
+{
+ console_t *c = (console_t *)mc->backend_data;
+
+ if (c->t != NULL) {
+ mrp_transport_disconnect(c->t);
+ mrp_transport_destroy(c->t);
+ unregister_logger(c);
+
+ c->t = NULL;
+ }
+}
+
+
+static void stream_connection_cb(mrp_transport_t *lt, void *user_data)
+{
+ static mrp_console_req_t req;
+
+ data_t *data = (data_t *)user_data;
+ console_t *c;
+ int flags;
+
+ if ((c = mrp_allocz(sizeof(*c))) != NULL) {
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ c->t = mrp_transport_accept(lt, c, flags);
+
+ if (c->t != NULL) {
+ req.write = stream_write_req;
+ req.close = stream_close_req;
+ req.free = stream_free_req;
+ req.set_prompt = stream_set_prompt_req;
+
+ c->mc = mrp_create_console(data->ctx, &req, c);
+
+ if (c->mc != NULL) {
+ c->id = next_id++;
+ c->dbgmeta = DEBUG_DEFAULT;
+ register_logger(c);
+
+ return;
+ }
+ else {
+ mrp_transport_destroy(c->t);
+ c->t = NULL;
+ }
+ }
+ }
+}
+
+
+static void stream_closed_cb(mrp_transport_t *t, int error, void *user_data)
+{
+ console_t *c = (console_t *)user_data;
+
+ if (error)
+ mrp_log_error("Connection to console/%d closed with error %d (%s).",
+ c->id, error, strerror(error));
+ else {
+ mrp_log_info("console/%d has closed the connection.", c->id);
+
+ mrp_transport_disconnect(t);
+ mrp_transport_destroy(t);
+ unregister_logger(c);
+ c->t = NULL;
+ }
+}
+
+
+static int stream_setup(data_t *data)
+{
+ static mrp_transport_evt_t evt;
+
+ mrp_mainloop_t *ml = data->ctx->ml;
+ mrp_transport_t *t;
+ const char *type;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ int flags;
+
+ t = NULL;
+ alen = sizeof(addr);
+ alen = mrp_transport_resolve(NULL, data->address, &addr, alen, &type);
+
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve console transport address '%s'.",
+ data->address);
+
+ return FALSE;
+ }
+
+ evt.connection = stream_connection_cb;
+ evt.closed = stream_closed_cb;
+ evt.recvmsg = stream_recv_cb;
+ evt.recvmsgfrom = NULL;
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+ t = mrp_transport_create(ml, type, &evt, data, flags);
+
+ if (t != NULL) {
+ if (mrp_transport_bind(t, &addr, alen) && mrp_transport_listen(t, 1)) {
+ data->t = t;
+
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Failed to bind console to '%s'.", data->address);
+ mrp_transport_destroy(t);
+ }
+ }
+ else
+ mrp_log_error("Failed to create console transport.");
+
+ return FALSE;
+}
+
+
+/*
+ * datagram transports
+ */
+
+#define dgram_write_req write_req
+#define dgram_free_req free_req
+#define dgram_set_prompt_req set_prompt_req
+
+#define dgram_recv_cb recv_cb
+#define dgram_recvfrom_cb recvfrom_cb
+
+
+static void dgram_close_req(mrp_console_t *mc)
+{
+ console_t *c = (console_t *)mc->backend_data;
+ mrp_msg_t *msg;
+ uint16_t tag, type;
+
+ tag = MRP_CONSOLE_BYE;
+ type = MRP_MSG_FIELD_BOOL;
+ msg = mrp_msg_create(tag, type, TRUE, NULL);
+
+ if (msg != NULL) {
+ mrp_transport_send(c->t, msg);
+ mrp_msg_unref(msg);
+ }
+
+ mrp_transport_disconnect(c->t);
+}
+
+
+static int dgram_setup(data_t *data)
+{
+ static mrp_transport_evt_t evt;
+ static mrp_console_req_t req;
+
+ mrp_mainloop_t *ml = data->ctx->ml;
+ mrp_transport_t *t;
+ const char *type;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ int flags;
+ console_t *c;
+
+ t = NULL;
+ alen = sizeof(addr);
+ alen = mrp_transport_resolve(NULL, data->address, &addr, alen, &type);
+
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve console transport address '%s'.",
+ data->address);
+
+ return FALSE;
+ }
+
+ c = mrp_allocz(sizeof(*c));
+
+ if (c != NULL) {
+ evt.recvmsg = dgram_recv_cb;
+ evt.recvmsgfrom = dgram_recvfrom_cb;
+ evt.connection = NULL;
+ evt.closed = NULL;
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+ t = mrp_transport_create(ml, type, &evt, c, flags);
+
+ if (t != NULL) {
+ if (mrp_transport_bind(t, &addr, alen)) {
+ req.write = dgram_write_req;
+ req.close = dgram_close_req;
+ req.free = dgram_free_req;
+ req.set_prompt = dgram_set_prompt_req;
+
+ c->t = t;
+ c->mc = mrp_create_console(data->ctx, &req, c);
+
+ if (c->mc != NULL) {
+ data->c = c;
+ c->mc->preserve = TRUE;
+
+ return TRUE;
+ }
+ else
+ mrp_log_error("Failed to create console.");
+ }
+ else
+ mrp_log_error("Failed to bind console to '%s'.", data->address);
+
+ c->t = NULL;
+ mrp_transport_destroy(t);
+ }
+ else
+ mrp_log_error("Failed to create console transport.");
+
+ mrp_free(c);
+ }
+
+ return FALSE;
+}
+
+
+#ifdef WEBSOCKETS_ENABLED
+
+/*
+ * websocket transport
+ */
+
+#define wsock_close_req stream_close_req
+#define wsock_free_req free_req
+#define wsock_closed_cb stream_closed_cb
+
+static ssize_t wsock_write_req(mrp_console_t *mc, void *buf, size_t size)
+{
+ console_t *c = (console_t *)mc->backend_data;
+ mrp_json_t *msg;
+
+ msg = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (msg != NULL) {
+ if (mrp_json_add_string_slice(msg, "output", buf, size))
+ mrp_transport_sendcustom(c->t, msg);
+
+ mrp_json_unref(msg);
+
+ return size;
+ }
+ else
+ return -1;
+}
+
+
+static void wsock_set_prompt_req(mrp_console_t *mc, const char *prompt)
+{
+ console_t *c = (console_t *)mc->backend_data;
+ mrp_json_t *msg;
+
+ msg = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (msg != NULL) {
+ if (mrp_json_add_string(msg, "prompt", prompt))
+ mrp_transport_sendcustom(c->t, msg);
+
+ mrp_json_unref(msg);
+ }
+}
+
+
+static void wsock_recv_cb(mrp_transport_t *t, void *data, void *user_data)
+{
+ console_t *c = (console_t *)user_data;
+ mrp_json_t *msg = (mrp_json_t *)data;
+ const char *s;
+ char *input;
+ size_t size;
+
+ MRP_UNUSED(t);
+
+ s = mrp_json_object_to_string((mrp_json_t *)data);
+
+ mrp_debug("recived WRT console message:");
+ mrp_debug(" %s", s);
+
+ if (mrp_json_get_string(msg, "input", &input)) {
+ size = strlen(input);
+
+ if (size > 0) {
+ MRP_CONSOLE_BUSY(c->mc, {
+ c->mc->evt.input(c->mc, input, size);
+ });
+
+ c->mc->check_destroy(c->mc);
+ }
+ }
+}
+
+
+static void wsock_connection_cb(mrp_transport_t *lt, void *user_data)
+{
+ static mrp_console_req_t req;
+ data_t *data = (data_t *)user_data;
+ console_t *c;
+
+ mrp_debug("incoming web console connection...");
+
+ if ((c = mrp_allocz(sizeof(*c))) != NULL) {
+ c->t = mrp_transport_accept(lt, c, 0);
+
+ if (c->t != NULL) {
+ req.write = wsock_write_req;
+ req.close = wsock_close_req;
+ req.free = wsock_free_req;
+ req.set_prompt = wsock_set_prompt_req;
+
+ c->mc = mrp_create_console(data->ctx, &req, c);
+
+ if (c->mc != NULL) {
+ c->id = next_id++;
+ register_logger(c);
+
+ return;
+ }
+ else {
+ mrp_transport_destroy(c->t);
+ c->t = NULL;
+ }
+ }
+ }
+}
+
+
+static int wsock_setup(data_t *data)
+{
+ static mrp_transport_evt_t evt;
+
+ mrp_mainloop_t *ml = data->ctx->ml;
+ const char *cert = data->sslcert;
+ const char *pkey = data->sslpkey;
+ const char *ca = data->sslca;
+ mrp_transport_t *t;
+ const char *type;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ int flags;
+
+ t = NULL;
+ alen = sizeof(addr);
+ alen = mrp_transport_resolve(NULL, data->address, &addr, alen, &type);
+
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve console transport address '%s'.",
+ data->address);
+
+ return FALSE;
+ }
+
+ evt.connection = wsock_connection_cb;
+ evt.closed = wsock_closed_cb;
+ evt.recvcustom = wsock_recv_cb;
+ evt.recvmsgfrom = NULL;
+
+ flags = MRP_TRANSPORT_MODE_CUSTOM;
+ t = mrp_transport_create(ml, type, &evt, data, flags);
+
+ if (t != NULL) {
+ if (cert || pkey || ca) {
+ mrp_transport_setopt(t, MRP_WSCK_OPT_SSL_CERT, cert);
+ mrp_transport_setopt(t, MRP_WSCK_OPT_SSL_PKEY, pkey);
+ mrp_transport_setopt(t, MRP_WSCK_OPT_SSL_CA , ca);
+ }
+
+ if (mrp_transport_bind(t, &addr, alen) && mrp_transport_listen(t, 1)) {
+ mrp_transport_setopt(t, MRP_WSCK_OPT_HTTPDIR, data->httpdir);
+ data->t = t;
+
+ return TRUE;
+ }
+ else {
+ mrp_log_error("Failed to bind console to '%s'.", data->address);
+ mrp_transport_destroy(t);
+ }
+ }
+ else
+ mrp_log_error("Failed to create console transport.");
+
+ return FALSE;
+}
+
+#endif /* WEBSOCKETS_ENABLED */
+
+
+enum {
+ ARG_ADDRESS, /* console transport address */
+ ARG_HTTPDIR, /* content directory for HTTP */
+ ARG_SSLCERT, /* path to SSL certificate */
+ ARG_SSLPKEY, /* path to SSL private key */
+ ARG_SSLCA /* path to SSL CA */
+};
+
+
+
+static int console_init(mrp_plugin_t *plugin)
+{
+ data_t *data;
+ int ok;
+
+ if ((data = mrp_allocz(sizeof(*data))) != NULL) {
+ mrp_list_init(&data->clients);
+
+ data->ctx = plugin->ctx;
+ data->address = plugin->args[ARG_ADDRESS].str;
+ data->httpdir = plugin->args[ARG_HTTPDIR].str;
+ data->sslcert = plugin->args[ARG_SSLCERT].str;
+ data->sslpkey = plugin->args[ARG_SSLPKEY].str;
+ data->sslca = plugin->args[ARG_SSLCA].str;
+
+ mrp_log_info("Using console address '%s'...", data->address);
+
+ if (!strncmp(data->address, "wsck:", 5)) {
+ if (data->httpdir != NULL)
+ mrp_log_info("Using '%s' for serving console Web agent...",
+ data->httpdir);
+ else
+ mrp_log_info("Not serving console Web agent...");
+ }
+
+ if (!strncmp(data->address, "tcp4:", 5) ||
+ !strncmp(data->address, "tcp6:", 5) ||
+ !strncmp(data->address, "unxs:", 5))
+ ok = stream_setup(data);
+#ifdef WEBSOCKETS_ENABLED
+ else if (!strncmp(data->address, "wsck:", 5))
+ ok = wsock_setup(data);
+#endif
+ else
+ ok = dgram_setup(data);
+
+ if (ok) {
+ plugin->data = data;
+
+ return TRUE;
+ }
+ }
+
+ mrp_free(data);
+
+ return FALSE;
+}
+
+
+static void console_exit(mrp_plugin_t *plugin)
+{
+ mrp_log_info("Cleaning up %s...", plugin->instance);
+}
+
+
+#define CONSOLE_DESCRIPTION "A debug console for Murphy."
+#define CONSOLE_HELP \
+ "The debug console provides a telnet-like remote session and a\n" \
+ "simple shell-like command interpreter with commands to help\n" \
+ "development, debugging, and trouble-shooting. The set of commands\n" \
+ "can be dynamically extended by registering new commands from\n" \
+ "other plugins."
+
+#define CONSOLE_VERSION MRP_VERSION_INT(0, 0, 1)
+#define CONSOLE_AUTHORS "Krisztian Litkey <kli@iki.fi>"
+
+
+static mrp_plugin_arg_t console_args[] = {
+ MRP_PLUGIN_ARGIDX(ARG_ADDRESS , STRING, "address", DEFAULT_ADDRESS),
+ MRP_PLUGIN_ARGIDX(ARG_HTTPDIR , STRING, "httpdir", DEFAULT_HTTPDIR),
+ MRP_PLUGIN_ARGIDX(ARG_SSLCERT , STRING, "sslcert", NULL),
+ MRP_PLUGIN_ARGIDX(ARG_SSLPKEY , STRING, "sslpkey", NULL),
+ MRP_PLUGIN_ARGIDX(ARG_SSLCA , STRING, "sslca" , NULL)
+};
+
+
+MRP_CONSOLE_GROUP(console_commands, "console", NULL, NULL, {
+ MRP_TOKENIZED_CMD("debug", debug_cb, FALSE,
+ "debug [function] [file] [line]",
+ "set debug metadata to show",
+ "Set what metadata to show for debug messages."),
+});
+
+MURPHY_REGISTER_CORE_PLUGIN("console",
+ CONSOLE_VERSION, CONSOLE_DESCRIPTION,
+ CONSOLE_AUTHORS, CONSOLE_HELP, MRP_MULTIPLE,
+ console_init, console_exit,
+ console_args, MRP_ARRAY_SIZE(console_args),
+ NULL, 0, NULL, 0, &console_commands);
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <alloca.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/transport.h>
+
+#include "domain-control-types.h"
+#include "message.h"
+#include "table.h"
+#include "client.h"
+
+
+/*
+ * mark an enforcement point busy (typically while executing a callback)
+ */
+
+#define DOMCTL_MARK_BUSY(dc, ...) do { \
+ (dc)->busy++; \
+ __VA_ARGS__ \
+ (dc)->busy--; \
+ check_destroyed(dc); \
+ } while (0)
+
+
+/*
+ * a pending request
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* hook to pending request queue */
+ uint32_t seqno; /* sequence number/request id */
+ int invoke : 1; /* whether a pending invocation */
+ union {
+ mrp_domctl_status_cb_t status; /* request completion callback */
+ mrp_domctl_return_cb_t ret; /* invocation return cb */
+ } cb;
+ void *user_data; /* opaque callback data */
+} pending_request_t;
+
+
+/*
+ * a registered proxied method
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to list of methods */
+ char *name; /* method name */
+ size_t max_out; /* max return arguments */
+ mrp_domctl_invoke_cb_t cb; /* handler callback */
+ void *user_data; /* opaque handler data */
+} method_t;
+
+static void recv_cb(mrp_transport_t *t, mrp_msg_t *msg, void *user_data);
+static void recvfrom_cb(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data);
+static void closed_cb(mrp_transport_t *t, int error, void *user_data);
+
+
+static int queue_pending(mrp_domctl_t *dc, uint32_t seq,
+ mrp_domctl_status_cb_t cb, void *user_data);
+static int notify_pending(mrp_domctl_t *dc, msg_t *msg);
+static int queue_invoke(mrp_domctl_t *dc, uint32_t seq,
+ mrp_domctl_return_cb_t cb, void *user_data);
+static void purge_pending(mrp_domctl_t *dc);
+
+
+
+
+mrp_domctl_t *mrp_domctl_create(const char *name, mrp_mainloop_t *ml,
+ mrp_domctl_table_t *tables, int ntable,
+ mrp_domctl_watch_t *watches, int nwatch,
+ mrp_domctl_connect_cb_t connect_cb,
+ mrp_domctl_watch_cb_t watch_cb, void *user_data)
+{
+ mrp_domctl_t *dc;
+ mrp_domctl_table_t *st, *dt;
+ mrp_domctl_watch_t *sw, *dw;
+ int i;
+
+ dc = mrp_allocz(sizeof(*dc));
+
+ if (dc != NULL) {
+ mrp_list_init(&dc->pending);
+ dc->ml = ml;
+
+ dc->name = mrp_strdup(name);
+ dc->tables = mrp_allocz_array(typeof(*dc->tables) , ntable);
+ dc->watches = mrp_allocz_array(typeof(*dc->watches), nwatch);
+
+ if (dc->name != NULL &&
+ (dc->tables != NULL || ntable == 0) &&
+ (dc->watches != NULL || nwatch == 0)) {
+ for (i = 0; i < ntable; i++) {
+ st = tables + i;
+ dt = dc->tables + i;
+
+ dt->table = mrp_strdup(st->table);
+ dt->mql_columns = mrp_strdup(st->mql_columns);
+ dt->mql_index = mrp_strdup(st->mql_index ? st->mql_index:"");
+
+ if (!dt->table || !dt->mql_columns || !dt->mql_index)
+ break;
+
+ dc->ntable++;
+ }
+
+ for (i = 0; i < nwatch; i++) {
+ sw = watches + i;
+ dw = dc->watches + i;
+
+ dw->table = mrp_strdup(sw->table);
+ dw->mql_columns = mrp_strdup(sw->mql_columns);
+ dw->mql_where = mrp_strdup(sw->mql_where ? sw->mql_where:"");
+ dw->max_rows = sw->max_rows;
+
+ if (!dw->table || !dw->mql_columns || !dw->mql_where)
+ break;
+
+ dc->nwatch++;
+ }
+
+ dc->connect_cb = connect_cb;
+ dc->watch_cb = watch_cb;
+ dc->user_data = user_data;
+ dc->seqno = 1;
+
+ mrp_list_init(&dc->methods);
+
+ return dc;
+ }
+
+ mrp_domctl_destroy(dc);
+ }
+
+ return NULL;
+}
+
+
+static void destroy_domctl(mrp_domctl_t *dc)
+{
+ int i;
+
+ purge_pending(dc);
+
+ for (i = 0; i < dc->ntable; i++) {
+ mrp_free((char *)dc->tables[i].table);
+ mrp_free((char *)dc->tables[i].mql_columns);
+ mrp_free((char *)dc->tables[i].mql_index);
+ }
+ mrp_free(dc->tables);
+
+ for (i = 0; i < dc->nwatch; i++) {
+ mrp_free((char *)dc->watches[i].table);
+ mrp_free((char *)dc->watches[i].mql_columns);
+ mrp_free((char *)dc->watches[i].mql_where);
+ }
+ mrp_free(dc->watches);
+
+ mrp_free(dc->name);
+ mrp_free(dc);
+}
+
+
+static inline void check_destroyed(mrp_domctl_t *dc)
+{
+ if (dc->destroyed && dc->busy <= 0) {
+ destroy_domctl(dc);
+ }
+}
+
+
+void mrp_domctl_destroy(mrp_domctl_t *dc)
+{
+ if (dc != NULL) {
+ mrp_domctl_disconnect(dc);
+
+ if (dc->busy <= 0)
+ destroy_domctl(dc);
+ else
+ dc->destroyed = TRUE;
+ }
+}
+
+
+static void notify_disconnect(mrp_domctl_t *dc, uint32_t errcode,
+ const char *errmsg)
+{
+ DOMCTL_MARK_BUSY(dc, {
+ dc->connected = FALSE;
+ dc->connect_cb(dc, FALSE, errcode, errmsg, dc->user_data);
+ });
+}
+
+
+static void notify_connect(mrp_domctl_t *dc)
+{
+ DOMCTL_MARK_BUSY(dc, {
+ dc->connected = TRUE;
+ dc->connect_cb(dc, TRUE, 0, NULL, dc->user_data);
+ });
+}
+
+
+static int domctl_register(mrp_domctl_t *dc)
+{
+ register_msg_t reg;
+ mrp_msg_t *msg;
+ int success;
+
+ mrp_clear(®);
+ reg.type = MSG_TYPE_REGISTER;
+ reg.seq = 0;
+ reg.name = dc->name;
+ reg.tables = dc->tables;
+ reg.ntable = dc->ntable;
+ reg.watches = dc->watches;
+ reg.nwatch = dc->nwatch;
+
+ msg = msg_encode_message((msg_t *)®);
+
+ if (msg != NULL) {
+ success = mrp_transport_send(dc->t, msg);
+ mrp_msg_unref(msg);
+ }
+ else
+ success = FALSE;
+
+ return success;
+}
+
+
+static int try_connect(mrp_domctl_t *dc)
+{
+ static mrp_transport_evt_t evt;
+
+ evt.closed = closed_cb;
+ evt.recvmsg = recv_cb;
+ evt.recvmsgfrom = recvfrom_cb;
+
+ dc->t = mrp_transport_create(dc->ml, dc->ttype, &evt, dc, 0);
+
+ if (dc->t != NULL) {
+ if (mrp_transport_connect(dc->t, &dc->addr, dc->addrlen))
+ if (domctl_register(dc))
+ return TRUE;
+
+ mrp_transport_destroy(dc->t);
+ dc->t = NULL;
+ }
+
+ return FALSE;
+}
+
+
+static void stop_reconnect(mrp_domctl_t *dc)
+{
+ mrp_del_timer(dc->ctmr);
+ dc->ctmr = NULL;
+}
+
+
+static void reconnect_cb(mrp_timer_t *t, void *user_data)
+{
+ mrp_domctl_t *dc = (mrp_domctl_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ if (try_connect(dc))
+ stop_reconnect(dc);
+}
+
+
+static int start_reconnect(mrp_domctl_t *dc)
+{
+ int interval;
+
+ if (dc->ctmr == NULL && dc->cival >= 0) {
+ interval = dc->cival ? 1000 * dc->cival : 5000;
+ dc->ctmr = mrp_add_timer(dc->ml, interval, reconnect_cb, dc);
+
+ if (dc->ctmr == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+int mrp_domctl_connect(mrp_domctl_t *dc, const char *address, int interval)
+{
+ mrp_sockaddr_t addr;
+ socklen_t addrlen;
+ const char *type;
+
+ if (dc == NULL)
+ return FALSE;
+
+ addrlen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &type);
+
+ if (addrlen > 0) {
+ dc->addr = addr;
+ dc->addrlen = addrlen;
+ dc->cival = interval;
+ dc->ttype = type;
+
+ if (try_connect(dc))
+ return TRUE;
+
+ if (interval >= 0)
+ return start_reconnect(dc);
+ }
+
+ return FALSE;
+}
+
+
+void mrp_domctl_disconnect(mrp_domctl_t *dc)
+{
+ if (dc->t != NULL) {
+ stop_reconnect(dc);
+ mrp_transport_destroy(dc->t);
+ dc->t = NULL;
+ dc->connected = FALSE;
+ }
+}
+
+
+int mrp_domctl_set_data(mrp_domctl_t *dc, mrp_domctl_data_t *tables, int ntable,
+ mrp_domctl_status_cb_t cb, void *user_data)
+{
+ set_msg_t set;
+ mrp_msg_t *msg;
+ uint32_t seq = dc->seqno++;
+ int success, i;
+
+ if (!dc->connected)
+ return FALSE;
+
+ for (i = 0; i < ntable; i++) {
+ if (tables[i].id < 0 || tables[i].id >= dc->ntable)
+ return FALSE;
+ }
+
+ mrp_clear(&set);
+ set.type = MSG_TYPE_SET;
+ set.seq = seq;
+ set.tables = tables;
+ set.ntable = ntable;
+
+ msg = msg_encode_message((msg_t *)&set);
+
+ if (msg != NULL) {
+ success = mrp_transport_send(dc->t, msg);
+ mrp_msg_unref(msg);
+
+ if (success)
+ queue_pending(dc, seq, cb, user_data);
+
+ return success;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_domctl_invoke(mrp_domctl_t *dc, const char *name, int narg,
+ mrp_domctl_arg_t *args, mrp_domctl_return_cb_t reply_cb,
+ void *user_data)
+{
+ invoke_msg_t invoke;
+ mrp_msg_t *msg;
+ uint32_t seq = dc->seqno++;
+ int success;
+
+ if (!dc->connected)
+ return FALSE;
+
+ if (reply_cb == NULL && user_data != NULL)
+ return FALSE;
+
+ mrp_clear(&invoke);
+ invoke.type = MSG_TYPE_INVOKE;
+ invoke.seq = seq;
+ invoke.name = name;
+ invoke.noret = reply_cb ? TRUE : FALSE;
+ invoke.narg = narg;
+ invoke.args = args;
+
+ msg = msg_encode_message((msg_t *)&invoke);
+
+ if (msg != NULL) {
+ success = mrp_transport_send(dc->t, msg);
+ mrp_msg_unref(msg);
+
+ if (success)
+ queue_invoke(dc, seq, reply_cb, user_data);
+
+ return success;
+ }
+ else
+ return FALSE;
+}
+
+
+int mrp_domctl_register_methods(mrp_domctl_t *dc, mrp_domctl_method_def_t *defs,
+ size_t ndef)
+{
+ mrp_domctl_method_def_t *def;
+ method_t *m;
+ size_t i;
+
+ for (i = 0, def = defs; i < ndef; i++, def++) {
+ m = mrp_allocz(sizeof(*m));
+
+ if (m == NULL)
+ return FALSE;
+
+ mrp_list_init(&m->hook);
+
+ m->name = mrp_strdup(def->name);
+ m->max_out = def->max_out;
+ m->cb = def->cb;
+ m->user_data = def->user_data;
+
+ if (m->name == NULL) {
+ mrp_free(m);
+ return FALSE;
+ }
+
+ mrp_list_append(&dc->methods, &m->hook);
+ }
+
+ return TRUE;
+}
+
+
+static method_t *find_method(mrp_domctl_t *dc, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ method_t *m;
+
+ mrp_list_foreach(&dc->methods, p, n) {
+ m = mrp_list_entry(p, typeof(*m), hook);
+
+ if (!strcmp(m->name, name))
+ return m;
+ }
+
+ return NULL;
+}
+
+
+static void process_ack(mrp_domctl_t *dc, ack_msg_t *ack)
+{
+ if (ack->seq != 0)
+ notify_pending(dc, (msg_t *)ack);
+ else
+ notify_connect(dc);
+}
+
+
+static void process_nak(mrp_domctl_t *dc, nak_msg_t *nak)
+{
+ if (nak->seq != 0)
+ notify_pending(dc, (msg_t *)nak);
+ else
+ notify_disconnect(dc, nak->error, nak->msg);
+}
+
+
+static void process_notify(mrp_domctl_t *dc, notify_msg_t *notify)
+{
+ dc->watch_cb(dc, notify->tables, notify->ntable, dc->user_data);
+}
+
+
+static void process_invoke(mrp_domctl_t *dc, invoke_msg_t *invoke)
+{
+ method_t *m;
+ mrp_domctl_arg_t *args, error;
+ int narg;
+ return_msg_t ret;
+ mrp_msg_t *msg;
+ int i;
+
+ mrp_clear(&ret);
+
+ m = find_method(dc, invoke->name);
+
+ ret.type = MSG_TYPE_RETURN;
+ ret.seq = invoke->seq;
+
+ if (m == NULL) {
+ ret.error = MRP_DOMCTL_NOTFOUND;
+ args = NULL;
+ narg = 0;
+ }
+ else {
+ ret.error = MRP_DOMCTL_OK;
+
+ narg = ret.narg = m->max_out;
+
+ if (narg > 0) {
+ args = ret.args = alloca(narg * sizeof(args[0]));
+ memset(args, 0, narg * sizeof(args[0]));
+ }
+ else
+ args = NULL;
+
+ ret.retval = m->cb(dc, invoke->narg, invoke->args,
+ &ret.narg, ret.args, m->user_data);
+ }
+
+ msg = msg_encode_message((msg_t *)&ret);
+
+ if (msg == NULL) {
+ error.type = MRP_DOMCTL_STRING;
+ error.str = "failed to encode return message (arguments)";
+ ret.error = MRP_DOMAIN_FAILED;
+ ret.narg = 1;
+ ret.args = &error;
+
+ msg = msg_encode_message((msg_t *)&ret);
+
+ ret.narg = 0;
+ ret.args = args = NULL;
+ }
+
+ if (msg != NULL) {
+ mrp_transport_send(dc->t, msg);
+ mrp_msg_unref(msg);
+ }
+
+ narg = ret.narg;
+ for (i = 0; i < narg; i++) {
+ if (args[i].type == MRP_DOMCTL_STRING)
+ mrp_free((char *)args[i].str);
+ else if (MRP_DOMCTL_IS_ARRAY(args[i].type)) {
+ uint32_t j;
+
+ for (j = 0; j < args[i].size; j++)
+ if (MRP_DOMCTL_ARRAY_TYPE(args[i].type) == MRP_DOMCTL_STRING)
+ mrp_free(((char **)args[i].arr)[j]);
+
+ mrp_free(args[i].arr);
+ }
+ }
+}
+
+
+static void process_return(mrp_domctl_t *dc, return_msg_t *ret)
+{
+ notify_pending(dc, (msg_t *)ret);
+}
+
+
+static void recv_cb(mrp_transport_t *t, mrp_msg_t *tmsg, void *user_data)
+{
+ mrp_domctl_t *dc = (mrp_domctl_t *)user_data;
+ msg_t *msg;
+
+ MRP_UNUSED(t);
+
+ /*
+ mrp_log_info("Received message:");
+ mrp_msg_dump(msg, stdout);
+ */
+
+ msg = msg_decode_message(tmsg);
+
+ if (msg != NULL) {
+ switch (msg->any.type) {
+ case MSG_TYPE_NOTIFY:
+ process_notify(dc, &msg->notify);
+ break;
+ case MSG_TYPE_ACK:
+ process_ack(dc, &msg->ack);
+ break;
+ case MSG_TYPE_NAK:
+ process_nak(dc, &msg->nak);
+ break;
+ case MSG_TYPE_INVOKE:
+ process_invoke(dc, &msg->invoke);
+ break;
+ case MSG_TYPE_RETURN:
+ process_return(dc, &msg->ret);
+ break;
+ default:
+ mrp_domctl_disconnect(dc);
+ notify_disconnect(dc, EINVAL, "unexpected message from server");
+ break;
+ }
+
+ msg_free_message(msg);
+ }
+ else {
+ mrp_domctl_disconnect(dc);
+ notify_disconnect(dc, EINVAL, "invalid message from server");
+ }
+}
+
+
+static void recvfrom_cb(mrp_transport_t *t, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data)
+{
+ MRP_UNUSED(t);
+ MRP_UNUSED(msg);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+ MRP_UNUSED(user_data);
+
+ /* XXX TODO:
+ * This should neither be called nor be necessary to specify.
+ * However, currently the transport layer mandates having to
+ * give both recv and recvfrom event callbacks if no connection
+ * event callback is given. However this is not correct because
+ * on a client side one wants to be able to create a connection-
+ * oriented transport without either connection or recvfrom event
+ * callbacks. This needs to be fixed in transport by moving the
+ * appropriate callback checks lower in the stack to the actual
+ * transport backends.
+ */
+
+ mrp_log_error("Whoa... recvfrom called for a connected transport.");
+ exit(1);
+}
+
+
+static void closed_cb(mrp_transport_t *t, int error, void *user_data)
+{
+ mrp_domctl_t *dc = (mrp_domctl_t *)user_data;
+
+ MRP_UNUSED(t);
+ MRP_UNUSED(dc);
+
+ if (error)
+ notify_disconnect(dc, error, strerror(error));
+ else {
+ notify_disconnect(dc, ECONNRESET, "server has closed the connection");
+ start_reconnect(dc);
+ }
+}
+
+
+static int queue_pending(mrp_domctl_t *dc, uint32_t seq,
+ mrp_domctl_status_cb_t cb, void *user_data)
+{
+ pending_request_t *pending;
+
+ pending = mrp_allocz(sizeof(*pending));
+
+ if (pending != NULL) {
+ mrp_list_init(&pending->hook);
+
+ pending->invoke = false;
+ pending->seqno = seq;
+ pending->cb.status = cb;
+ pending->user_data = user_data;
+
+ mrp_list_append(&dc->pending, &pending->hook);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int notify_pending(mrp_domctl_t *dc, msg_t *msg)
+{
+ mrp_list_hook_t *p, *n;
+ pending_request_t *pending;
+ uint32_t seq;
+ int error, status;
+ const char *message;
+ int narg;
+ mrp_domctl_arg_t *args;
+ int success;
+
+ seq = msg->any.seq;
+
+ mrp_list_foreach(&dc->pending, p, n) {
+ pending = mrp_list_entry(p, typeof(*pending), hook);
+
+ if (pending->seqno != seq)
+ continue;
+
+ if (!pending->invoke) {
+ switch (msg->any.type) {
+ case MSG_TYPE_ACK:
+ error = 0;
+ message = NULL;
+ goto notify;
+ case MSG_TYPE_NAK:
+ error = msg->nak.error;
+ message = msg->nak.msg;
+ notify:
+ DOMCTL_MARK_BUSY(dc, {
+ pending->cb.status(dc, error, message,
+ pending->user_data);
+ });
+ success = TRUE;
+ break;
+ default:
+ success = FALSE;
+ break;
+ }
+ }
+ else {
+ if (msg->any.type == MSG_TYPE_RETURN) {
+ error = msg->ret.error;
+ status = msg->ret.retval;
+ narg = msg->ret.narg;
+ args = msg->ret.args;
+
+ DOMCTL_MARK_BUSY(dc, {
+ pending->cb.ret(dc, error, status, narg, args,
+ pending->user_data);
+ });
+ success = TRUE;
+ }
+ else
+ success = FALSE;
+ }
+
+ mrp_list_delete(&pending->hook);
+ mrp_free(pending);
+
+ return success;
+ }
+
+ return FALSE;
+}
+
+
+static int queue_invoke(mrp_domctl_t *dc, uint32_t seq,
+ mrp_domctl_return_cb_t cb, void *user_data)
+{
+ pending_request_t *pending;
+
+ if (cb == NULL)
+ return TRUE;
+
+ pending = mrp_allocz(sizeof(*pending));
+
+ if (pending != NULL) {
+ mrp_list_init(&pending->hook);
+
+ pending->invoke = true;
+ pending->seqno = seq;
+ pending->cb.ret = cb;
+ pending->user_data = user_data;
+
+ mrp_list_append(&dc->pending, &pending->hook);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void purge_pending(mrp_domctl_t *dc)
+{
+ mrp_list_hook_t *p, *n;
+ pending_request_t *pending;
+
+ mrp_list_foreach(&dc->pending, p, n) {
+ pending = mrp_list_entry(p, typeof(*pending), hook);
+
+ mrp_list_delete(&pending->hook);
+ mrp_free(pending);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_CLIENT_H__
+#define __MURPHY_DOMAIN_CONTROL_CLIENT_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mainloop.h>
+#include <murphy-db/mqi.h>
+#include <murphy/core/domain-types.h>
+
+MRP_CDECL_BEGIN
+
+#define MRP_DEFAULT_DOMCTL_ADDRESS "unxs:@murphy-domctrl"
+
+
+/*
+ * a table owned by a domain controller
+ */
+
+typedef struct {
+ const char *table; /* table name */
+ const char *mql_columns; /* column definition scriptlet */
+ const char *mql_index; /* index column list */
+} mrp_domctl_table_t;
+
+#define MRP_DOMCTL_TABLE(_table, _columns, _index) \
+ { .table = _table, .mql_columns = _columns, .mql_index = _index }
+
+
+/*
+ * a table tracked by a domain controller
+ */
+
+typedef struct {
+ const char *table; /* table name */
+ const char *mql_columns; /* column list for select */
+ const char *mql_where; /* where clause for select */
+ int max_rows; /* max number of rows to select */
+} mrp_domctl_watch_t;
+
+#define MRP_DOMCTL_WATCH(_table, _columns, _where, _max_rows) { \
+ .table = _table , \
+ .mql_columns = _columns ? _columns : "", \
+ .mql_where = _where ? _where : "", \
+ .max_rows = _max_rows , \
+ }
+
+
+/*
+ * table data
+ */
+
+typedef struct {
+ int id; /* table id */
+ mqi_column_def_t *coldefs; /* column definitions */
+ int ncolumn; /* columns per row */
+ mrp_domctl_value_t **rows; /* row data */
+ int nrow; /* number of rows */
+} mrp_domctl_data_t;
+
+
+/** Opaque policy domain controller type. */
+typedef struct mrp_domctl_s mrp_domctl_t;
+
+/** Callback type for connection state notifications. */
+typedef void (*mrp_domctl_connect_cb_t)(mrp_domctl_t *dc, int connection,
+ int errcode, const char *errmsg,
+ void *user_data);
+
+/** Callback type for request status notifications. */
+typedef void (*mrp_domctl_status_cb_t)(mrp_domctl_t *dc, int errcode,
+ const char *errmsg, void *user_data);
+
+/** Callback type for data change notifications. */
+typedef void (*mrp_domctl_watch_cb_t)(mrp_domctl_t *dc,
+ mrp_domctl_data_t *tables, int ntable,
+ void *user_data);
+
+/** Callback type for return of/reply to a proxied method invocation. */
+typedef void (*mrp_domctl_return_cb_t)(mrp_domctl_t *dc, int error, int retval,
+ uint32_t narg, mrp_domctl_arg_t *args,
+ void *user_data);
+
+/** Callback type for a proxied method invocation. */
+typedef int (*mrp_domctl_invoke_cb_t)(mrp_domctl_t *dc, uint32_t narg,
+ mrp_domctl_arg_t *args,
+ uint32_t *nout, mrp_domctl_arg_t *outs,
+ void *user_data);
+
+/*
+ * proxied invocation errors
+ */
+
+typedef enum {
+ MRP_DOMCTL_OK = MRP_DOMAIN_OK,
+ MRP_DOMCTL_NOTFOUND = MRP_DOMAIN_NOTFOUND,
+ MRP_DOMCTL_NOMETHOD = MRP_DOMAIN_NOMETHOD,
+} mrp_domctl_error_t;
+
+/*
+ * a domain controller method definition
+ */
+
+typedef struct {
+ const char *name;
+ size_t max_out;
+ mrp_domctl_invoke_cb_t cb;
+ void *user_data;
+} mrp_domctl_method_def_t;
+
+
+/** Create a new policy domain controller. */
+mrp_domctl_t *mrp_domctl_create(const char *name, mrp_mainloop_t *ml,
+ mrp_domctl_table_t *tables, int ntable,
+ mrp_domctl_watch_t *watches, int nwatch,
+ mrp_domctl_connect_cb_t connect_cb,
+ mrp_domctl_watch_cb_t watch_cb,
+ void *user_data);
+
+/** Destroy the given policy domain controller. */
+void mrp_domctl_destroy(mrp_domctl_t *dc);
+
+/**
+ * Connect and register the given controller to the server. If timeout
+ * is non-negative, it will be used to automatically attempt re-connecting
+ * to the server this often (in seconds) whenever the connection goes down.
+ */
+int mrp_domctl_connect(mrp_domctl_t *dc, const char *address, int timeout);
+
+/** Close the connection to the server. */
+void mrp_domctl_disconnect(mrp_domctl_t *dc);
+
+/** Set the content of the given tables to the provided data. */
+int mrp_domctl_set_data(mrp_domctl_t *dc, mrp_domctl_data_t *tables, int ntable,
+ mrp_domctl_status_cb_t status_cb, void *user_data);
+
+/** Invoke a proxied method. */
+int mrp_domctl_invoke(mrp_domctl_t *dc, const char *method, int narg,
+ mrp_domctl_arg_t *args, mrp_domctl_return_cb_t return_cb,
+ void *user_data);
+
+/** Register a proxied method handler. */
+int mrp_domctl_register_methods(mrp_domctl_t *dc, mrp_domctl_method_def_t *defs,
+ size_t ndef);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_DOMAIN_CONTROL_CLIENT_H__ */
--- /dev/null
+/*
+ * debugging
+ */
+
+var DOMCTL_COMM = 0; /* message/communication */
+var DOMCTL_NOTIFY = 1; /* data notification */
+var DOMCTL_MISC = 2; /* other debug messages */
+
+var domctl_debug_names = [ 'COMM', 'NOTIFY', 'MISC'];
+var domctl_debug_mask = 0x0;
+
+
+function domctl_debug_index_of(name) {
+ for (var idx in domctl_debug_names) {
+ if (domctl_debug_names[idx] == name)
+ return idx;
+ }
+
+ return -1;
+}
+
+
+function domctl_debug_mask_of(name) {
+ var idx = domctl_debug_index_of(name);
+
+ if (idx >= 0)
+ return (1 << domctl_debug_index_of(name));
+ else
+ return 0;
+}
+
+
+function domctl_debug_set(flags, enable) {
+ var mask = 0;
+ var flag;
+
+ if (typeof flags == typeof "" || typeof flags == typeof 1)
+ flags = [ flags ];
+
+ for (var idx in flags) {
+ flag = flags[idx];
+
+ if (typeof flag == typeof "")
+ mask |= domctl_debug_mask_of(flag);
+ else
+ mask |= (1 << flag);
+ }
+
+ if (enable)
+ domctl_debug_mask |= mask;
+ else
+ domctl_debug_mask &= ~mask;
+}
+
+
+function domctl_debug_enable (flags) {
+ domctl_debug_set(flags, true);
+}
+
+
+function domctl_debug_disable(flags) {
+ domctl_debug_set(flags, false);
+}
+
+
+function domctl_debug() {
+ var flag, msg, i;
+
+ if (arguments.length >= 2) {
+ flag = arguments[0];
+ mask = (1 << flag);
+
+ if (!(mask & domctl_debug_mask))
+ return;
+
+ flag = domctl_debug_names[flag];
+
+ for (i = 1, msg = ""; i < arguments.length; i++) {
+ msg += arguments[i];
+ }
+
+ console.log("D: [" + flag + "] " + msg);
+ }
+ else {
+ if (arguments.length == 1)
+ console.log("D: [ALL] " + arguments[0]);
+ }
+}
+
+
+/*
+ * custom errors
+ */
+
+function DomainControllerError(message) {
+ this.name = "Domain Control Error";
+ this.message = message;
+}
+
+
+/*
+ * pending requests
+ */
+
+/** Contruct a new pending request for the given request and controller. */
+function DomainControllerPendingRequest (controller, req) {
+ this.controller = controller;
+ this.req = req;
+ this.reqno = req.seq;
+}
+
+
+/** Deliver pending request reply notification. */
+DomainControllerPendingRequest.prototype.notify = function (message) {
+ var event;
+
+ switch (message.type) {
+ case 'ack':
+ if (this.onsuccess) {
+ event = { type: 'ack', seq: message.seq };
+ this.onsuccess(event);
+ }
+ break;
+
+ case 'nak':
+ if (this.onerror) {
+ event = { type: 'nak', seq: message.seq };
+ event.error = message.error ? message.error : -1;
+ event.errmsg = message.errmsg ? message.errmsg : "<unknown error>";
+ this.onerror(message);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*
+ * DomainController
+ */
+
+/** Reset controller state to defaults. */
+DomainController.prototype.reset = function () {
+ this.connected = false;
+ this.server = null;
+ this.sck = null;
+ this.reqno = 1;
+ this.reqq = [];
+ this.sets = [];
+
+ this.ondisconnect = null;
+ this.onfailed = null;
+ this.onevent = null;
+}
+
+
+/** Ensure we have a connection, throw an error otherwise. */
+DomainController.prototype.check_connection = function () {
+ if (!this.connected)
+ throw new DomainControllerError("not connected");
+}
+
+
+/** Event handler for connection establishment. */
+DomainController.prototype.sckopen = function () {
+ var ctl = this.controller;
+
+ domctl_debug(DOMCTL_COMM, "connected to server " + ctl.server);
+
+ ctl.connected = true;
+ ctl.register();
+}
+
+
+/** Event handler for socket disconnection. */
+DomainController.prototype.sckclose = function () {
+ var ctl = this.controller;
+
+ domctl_debug(DOMCTL_COMM, "disconnected from server");
+
+ ctl.connected = false;
+
+ if (ctl.ondisconnect) /* notify listener if any */
+ ctl.ondisconnect();
+
+ ctl.reset();
+}
+
+
+/** Event handler for connection error. */
+DomainController.prototype.sckerror = function () {
+ var ctl = this.controller;
+
+ domctl_debug(DOMCTL_COMM, "connection error");
+
+ ctl.connected = false;
+
+ if (ctl.onerror) {
+ ctl.ondisconnect = null;
+ ctl.onerror();
+ }
+}
+
+
+
+/** Event handler for receiving messages. */
+DomainController.prototype.sckmessage = function (message) {
+ var ctl = this.controller;
+ var msg = JSON.parse(message.data);
+ var seq = msg.seq;
+ var pending;
+
+ domctl_debug(DOMCTL_COMM, "received message: " + message.data);
+
+ switch (msg.type) {
+ case 'notify':
+ ctl.notify(msg);
+ break;
+
+ case 'ack':
+ case 'nak':
+ pending = ctl.deq(msg.seq);
+
+ if (pending) {
+ pending.notify(msg);
+ }
+ break;
+ }
+}
+
+
+/** Deliver domain controller event notification. */
+DomainController.prototype.notify = function (message) {
+ var idx, t, w;
+ var event;
+
+ if (this.onevent) {
+ event = {
+ type: 'notify',
+ tables: {}
+ };
+
+ for (idx in message.tables) {
+ t = message.tables[idx];
+ w = this.watches[idx];
+
+ if (w) {
+ event.tables[w.table] = {
+ id: w.id,
+ rows: t.rows,
+ };
+ }
+ }
+
+ this.onevent(event);
+ }
+}
+
+
+/** Enqueue an outgoing request to the server. */
+DomainController.prototype.enq = function (req) {
+ var seq, pending;
+
+ seq = req.seq = this.reqno++;
+ pending = new DomainControllerPendingRequest(this, req);
+
+ this.reqq[seq] = pending;
+
+ return pending;
+}
+
+
+/** Dequeue a pending request for the given sequence number. */
+DomainController.prototype.deq = function (seq) {
+ var pending;
+
+ pending = this.reqq[seq];
+
+ if (pending)
+ delete this.reqq[seq];
+
+ return pending;
+}
+
+
+/** Send a request to the server returning a pending object if appropriate. */
+DomainController.prototype.send_request = function (req) {
+ var pending;
+
+ if (req.type != 'register')
+ pending = this.enq(req);
+ else {
+ req.seq = 0;
+ pending = null;
+ }
+
+ domctl_debug(DOMCTL_COMM, "sending message: " + JSON.stringify(req));
+
+ this.sck.send(JSON.stringify(req));
+
+ return pending;
+}
+
+
+/** Send register message to the server. */
+DomainController.prototype.register = function () {
+ var f, ntable, nwatch, req;
+
+ ntable = 0;
+ for (f in this.tables) {
+ this.tables[f].id = ntable++;
+ }
+
+ nwatch = 0;
+ for (f in this.watches) {
+ this.watches[f].id = nwatch++;
+ }
+
+ req = {
+ type: 'register',
+ name: this.name,
+ tables: this.tables,
+ ntable: ntable,
+ watches: this.watches,
+ nwatch: nwatch,
+ };
+
+ this.send_request(req);
+}
+
+
+/** Create a new domain controller object. */
+function DomainController(name, tables, watches) {
+ this.reset();
+
+ this.name = name;
+ this.tables = tables;
+ this.watches = watches;
+}
+
+
+/** Determine a WebSocket URI based on an HTTP URI. */
+DomainController.prototype.socketUri = function (http_uri) {
+ var proto, colon, rest;
+
+ colon = http_uri.indexOf(':'); /* get first colon */
+ proto = http_uri.substring(0, colon); /* get protocol */
+ rest = http_uri.substring(colon + 3); /* get URI sans protocol:// */
+ addr = rest.split("/")[0]; /* strip URI path from address */
+
+ switch (proto) {
+ case "http": return "ws://" + addr;
+ case "https": return "wss://" + addr;
+ default: return null;
+ }
+}
+
+
+/** Initiate connection to the given server. */
+DomainController.prototype.connect = function (server) {
+ if (this.connected)
+ throw new DomainControllerError("already connected to " + this.server);
+ else {
+ domctl_debug(DOMCTL_COMM, "trying to connect to " + server);
+ this.server = server;
+
+ if (typeof MozWebSocket != "undefined")
+ this.sck = new MozWebSocket(this.server, "murphy");
+ else
+ this.sck = new WebSocket(this.server, "murphy");
+
+ this.sck.controller = this;
+ this.sck.onopen = this.sckopen;
+ this.sck.onclose = this.sckclose;
+ this.sck.onerror = this.sckerror;
+ this.sck.onmessage = this.sckmessage;
+ }
+}
+
+
+/** Disconnect from the server. */
+DomainController.prototype.disconnect = function () {
+ this.check_connection();
+
+ domctl_debug(DOMCTL_COMM, "disconnecting from " + this.server);
+
+ this.sck.close();
+ delete this.sck;
+}
+
+
+/** Set data on server. */
+DomainController.prototype.set = function (table_data) {
+ var idx, id, name;
+ var table, ntbl, ntot, ncol, nrow;
+ var req;
+
+ req = { type: 'set', seq: 0, nchange: 0, ntotal: 0, tables: [] };
+
+ ntbl = ntot = 0;
+ for (idx in table_data) {
+ ntbl++;
+ data = table_data[idx];
+ table = data.table;
+ rows = data.rows;
+
+ ncol = rows[0].length;
+ nrow = rows.length;
+ ntot += ncol * nrow;
+
+ id = -1;
+ for (tbl in this.tables) {
+ if (this.tables[tbl].table == table)
+ id = this.tables[tbl].id;
+ }
+ if (id < 0)
+ throw new DomainControllerError("unknown table " + table);
+
+ name = this.tables[id].table;
+
+ req.tables[idx] = { id: id, nrow: nrow, ncol: ncol, rows: rows };
+ }
+
+ req.nchange = ntbl;
+ req.ntotal = ntot;
+
+ return this.send_request(req);
+}
--- /dev/null
+<html lang="en">
+<head><title>Domain Controller Webruntime Test</title>
+<script src="domain-control-api.js"></script>
+<script>
+
+var ctx;
+
+
+function controllerDisconnected () {
+ setStatus("disconnected");
+ setStatus("unknown", 'audio_status');
+ setStatus("unknown", 'video_status');
+ setStatus("unknown", 'data_status');
+}
+
+
+function controllerFailed () {
+ console.log("connection failed");
+}
+
+
+function controllerEvent (event) {
+ var tidx, ridx;
+ var table, row;
+
+ setStatus('connected');
+ enableButton('disconnect');
+ enableButton('update');
+ console.log("received '" + event.type + "' event");
+
+ for (name in event.tables) {
+ table = event.tables[name];
+ rows = table.rows;
+ id = table.id;
+
+ status = "";
+ t = "";
+ for (ridx in rows) {
+ row = rows[ridx];
+ zoneid = row[0];
+ zone = row[1];
+ appcls = row[2];
+ pid = row[4];
+
+ status += t + zone + " zone: " + appcls;
+ if (pid)
+ status += " (pid " + pid + ")";
+ }
+
+ switch (name) {
+ case 'audio_playback_owner':
+ setStatus(status, 'audio_status');
+ break;
+ case 'video_playback_owner':
+ setStatus(status, 'video_status');
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+function setContent(id, text) {
+ var elem = document.getElementById(id);
+
+ if (elem)
+ elem.textContent = text;
+}
+
+
+function setStatus(status, which) {
+ if (which)
+ setContent(which, status)
+ else
+ setContent('general_status', status);
+}
+
+
+function enableButton(name) {
+ var btn = document.getElementById(name);
+
+ if (btn)
+ btn.disabled = false;
+}
+
+
+function disableButton(name) {
+ var btn = document.getElementById(name);
+
+ if (btn)
+ btn.disabled = true;
+}
+
+
+function initialize() {
+ disableButton("disconnect");
+ disableButton("update");
+ enableButton("connect");
+}
+
+
+function connect() {
+ var ctl;
+
+ setStatus("connecting...");
+
+ domctl_debug_enable([DOMCTL_COMM, DOMCTL_NOTIFY, DOMCTL_MISC]);
+
+ ctx = {};
+ ctl = new DomainController('wrt-test', [
+ {
+ "table": "wrt_test",
+ "columns": "count integer",
+ "index": ""
+ }
+ ],
+ [ {
+ "table": "audio_playback_owner",
+ "columns": "*",
+ "where": "",
+ "maxrows": 0
+ },
+ {
+ "table": "video_playback_owner",
+ "columns": "*",
+ "where": "",
+ "maxrows": 0
+ }
+ ]);
+
+ ctl.ondisconnect = controllerDisconnected;
+ ctl.onerror = controllerFailed;
+ ctl.onevent = controllerEvent;
+ ctl.user_data = ctx;
+
+ ctx.ctl = ctl;
+ ctx.cnt = 0;
+
+ console.log("server URI: " + ctl.socketUri(document.URL));
+ ctl.connect(ctl.socketUri(document.URL));
+}
+
+
+function disconnect() {
+ var ctl = ctx.ctl;
+
+ setStatus("disconnecting...");
+
+ ctl.disconnect();
+}
+
+
+function update_ack(e) {
+ console.log("Data updated successfully.");
+ setStatus(ctx.cnt - 1, "data_status");
+}
+
+
+function update_nak(e) {
+ var msg = e.errmsg ? e.errmsg : "unknown error";
+
+ console.log("Data update failed.");
+
+ setStatus("error (" + msg + ")", "data_status");
+}
+
+
+function update() {
+ var ctl = ctx.ctl;
+ var cnt = ctx.cnt++;
+ var pending;
+
+ try {
+ pending = ctl.set([{table: "wrt_test", rows: [[cnt]]}]);
+ pending.onsuccess = update_ack;
+ pending.onerror = update_nak;
+ } catch (e) {
+ console.log("Failed to update data on server (" + e.message + ")");
+ }
+}
+
+
+</script>
+
+</head>
+
+<body onload="initialize();">
+
+
+<table>
+ <tr>
+ <th align=left>Actions:</th>
+ <td align=left>
+ <input type=button id=connect value="Connect"
+ onclick="connect();">
+ <input type=button id=disconnect value="Disconnect"
+ onclick="disconnect();" disabled>
+ <input type=button id=update value="Update Data"
+ onclick="update();" disabled>
+
+ </td>
+ </tr>
+ <tr>
+ <th align=left>Status:</th>
+ <td align=left><div id=general_status>disconnected</div></td>
+ </tr>
+ <tr>
+ <th align=left>Audio:</th>
+ <td align=left><div id=audio_status>unknown</div></td>
+ </tr>
+ <tr>
+ <th align=left>Video:</th>
+ <td align=left><div id=video_status>unknown</div></td>
+ </tr>
+ <tr>
+ <th align=left>Data:</th>
+ <td align=left><div id=data_status>unknown</div></td>
+ </tr>
+</table>
+
+
+</body>
+</html>
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_TYPES_H__
+#define __MURPHY_DOMAIN_CONTROL_TYPES_H__
+
+#include <stdbool.h>
+
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/core/context.h>
+#include <murphy-db/mql.h>
+
+#include "client.h"
+
+typedef struct pep_proxy_s pep_proxy_t;
+typedef struct pep_table_s pep_table_t;
+typedef struct pep_watch_s pep_watch_t;
+typedef struct pdp_s pdp_t;
+typedef union msg_u msg_t;
+
+/*
+ * a domain controller (on the client side)
+ */
+
+struct mrp_domctl_s {
+ char *name; /* enforcment point name */
+ mrp_mainloop_t *ml; /* main loop */
+ mrp_sockaddr_t addr; /* server address */
+ socklen_t addrlen; /* address length */
+ mrp_timer_t *ctmr; /* connection timer */
+ int cival; /* connection attempt interval */
+ const char *ttype; /* transport type */
+ mrp_transport_t *t; /* transport towards murphy */
+ int connected; /* transport is up */
+ mrp_domctl_table_t *tables; /* owned tables */
+ int ntable; /* number of owned tables */
+ mrp_domctl_watch_t *watches; /* watched tables */
+ int nwatch; /* number of watched tables */
+ mrp_domctl_connect_cb_t connect_cb; /* connection state change callback */
+ mrp_domctl_watch_cb_t watch_cb; /* watched table change callback */
+ void *user_data; /* opqaue user data for callbacks */
+ int busy; /* non-zero if a callback is active */
+ int destroyed:1;/* non-zero if destroy pending */
+ uint32_t seqno; /* request sequence number */
+ mrp_list_hook_t pending; /* queue of outstanding requests */
+ mrp_list_hook_t methods; /* registered proxied methods */
+};
+
+
+/*
+ * a table associated with or tracked by an enforcement point
+ */
+
+struct pep_table_s {
+ char *name; /* table name */
+ char *mql_columns; /* column definition clause */
+ char *mql_index; /* index column list */
+ mrp_list_hook_t hook; /* to list of tables */
+ mqi_handle_t h; /* table handle */
+ mqi_column_def_t *columns; /* column definitions */
+ mqi_column_desc_t *coldesc; /* column descriptors */
+ int ncolumn; /* number of columns */
+ int idx_col; /* column index of index column */
+ mrp_list_hook_t watches; /* watches for this table */
+ bool changed; /* whether has unsynced changes */
+};
+
+
+/*
+ * a table watch
+ */
+
+struct pep_watch_s {
+ pep_table_t *table; /* table being watched */
+ char *mql_columns; /* column list to select */
+ char *mql_where; /* where clause for select */
+ int max_rows; /* max number of rows to select */
+ pep_proxy_t *proxy; /* enforcement point */
+ int id; /* table id within proxy */
+ mrp_list_hook_t tbl_hook; /* hook to table watch list */
+ mrp_list_hook_t pep_hook; /* hook to proxy watch list */
+ bool notify; /* whether to notify this watch */
+};
+
+
+/*
+ * a policy enforcement point (on the server side)
+ */
+
+typedef struct {
+ int (*send_msg)(pep_proxy_t *proxy, msg_t *msg);
+ void (*unref)(void *data);
+ int (*create_notify)(pep_proxy_t *proxy);
+ int (*update_notify)(pep_proxy_t *proxy, int tblid, mql_result_t *r);
+ int (*send_notify)(pep_proxy_t *proxy);
+ void (*free_notify)(pep_proxy_t *proxy);
+} proxy_ops_t;
+
+
+
+struct pep_proxy_s {
+ char *name; /* enforcement point name */
+ pdp_t *pdp; /* domain controller context */
+ mrp_transport_t *t; /* associated transport */
+ mrp_list_hook_t hook; /* to list of all enforcement points */
+ pep_table_t *tables; /* tables owned by this */
+ int ntable; /* number of tables */
+ mrp_list_hook_t watches; /* tables watched by this */
+ proxy_ops_t *ops; /* transport/messaging operations */
+ uint32_t seqno; /* request sequence number */
+ mrp_list_hook_t pending; /* pending method invocations */
+ void *notify_msg; /* notification being built */
+ int notify_ntable; /* number of changed tables */
+ int notify_ncolumn; /* total columns in notification */
+ int notify_fail : 1; /* notification failure */
+ int notify : 1; /* whether has pending notifications */
+};
+
+
+/*
+ * policy domain controller context
+ */
+
+struct pdp_s {
+ mrp_context_t *ctx; /* murphy context */
+ const char *address; /* external transport address */
+ mrp_transport_t *extt; /* external transport */
+ mrp_transport_t *wrtt; /* WRT transport */
+ mrp_transport_t *intt; /* internal transport */
+ mrp_list_hook_t proxies; /* list of enforcement points */
+ mrp_list_hook_t tables; /* list of tables we track */
+ mrp_htbl_t *watched; /* tracked tables by name */
+ mrp_deferred_t *notify; /* deferred notification */
+ bool notify_scheduled; /* is notification scheduled? */
+ void *reh; /* resolver event handler */
+ int ractive; /* resolver active */
+ bool rblocked; /* resolver blocked update */
+};
+
+
+
+#endif /* __MURPHY_DOMAIN_CONTROL_TYPES_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/wsck-transport.h>
+
+#include <murphy/resolver/resolver.h>
+#include <murphy/core/domain.h>
+
+#include "proxy.h"
+#include "message.h"
+#include "table.h"
+#include "notify.h"
+#include "domain-control.h"
+
+static mrp_transport_t *create_transport(pdp_t *pdp, const char *address);
+static void destroy_transport(mrp_transport_t *t);
+
+static int invoke_handler(void *handler_data, const char *id,
+ const char *method, int narg,
+ mrp_domctl_arg_t *args,
+ mrp_domain_return_cb_t return_cb,
+ void *user_data);
+
+static uint32_t RESEVT_START, RESEVT_DONE, RESEVT_FAIL;
+
+
+static void resolver_event_cb(mrp_event_watch_t *w, uint32_t id, int format,
+ void *data, void *user_data)
+{
+ pdp_t *pdp = (pdp_t *)user_data;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(format);
+ MRP_UNUSED(data);
+
+ if (id == RESEVT_START)
+ pdp->ractive++;
+ else if (id == RESEVT_DONE || id == RESEVT_FAIL)
+ pdp->ractive--;
+
+ mrp_debug("resolver is %s active", pdp->ractive == 1 ? "now" :
+ pdp->ractive > 1 ? "still" : "no longer");
+
+ if (pdp->ractive == 0) {
+ schedule_notification(pdp);
+ pdp->rblocked = false;
+ }
+}
+
+
+static int add_resolver_trigger(pdp_t *pdp)
+{
+ mrp_context_t *ctx = pdp->ctx;
+ mrp_mainloop_t *ml = ctx->ml;
+ mrp_event_bus_t *bus = mrp_event_bus_get(ml, MRP_RESOLVER_BUS);
+ mrp_event_mask_t mask;
+
+ if (bus == NULL)
+ return FALSE;
+
+ RESEVT_START = mrp_event_id(MRP_RESOLVER_EVENT_STARTED);
+ RESEVT_DONE = mrp_event_id(MRP_RESOLVER_EVENT_DONE);
+ RESEVT_FAIL = mrp_event_id(MRP_RESOLVER_EVENT_FAILED);
+
+ mrp_mask_init(&mask);
+ if (!mrp_mask_set(&mask, RESEVT_START) ||
+ !mrp_mask_set(&mask, RESEVT_DONE ) ||
+ !mrp_mask_set(&mask, RESEVT_FAIL ))
+ return FALSE;
+
+ pdp->reh = mrp_event_add_watch_mask(bus, &mask, resolver_event_cb, pdp);
+
+ return pdp->reh != NULL;
+}
+
+
+static void del_resolver_trigger(pdp_t *pdp)
+{
+ mrp_event_del_watch(pdp->reh);
+ pdp->reh = NULL;
+}
+
+
+pdp_t *create_domain_control(mrp_context_t *ctx,
+ const char *extaddr, const char *intaddr,
+ const char *wrtaddr, const char *httpdir)
+{
+ pdp_t *pdp;
+
+ pdp = mrp_allocz(sizeof(*pdp));
+
+ if (pdp != NULL) {
+ pdp->ctx = ctx;
+ pdp->address = extaddr;
+
+ if (init_proxies(pdp) && init_tables(pdp)) {
+
+ if (!add_resolver_trigger(pdp))
+ goto fail;
+
+ if (extaddr && *extaddr)
+ pdp->extt = create_transport(pdp, extaddr);
+
+ if (intaddr && *intaddr)
+ pdp->intt = create_transport(pdp, intaddr);
+
+ if (wrtaddr && *wrtaddr) {
+ pdp->wrtt = create_transport(pdp, wrtaddr);
+
+ if (pdp->wrtt != NULL) {
+ const char *sm_opt = MRP_WSCK_OPT_SENDMODE;
+ const char *sm_val = MRP_WSCK_SENDMODE_TEXT;
+ const char *hd_opt = MRP_WSCK_OPT_HTTPDIR;
+ const char *hd_val = httpdir;
+
+ mrp_transport_setopt(pdp->wrtt, sm_opt, sm_val);
+ mrp_transport_setopt(pdp->wrtt, hd_opt, hd_val);
+ }
+ }
+
+
+ if ((!extaddr || !*extaddr || pdp->extt != NULL) &&
+ (!intaddr || !*intaddr || pdp->intt != NULL) &&
+ (!wrtaddr || !*wrtaddr || pdp->wrtt != NULL)) {
+ mrp_set_domain_invoke_handler(ctx, invoke_handler, pdp);
+ return pdp;
+ }
+ }
+
+ fail:
+ destroy_domain_control(pdp);
+ }
+
+ return NULL;
+}
+
+
+void destroy_domain_control(pdp_t *pdp)
+{
+ if (pdp != NULL) {
+ del_resolver_trigger(pdp);
+ destroy_proxies(pdp);
+ destroy_tables(pdp);
+ destroy_transport(pdp->extt);
+ destroy_transport(pdp->intt);
+ destroy_transport(pdp->wrtt);
+
+ mrp_free(pdp);
+ }
+}
+
+
+static void notify_cb(mrp_deferred_t *d, void *user_data)
+{
+ pdp_t *pdp = (pdp_t *)user_data;
+
+ mrp_disable_deferred(d);
+ pdp->notify_scheduled = false;
+ notify_table_changes(pdp);
+}
+
+
+void schedule_notification(pdp_t *pdp)
+{
+
+ if (pdp->notify == NULL)
+ pdp->notify = mrp_add_deferred(pdp->ctx->ml, notify_cb, pdp);
+
+ if (!pdp->notify_scheduled) {
+ mrp_debug("scheduling client notification");
+ mrp_enable_deferred(pdp->notify);
+ pdp->notify_scheduled = true;
+ }
+}
+
+
+static int msg_send_message(pep_proxy_t *proxy, msg_t *msg)
+{
+ mrp_msg_t *tmsg;
+
+ tmsg = msg_encode_message(msg);
+
+ if (tmsg != NULL) {
+ mrp_transport_send(proxy->t, tmsg);
+ mrp_msg_unref(tmsg);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int msg_send_ack(pep_proxy_t *proxy, uint32_t seq)
+{
+ ack_msg_t ack;
+
+ mrp_clear(&ack);
+ ack.type = MSG_TYPE_ACK;
+ ack.seq = seq;
+
+ return proxy->ops->send_msg(proxy, (msg_t *)&ack);
+}
+
+
+static int msg_send_nak(pep_proxy_t *proxy, uint32_t seq,
+ int32_t error, const char *msg)
+{
+ nak_msg_t nak;
+
+ mrp_clear(&nak);
+ nak.type = MSG_TYPE_NAK;
+ nak.seq = seq;
+ nak.error = error;
+ nak.msg = msg;
+
+ return proxy->ops->send_msg(proxy, (msg_t *)&nak);
+}
+
+
+
+static void process_register(pep_proxy_t *proxy, register_msg_t *reg)
+{
+ int error;
+ const char *errmsg;
+
+ if (register_proxy(proxy, reg->name, reg->tables, reg->ntable,
+ reg->watches, reg->nwatch, &error, &errmsg)) {
+ msg_send_ack(proxy, reg->seq);
+ schedule_notification(proxy->pdp);
+ }
+ else
+ msg_send_nak(proxy, reg->seq, error, errmsg);
+}
+
+
+static void process_unregister(pep_proxy_t *proxy, unregister_msg_t *unreg)
+{
+ msg_send_ack(proxy, unreg->seq);
+}
+
+
+static void process_set(pep_proxy_t *proxy, set_msg_t *set)
+{
+ int error;
+ const char *errmsg;
+
+ if (set_proxy_tables(proxy, set->tables, set->ntable, &error, &errmsg)) {
+ msg_send_ack(proxy, set->seq);
+ }
+ else
+ msg_send_nak(proxy, set->seq, error, errmsg);
+}
+
+
+static void process_invoke(pep_proxy_t *proxy, invoke_msg_t *invoke)
+{
+ mrp_context_t *ctx = proxy->pdp->ctx;
+ mrp_domain_invoke_cb_t cb;
+ void *user_data;
+ int max_out;
+ mrp_domctl_arg_t *args;
+ int narg;
+ return_msg_t ret;
+ mrp_msg_t *msg;
+ int i;
+
+ mrp_clear(&ret);
+
+ ret.type = MSG_TYPE_RETURN;
+ ret.seq = invoke->seq;
+ args = NULL;
+ narg = 0;
+
+ if (!mrp_lookup_domain_method(ctx, invoke->name, &cb, &max_out,&user_data)) {
+ ret.error = MRP_DOMCTL_NOTFOUND;
+ }
+ else {
+ ret.error = MRP_DOMCTL_OK;
+
+ narg = ret.narg = max_out;
+
+ if (narg > 0) {
+ args = ret.args = alloca(narg * sizeof(args[0]));
+ memset(args, 0, narg * sizeof(args[0]));
+ }
+
+ ret.retval = cb(invoke->narg, invoke->args,
+ &ret.narg, ret.args, user_data);
+ }
+
+ msg = msg_encode_message((msg_t *)&ret);
+
+ if (msg != NULL) {
+ mrp_transport_send(proxy->t, msg);
+ mrp_msg_unref(msg);
+ }
+
+ narg = ret.narg;
+ for (i = 0; i < narg; i++) {
+ if (args[i].type == MRP_DOMCTL_STRING)
+ mrp_free((char *)args[i].str);
+ else if (MRP_DOMCTL_IS_ARRAY(args[i].type)) {
+ uint32_t j;
+
+ for (j = 0; j < args[i].size; j++)
+ if (MRP_DOMCTL_ARRAY_TYPE(args[i].type) == MRP_DOMCTL_STRING)
+ mrp_free(((char **)args[i].arr)[j]);
+
+ mrp_free(args[i].arr);
+ }
+ }
+}
+
+
+static void process_return(pep_proxy_t *proxy, return_msg_t *ret)
+{
+ uint32_t id = ret->seq;
+ mrp_domain_return_cb_t cb;
+ void *user_data;
+
+ if (!proxy_dequeue_pending(proxy, id, &cb, &user_data))
+ return;
+
+ cb(ret->error, ret->retval, ret->narg, ret->args, user_data);
+}
+
+
+static void process_message(pep_proxy_t *proxy, msg_t *msg)
+{
+ char *name = proxy->name ? proxy->name : "<unknown>";
+
+ switch (msg->any.type) {
+ case MSG_TYPE_REGISTER:
+ process_register(proxy, &msg->reg);
+ break;
+ case MSG_TYPE_UNREGISTER:
+ process_unregister(proxy, &msg->unreg);
+ break;
+ case MSG_TYPE_SET:
+ process_set(proxy, &msg->set);
+ break;
+ case MSG_TYPE_INVOKE:
+ process_invoke(proxy, &msg->invoke);
+ break;
+ case MSG_TYPE_RETURN:
+ process_return(proxy, &msg->ret);
+ break;
+ default:
+ mrp_log_error("Unexpected message 0x%x from client %s.",
+ msg->any.type, name);
+ break;
+ }
+}
+
+
+static int invoke_handler(void *handler_data, const char *domain,
+ const char *method, int narg,
+ mrp_domctl_arg_t *args,
+ mrp_domain_return_cb_t return_cb,
+ void *user_data)
+{
+ pdp_t *pdp = (pdp_t *)handler_data;
+ pep_proxy_t *proxy = find_proxy(pdp, domain);
+ uint32_t id;
+ invoke_msg_t invoke;
+
+ if (proxy == NULL)
+ return FALSE;
+
+ id = proxy_queue_pending(proxy, return_cb, user_data);
+
+ if (!id)
+ return FALSE;
+
+ mrp_clear(&invoke);
+
+ invoke.type = MSG_TYPE_INVOKE;
+ invoke.seq = id;
+ invoke.name = method;
+ invoke.noret = (return_cb == NULL);
+ invoke.narg = narg;
+ invoke.args = args;
+
+ return msg_send_message(proxy, (msg_t *)&invoke);
+}
+
+
+static int msg_op_send_msg(pep_proxy_t *proxy, msg_t *msg)
+{
+ return msg_send_message(proxy, msg);
+}
+
+
+static void msg_op_unref_msg(void *msg)
+{
+ mrp_msg_unref((mrp_msg_t *)msg);
+}
+
+
+static int msg_op_create_notify(pep_proxy_t *proxy)
+{
+ if (proxy->notify_msg == NULL)
+ proxy->notify_msg = msg_create_notify();
+
+ if (proxy->notify_msg != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int msg_op_update_notify(pep_proxy_t *proxy, int tblid, mql_result_t *r)
+{
+ int n;
+
+ n = msg_update_notify((mrp_msg_t *)proxy->notify_msg, tblid, r);
+
+ if (n >= 0) {
+ proxy->notify_ncolumn += n;
+ proxy->notify_ntable++;
+ }
+
+ return n;
+}
+
+
+static int msg_op_send_notify(pep_proxy_t *proxy)
+{
+ mrp_msg_t *msg = proxy->notify_msg;
+ uint16_t nchange = proxy->notify_ntable;
+ uint16_t ntotal = proxy->notify_ncolumn;
+
+ mrp_msg_set(msg, MSG_UINT16(NCHANGE, nchange));
+ mrp_msg_set(msg, MSG_UINT16(NTOTAL , ntotal));
+
+ return mrp_transport_send(proxy->t, msg);
+}
+
+
+static void msg_op_free_notify(pep_proxy_t *proxy)
+{
+ mrp_msg_unref((mrp_msg_t *)proxy->notify_msg);
+ proxy->notify_msg = NULL;
+}
+
+
+static void msg_connect_cb(mrp_transport_t *t, void *user_data)
+{
+ static proxy_ops_t ops = {
+ .send_msg = msg_op_send_msg,
+ .unref = msg_op_unref_msg,
+ .create_notify = msg_op_create_notify,
+ .update_notify = msg_op_update_notify,
+ .send_notify = msg_op_send_notify,
+ .free_notify = msg_op_free_notify,
+ };
+
+ pdp_t *pdp = (pdp_t *)user_data;
+ pep_proxy_t *proxy;
+ int flags;
+
+ proxy = create_proxy(pdp);
+
+ if (proxy != NULL) {
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ proxy->t = mrp_transport_accept(t, proxy, flags);
+
+ if (proxy->t != NULL) {
+ proxy->ops = &ops;
+ mrp_log_info("Accepted new client connection.");
+ }
+ else {
+ mrp_log_error("Failed to accept new client connection.");
+ destroy_proxy(proxy);
+ }
+ }
+}
+
+
+static void msg_closed_cb(mrp_transport_t *t, int error, void *user_data)
+{
+ pep_proxy_t *proxy = (pep_proxy_t *)user_data;
+ char *name = proxy && proxy->name ? proxy->name : "<unknown>";
+
+ MRP_UNUSED(t);
+
+ if (error)
+ mrp_log_error("Transport to client %s closed (%d: %s).",
+ name, error, strerror(error));
+ else
+ mrp_log_info("Transport to client %s closed.", name);
+
+ mrp_log_info("Destroying client %s.", name);
+ destroy_proxy(proxy);
+}
+
+
+static void msg_recv_cb(mrp_transport_t *t, mrp_msg_t *tmsg, void *user_data)
+{
+ pep_proxy_t *proxy = (pep_proxy_t *)user_data;
+ char *name;
+ msg_t *msg;
+ uint32_t seqno;
+
+ MRP_UNUSED(t);
+
+ /*
+ mrp_log_info("Message from client %p:", proxy);
+ mrp_msg_dump(msg, stdout);
+ */
+
+ if (proxy != NULL) {
+ name = proxy->name ? proxy->name : "<unknown>";
+ msg = msg_decode_message(tmsg);
+
+ if (msg != NULL) {
+ process_message(proxy, msg);
+ msg_free_message(msg);
+ }
+ else {
+ if (!mrp_msg_get(tmsg, MSG_UINT32(MSGSEQ, &seqno), MSG_END))
+ seqno = 0;
+ mrp_log_error("Failed to decode message from %s.", name);
+ msg_send_nak(proxy, seqno, 1, "failed to decode message");
+ }
+ }
+}
+
+
+static int wrt_send_message(pep_proxy_t *proxy, msg_t *msg)
+{
+ mrp_json_t *tmsg;
+
+ tmsg = json_encode_message(msg);
+
+ if (tmsg != NULL) {
+ mrp_transport_sendcustom(proxy->t, tmsg);
+ mrp_json_unref(tmsg);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int wrt_op_send_msg(pep_proxy_t *proxy, msg_t *msg)
+{
+ return wrt_send_message(proxy, msg);
+}
+
+
+static void wrt_op_unref_msg(void *msg)
+{
+ mrp_json_unref((mrp_json_t *)msg);
+}
+
+
+static int wrt_op_create_notify(pep_proxy_t *proxy)
+{
+ if (proxy->notify_msg == NULL)
+ proxy->notify_msg = json_create_notify();
+
+ if (proxy->notify_msg != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+static int wrt_op_update_notify(pep_proxy_t *proxy, int tblid, mql_result_t *r)
+{
+ int n;
+
+ n = json_update_notify((mrp_json_t *)proxy->notify_msg, tblid, r);
+
+ if (n >= 0) {
+ proxy->notify_ncolumn += n;
+ proxy->notify_ntable++;
+ }
+
+ return n;
+}
+
+
+static int wrt_op_send_notify(pep_proxy_t *proxy)
+{
+ mrp_json_t *msg = proxy->notify_msg;
+ int nchange = proxy->notify_ntable;
+ int ntotal = proxy->notify_ncolumn;
+
+ if (mrp_json_add_integer(msg, "nchange", nchange) &&
+ mrp_json_add_integer(msg, "ntotal" , ntotal))
+ return mrp_transport_sendcustom(proxy->t, msg);
+ else
+ return FALSE;
+}
+
+
+static void wrt_op_free_notify(pep_proxy_t *proxy)
+{
+ mrp_json_unref((mrp_json_t *)proxy->notify_msg);
+ proxy->notify_msg = NULL;
+}
+
+
+static void wrt_connect_cb(mrp_transport_t *t, void *user_data)
+{
+ static proxy_ops_t ops = {
+ .send_msg = wrt_op_send_msg,
+ .unref = wrt_op_unref_msg,
+ .create_notify = wrt_op_create_notify,
+ .update_notify = wrt_op_update_notify,
+ .send_notify = wrt_op_send_notify,
+ .free_notify = wrt_op_free_notify,
+ };
+
+ pdp_t *pdp = (pdp_t *)user_data;
+ pep_proxy_t *proxy;
+ int flags;
+
+ proxy = create_proxy(pdp);
+
+ if (proxy != NULL) {
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ proxy->t = mrp_transport_accept(t, proxy, flags);
+
+ if (proxy->t != NULL) {
+ proxy->ops = &ops;
+ mrp_log_info("Accepted new client connection.");
+ }
+ else {
+ mrp_log_error("Failed to accept new client connection.");
+ destroy_proxy(proxy);
+ }
+ }
+}
+
+
+static void wrt_closed_cb(mrp_transport_t *t, int error, void *user_data)
+{
+ pep_proxy_t *proxy = (pep_proxy_t *)user_data;
+ char *name = proxy && proxy->name ? proxy->name : "<unknown>";
+
+ MRP_UNUSED(t);
+
+ if (error)
+ mrp_log_error("Transport to client %s closed (%d: %s).",
+ name, error, strerror(error));
+ else
+ mrp_log_info("Transport to client %s closed.", name);
+
+ mrp_log_info("Destroying client %s.", name);
+ destroy_proxy(proxy);
+}
+
+
+static void wrt_recv_cb(mrp_transport_t *t, void *data, void *user_data)
+{
+ pep_proxy_t *proxy = (pep_proxy_t *)user_data;
+ char *name;
+ msg_t *msg;
+ int seqno;
+
+ MRP_UNUSED(t);
+
+ /*
+ mrp_log_info("Message from WRT client %p:", proxy);
+ */
+
+ if (proxy != NULL) {
+ name = proxy->name ? proxy->name : "<unknown>";
+ msg = json_decode_message(data);
+
+ if (msg != NULL) {
+ process_message(proxy, msg);
+ msg_free_message(msg);
+ }
+ else {
+ if (!mrp_json_get_integer(data, "seq", &seqno))
+ seqno = 0;
+ mrp_log_error("Failed to decode message from %s.", name);
+ msg_send_nak(proxy, seqno, 1, "failed to decode message");
+ }
+ }
+}
+
+
+
+
+static mrp_transport_t *create_transport(pdp_t *pdp, const char *address)
+{
+ static mrp_transport_evt_t msg_evt, wrt_evt;
+
+ mrp_transport_evt_t *e;
+ mrp_transport_t *t;
+ mrp_sockaddr_t addr;
+ socklen_t alen;
+ int flags;
+ const char *type;
+
+ t = NULL;
+ alen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &type);
+
+ if (alen <= 0) {
+ mrp_log_error("Failed to resolve transport address '%s'.", address);
+ return NULL;
+ }
+
+ flags = MRP_TRANSPORT_REUSEADDR;
+
+ if (strncmp(address, "wsck", 4) != 0) {
+ e = &msg_evt;
+
+ e->connection = msg_connect_cb;
+ e->closed = msg_closed_cb;
+ e->recvmsg = msg_recv_cb;
+ e->recvmsgfrom = NULL;
+ }
+ else {
+ e = &wrt_evt;
+
+ e->connection = wrt_connect_cb;
+ e->closed = wrt_closed_cb;
+ e->recvcustom = wrt_recv_cb;
+ e->recvcustomfrom = NULL;
+
+ flags |= MRP_TRANSPORT_MODE_CUSTOM;
+ }
+
+ t = mrp_transport_create(pdp->ctx->ml, type, e, pdp, flags);
+
+ if (t != NULL) {
+ if (mrp_transport_bind(t, &addr, alen) && mrp_transport_listen(t, 4))
+ return t;
+ else {
+ mrp_log_error("Failed to bind to transport address '%s'.", address);
+ mrp_transport_destroy(t);
+ }
+ }
+ else
+ mrp_log_error("Failed to create transport '%s'.", address);
+
+ return NULL;
+}
+
+
+static void destroy_transport(mrp_transport_t *t)
+{
+ mrp_transport_destroy(t);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_H__
+#define __MURPHY_DOMAIN_CONTROL_H__
+
+#include "domain-control-types.h"
+
+pdp_t *create_domain_control(mrp_context_t *ctx, const char *ext_addr,
+ const char *int_addr, const char *wrt_addr,
+ const char *httpdir);
+void destroy_domain_control(pdp_t *pdp);
+
+void schedule_notification(pdp_t *pdp);
+
+#endif /* __MURPHY_DOMAIN_CONTROL_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/mm.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/json.h>
+
+#include <murphy-db/mql.h>
+
+#include "message.h"
+
+
+static void unref_wire(msg_t *msg)
+{
+ if (msg->any.wire && msg->any.unref_wire) {
+ msg->any.unref_wire(msg->any.wire);
+ msg->any.wire = NULL;
+ }
+}
+
+
+static void msg_unref_wire(void *wire)
+{
+ mrp_msg_unref((mrp_msg_t *)wire);
+}
+
+
+static void msg_free_register(msg_t *msg)
+{
+ register_msg_t *reg = (register_msg_t *)msg;
+
+ if (reg != NULL) {
+ mrp_free(reg->tables);
+ mrp_free(reg->watches);
+ unref_wire(msg);
+
+ mrp_free(reg);
+ }
+}
+
+
+mrp_msg_t *msg_encode_register(register_msg_t *reg)
+{
+ mrp_msg_t *msg;
+ mrp_domctl_table_t *t;
+ mrp_domctl_watch_t *w;
+ int i;
+
+ msg = mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_REGISTER),
+ MSG_UINT32(MSGSEQ , 0),
+ MSG_STRING(NAME , reg->name),
+ MSG_UINT16(NTABLE , reg->ntable),
+ MSG_UINT16(NWATCH , reg->nwatch),
+ MSG_END);
+
+ for (i = 0, t = reg->tables; i < reg->ntable; i++, t++) {
+ mrp_msg_append(msg, MSG_STRING(TBLNAME, t->table));
+ mrp_msg_append(msg, MSG_STRING(COLUMNS, t->mql_columns));
+ mrp_msg_append(msg, MSG_STRING(INDEX , t->mql_index));
+ }
+
+ for (i = 0, w = reg->watches; i < reg->nwatch; i++, w++) {
+ mrp_msg_append(msg, MSG_STRING(TBLNAME, w->table));
+ mrp_msg_append(msg, MSG_STRING(COLUMNS, w->mql_columns));
+ mrp_msg_append(msg, MSG_STRING(WHERE , w->mql_where));
+ mrp_msg_append(msg, MSG_UINT16(MAXROWS, w->max_rows));
+ }
+
+ return msg;
+}
+
+
+msg_t *msg_decode_register(mrp_msg_t *msg)
+{
+ register_msg_t *reg;
+ void *it;
+ mrp_domctl_table_t *t;
+ mrp_domctl_watch_t *w;
+ char *name, *table, *columns, *index, *where;
+ uint16_t ntable, nwatch, max_rows;
+ uint32_t seqno;
+ int i;
+
+ it = NULL;
+
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ, &seqno),
+ MSG_STRING(NAME , &name),
+ MSG_UINT16(NTABLE, &ntable),
+ MSG_UINT16(NWATCH, &nwatch),
+ MSG_END))
+ return NULL;
+
+ reg = mrp_allocz(sizeof(*reg));
+
+ if (reg == NULL)
+ return NULL;
+
+ reg->type = MSG_TYPE_REGISTER;
+ reg->seq = seqno;
+ reg->name = name;
+ reg->tables = mrp_allocz(sizeof(*reg->tables) * ntable);
+ reg->watches = mrp_allocz(sizeof(*reg->watches) * nwatch);
+
+ if ((reg->tables == NULL && ntable) || (reg->watches == NULL && nwatch))
+ goto fail;
+
+ for (i = 0, t = reg->tables; i < ntable; i++, t++) {
+ if (mrp_msg_iterate_get(msg, &it,
+ MSG_STRING(TBLNAME, &table),
+ MSG_STRING(COLUMNS, &columns),
+ MSG_STRING(INDEX , &index),
+ MSG_END)) {
+ t->table = table;
+ t->mql_columns = columns;
+ t->mql_index = index;
+ }
+ else
+ goto fail;
+ }
+
+ reg->ntable = ntable;
+
+ for (i = 0, w = reg->watches; i < nwatch; i++, w++) {
+ if (mrp_msg_iterate_get(msg, &it,
+ MSG_STRING(TBLNAME, &table),
+ MSG_STRING(COLUMNS, &columns),
+ MSG_STRING(WHERE , &where),
+ MSG_UINT16(MAXROWS, &max_rows),
+ MSG_END)) {
+ w->table = table;
+ w->mql_columns = columns;
+ w->mql_where = where;
+ w->max_rows = max_rows;
+ }
+ else
+ goto fail;
+ }
+
+ reg->nwatch = nwatch;
+
+ reg->wire = mrp_msg_ref(msg);
+ reg->unref_wire = msg_unref_wire;
+
+ return (msg_t *)reg;
+
+ fail:
+ msg_free_register((msg_t *)reg);
+
+ return NULL;
+}
+
+
+void msg_free_unregister(msg_t *msg)
+{
+ unregister_msg_t *ureg = (unregister_msg_t *)msg;
+
+ if (ureg != NULL) {
+ unref_wire(msg);
+ mrp_free(ureg);
+ }
+}
+
+
+mrp_msg_t *msg_encode_unregister(unregister_msg_t *ureg)
+{
+ return mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_UNREGISTER),
+ MSG_UINT32(MSGSEQ , ureg->seq),
+ MSG_END);
+}
+
+
+msg_t *msg_decode_unregister(mrp_msg_t *msg)
+{
+ unregister_msg_t *ureg;
+ void *it;
+ uint32_t seqno;
+
+ ureg = mrp_allocz(sizeof(*ureg));
+
+ if (ureg != NULL) {
+ it = NULL;
+
+ if (mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ, &seqno),
+ MSG_END)) {
+ ureg->type = MSG_TYPE_UNREGISTER;
+ ureg->seq = seqno;
+
+ return (msg_t *)ureg;
+ }
+
+ msg_free_unregister((msg_t *)ureg);
+ }
+
+ return NULL;
+}
+
+
+void msg_free_ack(msg_t *msg)
+{
+ ack_msg_t *ack = (ack_msg_t *)msg;
+
+ if (ack != NULL) {
+ unref_wire(msg);
+ mrp_free(ack);
+ }
+}
+
+
+mrp_msg_t *msg_encode_ack(ack_msg_t *ack)
+{
+ return mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_ACK),
+ MSG_UINT32(MSGSEQ , ack->seq),
+ MSG_END);
+}
+
+
+msg_t *msg_decode_ack(mrp_msg_t *msg)
+{
+ ack_msg_t *ack;
+ void *it;
+ uint32_t seqno;
+
+ ack = mrp_allocz(sizeof(*ack));
+
+ if (ack != NULL) {
+ it = NULL;
+
+ if (mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ, &seqno),
+ MSG_END)) {
+ ack->type = MSG_TYPE_ACK;
+ ack->seq = seqno;
+
+ return (msg_t *)ack;
+ }
+
+ msg_free_ack((msg_t *)ack);
+ }
+
+ return NULL;
+}
+
+
+void msg_free_nak(msg_t *msg)
+{
+ nak_msg_t *nak = (nak_msg_t *)msg;
+
+ if (nak != NULL) {
+ unref_wire(msg);
+ mrp_free(nak);
+ }
+}
+
+
+mrp_msg_t *msg_encode_nak(nak_msg_t *nak)
+{
+ return mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_NAK),
+ MSG_UINT32(MSGSEQ , nak->seq),
+ MSG_SINT32(ERRCODE, nak->error),
+ MSG_STRING(ERRMSG, nak->msg),
+ MSG_END);
+}
+
+
+msg_t *msg_decode_nak(mrp_msg_t *msg)
+{
+ nak_msg_t *nak;
+ void *it;
+ uint32_t seqno;
+ int32_t error;
+ const char *errmsg;
+
+ nak = mrp_allocz(sizeof(*nak));
+
+ if (nak != NULL) {
+ it = NULL;
+
+ if (mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ , &seqno),
+ MSG_SINT32(ERRCODE, &error),
+ MSG_STRING(ERRMSG , &errmsg),
+ MSG_END)) {
+ nak->type = MSG_TYPE_NAK;
+ nak->seq = seqno;
+ nak->error = error;
+ nak->msg = errmsg;
+
+ nak->wire = mrp_msg_ref(msg);
+ nak->unref_wire = msg_unref_wire;
+
+ return (msg_t *)nak;
+ }
+
+ msg_free_nak((msg_t *)nak);
+ }
+
+ return NULL;
+}
+
+
+void msg_free_set(msg_t *msg)
+{
+ set_msg_t *set = (set_msg_t *)msg;
+ int values_freed, i;
+
+ if (set != NULL) {
+ values_freed = FALSE;
+ for (i = 0; i < set->ntable; i++) {
+ if (set->tables != NULL && set->tables[i].rows != NULL) {
+ if (!values_freed && set->tables[i].rows[0] != NULL) {
+ mrp_free(set->tables[i].rows[0]);
+ values_freed = TRUE;
+ }
+ mrp_free(set->tables[i].rows);
+ }
+ }
+
+ mrp_free(set->tables);
+ mrp_free(set);
+ }
+}
+
+
+mrp_msg_t *msg_encode_set(set_msg_t *set)
+{
+ mrp_msg_t *msg;
+ mrp_domctl_value_t *rows, *col;
+ uint16_t utable, utotal, tid, ncol, nrow;
+ int i, r, c;
+
+ utable = set->ntable;
+ utotal = 0;
+
+ msg = mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_SET),
+ MSG_UINT32(MSGSEQ , set->seq),
+ MSG_UINT16(NCHANGE, utable),
+ MSG_UINT16(NTOTAL , 0),
+ MSG_END);
+
+ if (msg == NULL)
+ return NULL;
+
+ for (i = 0; i < set->ntable; i++) {
+ tid = set->tables[i].id;
+ ncol = set->tables[i].ncolumn;
+ nrow = set->tables[i].nrow;
+
+ if (!mrp_msg_append(msg, MSG_UINT16(TBLID, tid)) ||
+ !mrp_msg_append(msg, MSG_UINT16(NROW , nrow)) ||
+ !mrp_msg_append(msg, MSG_UINT16(NCOL , ncol)))
+ goto fail;
+
+ for (r = 0; r < nrow; r++) {
+ rows = set->tables[i].rows[r];
+
+ for (c = 0; c < ncol; c++) {
+ col = rows + c;
+
+#define HANDLE_TYPE(pt, t, m) \
+ case MRP_DOMCTL_##pt: \
+ if (!mrp_msg_append(msg, MSG_##t(DATA,col->m))) \
+ goto fail; \
+ break
+
+ switch (col->type) {
+ HANDLE_TYPE(STRING , STRING, str);
+ HANDLE_TYPE(INTEGER , SINT32, s32);
+ HANDLE_TYPE(UNSIGNED, UINT32, u32);
+ HANDLE_TYPE(DOUBLE , DOUBLE, dbl);
+ default:
+ goto fail;
+ }
+#undef HANDLE_TYPE
+ }
+ }
+
+ utotal += nrow * ncol;
+ }
+
+ mrp_msg_set(msg, MSG_UINT16(NTOTAL, utotal));
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ return NULL;
+}
+
+
+msg_t *msg_decode_set(mrp_msg_t *msg)
+{
+ set_msg_t *set;
+ void *it;
+ mrp_domctl_data_t *d;
+ mrp_domctl_value_t *values, *v;
+ uint64_t columns_so_far;
+ uint32_t seqno;
+ uint16_t ntable, ntotal, nrow, ncol, tblid, type;
+ int t, r, c;
+ mrp_msg_value_t value;
+
+ it = NULL;
+ columns_so_far = 0;
+
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ , &seqno),
+ MSG_UINT16(NCHANGE, &ntable),
+ MSG_UINT16(NTOTAL , &ntotal),
+ MSG_END))
+ return NULL;
+
+ set = mrp_allocz(sizeof(*set));
+
+ if (set == NULL)
+ return NULL;
+
+ values = NULL;
+ set->type = MSG_TYPE_SET;
+ set->seq = seqno;
+ set->tables = mrp_allocz(sizeof(*set->tables) * ntable);
+
+ if (set->tables == NULL && ntable != 0)
+ goto fail;
+
+ values = mrp_allocz(sizeof(*values) * ntotal);
+
+ if (values == NULL && ntotal != 0)
+ goto fail;
+
+ d = set->tables;
+ v = values;
+
+ for (t = 0; t < ntable; t++) {
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT16(TBLID, &tblid),
+ MSG_UINT16(NROW , &nrow ),
+ MSG_UINT16(NCOL , &ncol ),
+ MSG_END))
+ goto fail;
+
+ d->id = tblid;
+ d->ncolumn = ncol;
+ d->nrow = nrow;
+ d->rows = mrp_allocz(sizeof(*d->rows) * nrow);
+
+ if (d->rows == NULL && nrow)
+ goto fail;
+
+ /* Check if we go over the possible total */
+ if (columns_so_far + (nrow * ncol) > ntotal)
+ goto fail;
+
+ /* If we are not overflowing, add ncol to count */
+ columns_so_far += nrow * ncol;
+
+ for (r = 0; r < nrow; r++) {
+ d->rows[r] = v;
+
+ for (c = 0; c < ncol; c++) {
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_ANY(DATA, &type, &value),
+ MSG_END))
+ goto fail;
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ v->type = MRP_DOMCTL_STRING;
+ v->str = value.str;
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ v->type = MRP_DOMCTL_INTEGER;
+ v->s32 = value.s32;
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ v->type = MRP_DOMCTL_UNSIGNED;
+ v->u32 = value.u32;
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ v->type = MRP_DOMCTL_DOUBLE;
+ v->dbl = value.dbl;
+ break;
+ default:
+ goto fail;
+ }
+
+ v++;
+ }
+ }
+
+ d++;
+ }
+
+ set->ntable = ntable;
+
+ set->wire = mrp_msg_ref(msg);
+ set->unref_wire = msg_unref_wire;
+
+ return (msg_t *)set;
+
+ fail:
+ msg_free_set((msg_t *)set);
+ mrp_free(values);
+
+ return NULL;
+}
+
+
+void msg_free_notify(msg_t *msg)
+{
+ notify_msg_t *notify = (notify_msg_t *)msg;
+ int values_freed, i;
+
+ if (notify != NULL) {
+ values_freed = FALSE;
+ for (i = 0; i < notify->ntable; i++) {
+ if (notify->tables[i].rows != NULL) {
+ if (!values_freed && notify->tables[i].rows[0] != NULL) {
+ mrp_free(notify->tables[i].rows[0]);
+ values_freed = TRUE;
+ }
+ mrp_free(notify->tables[i].rows);
+ }
+ }
+
+ mrp_free(notify->tables);
+ unref_wire((msg_t *)notify);
+ mrp_free(notify);
+ }
+}
+
+
+mrp_msg_t *msg_encode_notify(notify_msg_t *msg)
+{
+ MRP_UNUSED(msg);
+
+ return NULL;
+}
+
+
+mrp_msg_t *msg_create_notify(void)
+{
+ return mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_NOTIFY),
+ MSG_UINT32(MSGSEQ , 0),
+ MSG_UINT16(NCHANGE, 0),
+ MSG_UINT16(NTOTAL , 0),
+ MSG_END);
+}
+
+
+int msg_update_notify(mrp_msg_t *msg, int tblid, mql_result_t *r)
+{
+ uint16_t tid, nrow, ncol;
+ int types[MQI_COLUMN_MAX];
+ const char *str;
+ uint32_t u32;
+ int32_t s32;
+ double dbl;
+ int i, j;
+
+ if (r != NULL) {
+ nrow = mql_result_rows_get_row_count(r);
+ ncol = mql_result_rows_get_row_column_count(r);
+ }
+ else
+ nrow = ncol = 0;
+
+ tid = tblid;
+ if (!mrp_msg_append(msg, MSG_UINT16(TBLID, tid)) ||
+ !mrp_msg_append(msg, MSG_UINT16(NROW , nrow)) ||
+ !mrp_msg_append(msg, MSG_UINT16(NCOL , ncol)))
+ goto fail;
+
+ for (i = 0; i < ncol; i++)
+ types[i] = mql_result_rows_get_row_column_type(r, i);
+
+ for (i = 0; i < nrow; i++) {
+ for (j = 0; j < ncol; j++) {
+ switch (types[j]) {
+ case mqi_string:
+ str = mql_result_rows_get_string(r, j, i, NULL, 0);
+ if (!mrp_msg_append(msg, MSG_STRING(DATA, str)))
+ goto fail;
+ break;
+ case mqi_integer:
+ s32 = mql_result_rows_get_integer(r, j, i);
+ if (!mrp_msg_append(msg, MSG_SINT32(DATA, s32)))
+ goto fail;
+ break;
+ case mqi_unsignd:
+ u32 = mql_result_rows_get_unsigned(r, j, i);
+ if (!mrp_msg_append(msg, MSG_UINT32(DATA, u32)))
+ goto fail;
+ break;
+
+ case mqi_floating:
+ dbl = mql_result_rows_get_floating(r, j, i);
+ if (!mrp_msg_append(msg, MSG_DOUBLE(DATA, dbl)))
+ goto fail;
+ break;
+
+ default:
+ goto fail;
+ }
+ }
+
+ mrp_debug_code({
+ char buf[4096], *p;
+ int n, l;
+
+ p = buf;
+ l = sizeof(buf) - 1;
+
+ n = snprintf(p, l, "{");
+ p += n;
+ l -= n;
+
+ for (j = 0; j < ncol; j++) {
+ switch (types[j]) {
+ case mqi_string:
+ str = mql_result_rows_get_string(r, j, i, NULL, 0);
+ n = snprintf(p, l, "%s'%s'", j ? ", " : " ", str);
+ break;
+ case mqi_integer:
+ s32 = mql_result_rows_get_integer(r, j, i);
+ n = snprintf(p, l, "%s%d", j ? ", " : " ", s32);
+ break;
+ case mqi_unsignd:
+ u32 = mql_result_rows_get_unsigned(r, j, i);
+ n = snprintf(p, l, "%s%u", j ? ", " : " ", u32);
+ break;
+ case mqi_floating:
+ dbl = mql_result_rows_get_floating(r, j, i);
+ n = snprintf(p, l, "%s%f", j ? ", " : " ", dbl);
+ break;
+ default:
+ continue;
+ }
+
+ p += n;
+ l -= n;
+
+ if (l <= 0)
+ break;
+ }
+
+ if (l > 2) {
+ *p++ = ' ', *p++ = '}';
+ *p = '\0';
+
+ mrp_debug("%s", buf);
+ }
+ });
+ }
+
+ return nrow * ncol;
+
+ fail:
+ return -1;
+}
+
+
+msg_t *msg_decode_notify(mrp_msg_t *msg)
+{
+ notify_msg_t *notify;
+ mrp_domctl_data_t *d;
+ mrp_domctl_value_t *values, *v;
+ void *it;
+ uint64_t columns_so_far;
+ uint32_t seqno;
+ uint16_t ntable, ntotal, nrow, ncol;
+ uint16_t tblid;
+ int t, r, c;
+ uint16_t type;
+ mrp_msg_value_t value;
+
+ it = NULL;
+ columns_so_far = 0;
+
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ, &seqno),
+ MSG_UINT16(NCHANGE, &ntable),
+ MSG_UINT16(NTOTAL , &ntotal),
+ MSG_END))
+ return NULL;
+
+ notify = mrp_allocz(sizeof(*notify));
+
+ if (notify == NULL)
+ return NULL;
+
+ values = NULL;
+ notify->type = MSG_TYPE_NOTIFY;
+ notify->seq = seqno;
+ notify->tables = mrp_allocz(sizeof(*notify->tables) * ntable);
+
+ if (notify->tables == NULL && ntable != 0)
+ goto fail;
+
+ values = ntotal ? mrp_allocz(sizeof(*values) * ntotal) : NULL;
+
+ if (values == NULL && ntotal != 0)
+ goto fail;
+
+ d = notify->tables;
+ v = values;
+
+ for (t = 0; t < ntable; t++) {
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT16(TBLID, &tblid),
+ MSG_UINT16(NROW , &nrow ),
+ MSG_UINT16(NCOL , &ncol ),
+ MSG_END))
+ goto fail;
+
+ d->id = tblid;
+ d->ncolumn = ncol;
+ d->nrow = nrow;
+ d->rows = nrow ? mrp_allocz(sizeof(*d->rows) * nrow) : NULL;
+
+ if (d->rows == NULL && nrow != 0)
+ goto fail;
+
+ /* Check if we go over the possible total */
+ if (columns_so_far + (nrow * ncol) > ntotal)
+ goto fail;
+
+ /* If we are not overflowing, add ncol to count */
+ columns_so_far += nrow * ncol;
+
+ for (r = 0; r < nrow; r++) {
+ d->rows[r] = v;
+
+ for (c = 0; c < ncol; c++) {
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_ANY(DATA, &type, &value),
+ MSG_END))
+ goto fail;
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ v->type = MRP_DOMCTL_STRING;
+ v->str = value.str;
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ v->type = MRP_DOMCTL_INTEGER;
+ v->s32 = value.s32;
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ v->type = MRP_DOMCTL_UNSIGNED;
+ v->u32 = value.u32;
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ v->type = MRP_DOMCTL_DOUBLE;
+ v->dbl = value.dbl;
+ break;
+ default:
+ goto fail;
+ }
+
+ v++;
+ }
+ }
+
+ d++;
+ }
+
+ notify->ntable = ntable;
+
+ notify->wire = mrp_msg_ref(msg);
+ notify->unref_wire = msg_unref_wire;
+
+ return (msg_t *)notify;
+
+ fail:
+ msg_free_notify((msg_t *)notify);
+ mrp_free(values);
+
+ return NULL;
+}
+
+
+void msg_free_invoke(msg_t *msg)
+{
+ if (msg != NULL) {
+ mrp_free(msg->invoke.args);
+ mrp_free(msg);
+ }
+}
+
+
+mrp_msg_t *msg_encode_invoke(invoke_msg_t *invoke)
+{
+ mrp_msg_t *msg;
+ mrp_domctl_arg_t *arg;
+ uint32_t i;
+
+ msg = mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_INVOKE),
+ MSG_UINT32(MSGSEQ , invoke->seq),
+ MSG_STRING(METHOD , invoke->name),
+ MSG_BOOL (NORET , invoke->noret),
+ MSG_UINT32(NARG , invoke->narg),
+ MSG_END);
+
+ for (i = 0, arg = invoke->args; i < invoke->narg; i++, arg++) {
+ switch (arg->type) {
+ case MRP_DOMCTL_STRING:
+ if (!mrp_msg_append(msg, MSG_STRING(ARG, arg->str)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_DOUBLE:
+ if (!mrp_msg_append(msg, MSG_DOUBLE(ARG, arg->dbl)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_BOOL:
+ if (!mrp_msg_append(msg, MSG_BOOL(ARG, arg->bln)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT8:
+ if (!mrp_msg_append(msg, MSG_UINT8(ARG, arg->s8)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT8:
+ if (!mrp_msg_append(msg, MSG_SINT8(ARG, arg->u8)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT16:
+ if (!mrp_msg_append(msg, MSG_UINT16(ARG, arg->s16)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT16:
+ if (!mrp_msg_append(msg, MSG_SINT16(ARG, arg->u16)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT32:
+ if (!mrp_msg_append(msg, MSG_UINT32(ARG, arg->s32)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT32:
+ if (!mrp_msg_append(msg, MSG_SINT32(ARG, arg->u32)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT64:
+ if (!mrp_msg_append(msg, MSG_UINT64(ARG, arg->s64)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT64:
+ if (!mrp_msg_append(msg, MSG_SINT64(ARG, arg->u64)))
+ goto fail;
+ break;
+ default:
+ if (MRP_DOMCTL_IS_ARRAY(arg->type)) {
+ if (!mrp_msg_append(msg, MSG_ARRAY(ARG, arg->type,
+ arg->size, arg->arr)))
+ goto fail;
+ }
+ else
+ goto fail;
+ break;
+ }
+ }
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ return NULL;
+}
+
+
+msg_t *msg_decode_invoke(mrp_msg_t *msg)
+{
+ invoke_msg_t *invoke;
+ void *it;
+ uint16_t tag, type;
+ mrp_msg_value_t val;
+ mrp_domctl_arg_t *arg;
+ uint32_t i;
+ size_t size;
+
+ mrp_debug_code({
+ mrp_debug("got domain invoke request:");
+ mrp_msg_dump(msg, stdout); });
+
+ it = NULL;
+ invoke = mrp_allocz(sizeof(*invoke));
+
+ if (invoke == NULL)
+ goto fail;
+
+ invoke->type = MSG_TYPE_INVOKE;
+
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ, &invoke->seq),
+ MSG_STRING(METHOD, &invoke->name),
+ MSG_BOOL (NORET , &invoke->noret),
+ MSG_UINT32(NARG , &invoke->narg),
+ MSG_END))
+ goto fail;
+
+
+
+ if (invoke->narg > 0)
+ invoke->args = mrp_allocz(invoke->narg * sizeof(invoke->args[0]));
+
+ if (invoke->args == NULL && invoke->narg > 0)
+ goto fail;
+
+ for (i = 0, arg = invoke->args; i < invoke->narg; i++, arg++) {
+ if (!mrp_msg_iterate(msg, &it, &tag, &type, &val, &size))
+ goto fail;
+
+ arg->type = type;
+
+ switch (type) {
+ case MRP_DOMCTL_STRING: arg->str = val.str; break;
+ case MRP_DOMCTL_BOOL: arg->bln = val.bln; break;
+ case MRP_DOMCTL_UINT8: arg->u8 = val.u8; break;
+ case MRP_DOMCTL_INT8: arg->s8 = val.s8; break;
+ case MRP_DOMCTL_UINT16: arg->u16 = val.u16; break;
+ case MRP_DOMCTL_INT16: arg->s16 = val.s16; break;
+ case MRP_DOMCTL_UINT32: arg->u32 = val.u32; break;
+ case MRP_DOMCTL_INT32: arg->s32 = val.s32; break;
+ case MRP_DOMCTL_UINT64: arg->u64 = val.u64; break;
+ case MRP_DOMCTL_INT64: arg->s64 = val.s64; break;
+ case MRP_DOMCTL_DOUBLE: arg->dbl = val.dbl; break;
+ default:
+ if (MRP_DOMCTL_IS_ARRAY(type)) {
+ arg->arr = val.aany;
+ arg->size = size;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ invoke->wire = mrp_msg_ref(msg);
+ invoke->unref_wire = msg_unref_wire;
+
+ return (msg_t *)invoke;
+
+ fail:
+ msg_free_invoke((msg_t *)invoke);
+ return NULL;
+}
+
+
+void msg_free_return(msg_t *msg)
+{
+ if (msg != NULL) {
+ mrp_free(msg->ret.args);
+ mrp_free(msg);
+ }
+}
+
+
+mrp_msg_t *msg_encode_return(return_msg_t *ret)
+{
+ mrp_msg_t *msg;
+ mrp_domctl_arg_t *arg;
+ uint32_t i;
+
+ msg = mrp_msg_create(MSG_UINT16(MSGTYPE, MSG_TYPE_RETURN),
+ MSG_UINT32(MSGSEQ , ret->seq),
+ MSG_UINT32(ERROR , ret->error),
+ MSG_SINT32(RETVAL , ret->retval),
+ MSG_UINT32(NARG , ret->narg),
+ MSG_END);
+
+ for (i = 0, arg = ret->args; i < ret->narg; i++, arg++) {
+ switch (arg->type) {
+ case MRP_DOMCTL_STRING:
+ if (!mrp_msg_append(msg, MSG_STRING(ARG, arg->str)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_DOUBLE:
+ if (!mrp_msg_append(msg, MSG_DOUBLE(ARG, arg->dbl)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_BOOL:
+ if (!mrp_msg_append(msg, MSG_BOOL(ARG, arg->bln)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT8:
+ if (!mrp_msg_append(msg, MSG_UINT8(ARG, arg->s8)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT8:
+ if (!mrp_msg_append(msg, MSG_SINT8(ARG, arg->u8)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT16:
+ if (!mrp_msg_append(msg, MSG_UINT16(ARG, arg->s16)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT16:
+ if (!mrp_msg_append(msg, MSG_SINT16(ARG, arg->u16)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT32:
+ if (!mrp_msg_append(msg, MSG_UINT32(ARG, arg->s32)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT32:
+ if (!mrp_msg_append(msg, MSG_SINT32(ARG, arg->u32)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_UINT64:
+ if (!mrp_msg_append(msg, MSG_UINT64(ARG, arg->s64)))
+ goto fail;
+ break;
+ case MRP_DOMCTL_INT64:
+ if (!mrp_msg_append(msg, MSG_SINT64(ARG, arg->u64)))
+ goto fail;
+ break;
+ default:
+ if (MRP_DOMCTL_IS_ARRAY(arg->type)) {
+ if (!mrp_msg_append(msg, MSG_ARRAY(ARG, arg->type,
+ arg->size, arg->arr)))
+ goto fail;
+ }
+ else
+ goto fail;
+ break;
+ }
+ }
+
+ return msg;
+
+ fail:
+ mrp_msg_unref(msg);
+ return NULL;
+}
+
+
+msg_t *msg_decode_return(mrp_msg_t *msg)
+{
+ return_msg_t *ret;
+ void *it;
+ uint16_t tag, type;
+ mrp_msg_value_t val;
+ mrp_domctl_arg_t *arg;
+ uint32_t i;
+ size_t size;
+
+ mrp_debug_code({
+ mrp_debug("got domain return (invoke reply):");
+ mrp_msg_dump(msg, stdout); });
+
+ it = NULL;
+ ret = mrp_allocz(sizeof(*ret));
+
+ if (ret == NULL)
+ goto fail;
+
+ ret->type = MSG_TYPE_RETURN;
+
+ if (!mrp_msg_iterate_get(msg, &it,
+ MSG_UINT32(MSGSEQ, &ret->seq),
+ MSG_UINT32(ERROR , &ret->error),
+ MSG_SINT32(RETVAL, &ret->retval),
+ MSG_UINT32(NARG , &ret->narg),
+ MSG_END))
+ goto fail;
+
+ if (ret->narg > 0)
+ ret->args = mrp_allocz(ret->narg * sizeof(ret->args[0]));
+
+ if (ret->args == NULL && ret->narg > 0)
+ goto fail;
+
+ for (i = 0, arg = ret->args; i < ret->narg; i++, arg++) {
+ if (!mrp_msg_iterate(msg, &it, &tag, &type, &val, &size))
+ goto fail;
+
+ arg->type = type;
+
+ switch (type) {
+ case MRP_DOMCTL_STRING: arg->str = val.str; break;
+ case MRP_DOMCTL_BOOL: arg->bln = val.bln; break;
+ case MRP_DOMCTL_UINT8: arg->u8 = val.u8; break;
+ case MRP_DOMCTL_INT8: arg->s8 = val.s8; break;
+ case MRP_DOMCTL_UINT16: arg->u16 = val.u16; break;
+ case MRP_DOMCTL_INT16: arg->s16 = val.s16; break;
+ case MRP_DOMCTL_UINT32: arg->u32 = val.u32; break;
+ case MRP_DOMCTL_INT32: arg->s32 = val.s32; break;
+ case MRP_DOMCTL_UINT64: arg->u64 = val.u64; break;
+ case MRP_DOMCTL_INT64: arg->s64 = val.s64; break;
+ case MRP_DOMCTL_DOUBLE: arg->dbl = val.dbl; break;
+ default:
+ if (MRP_DOMCTL_IS_ARRAY(type)) {
+ arg->arr = val.aany;
+ arg->size = size;
+ }
+ else
+ goto fail;
+ }
+ }
+
+ ret->wire = mrp_msg_ref(msg);
+ ret->unref_wire = msg_unref_wire;
+
+ return (msg_t *)ret;
+
+ fail:
+ msg_free_return((msg_t *)ret);
+ return NULL;
+}
+
+
+msg_t *msg_decode_message(mrp_msg_t *msg)
+{
+ uint16_t type;
+
+ if (mrp_msg_get(msg, MSG_UINT16(MSGTYPE, &type), MSG_END)) {
+ switch (type) {
+ case MSG_TYPE_REGISTER: return msg_decode_register(msg);
+ case MSG_TYPE_UNREGISTER: return msg_decode_unregister(msg);
+ case MSG_TYPE_SET: return msg_decode_set(msg);
+ case MSG_TYPE_NOTIFY: return msg_decode_notify(msg);
+ case MSG_TYPE_ACK: return msg_decode_ack(msg);
+ case MSG_TYPE_NAK: return msg_decode_nak(msg);
+ case MSG_TYPE_INVOKE: return msg_decode_invoke(msg);
+ case MSG_TYPE_RETURN: return msg_decode_return(msg);
+ default: break;
+ }
+ }
+
+ return NULL;
+}
+
+
+mrp_msg_t *msg_encode_message(msg_t *msg)
+{
+ switch (msg->any.type) {
+ case MSG_TYPE_REGISTER: return msg_encode_register(&msg->reg);
+ case MSG_TYPE_UNREGISTER: return msg_encode_unregister(&msg->unreg);
+ case MSG_TYPE_SET: return msg_encode_set(&msg->set);
+ case MSG_TYPE_NOTIFY: return msg_encode_notify(&msg->notify);
+ case MSG_TYPE_ACK: return msg_encode_ack(&msg->ack);
+ case MSG_TYPE_NAK: return msg_encode_nak(&msg->nak);
+ case MSG_TYPE_INVOKE: return msg_encode_invoke(&msg->invoke);
+ case MSG_TYPE_RETURN: return msg_encode_return(&msg->ret);
+ default: return NULL;
+ }
+}
+
+
+void msg_free_message(msg_t *msg)
+{
+ if (msg != NULL) {
+ switch (msg->any.type) {
+ case MSG_TYPE_REGISTER: msg_free_register(msg); break;
+ case MSG_TYPE_UNREGISTER: msg_free_unregister(msg); break;
+ case MSG_TYPE_SET: msg_free_set(msg); break;
+ case MSG_TYPE_NOTIFY: msg_free_notify(msg); break;
+ case MSG_TYPE_ACK: msg_free_ack(msg); break;
+ case MSG_TYPE_NAK: msg_free_nak(msg); break;
+ case MSG_TYPE_INVOKE: msg_free_invoke(msg); break;
+ case MSG_TYPE_RETURN: msg_free_return(msg); break;
+ default: break;
+ }
+ }
+}
+
+
+static void json_unref_wire(void *wire)
+{
+ mrp_json_unref((mrp_json_t *)wire);
+}
+
+
+mrp_json_t *json_encode_register(register_msg_t *reg)
+{
+ MRP_UNUSED(reg);
+
+ return NULL;
+}
+
+
+msg_t *json_decode_register(mrp_json_t *msg)
+{
+
+ register_msg_t *reg;
+ mrp_domctl_table_t *t;
+ mrp_domctl_watch_t *w;
+ int seqno;
+ char *name, *table, *columns, *index, *where;
+ int ntable, nwatch, max_rows;
+ mrp_json_t *arr, *tbl, *wch;
+ int i;
+
+ if (!mrp_json_get_integer(msg, "seq" , &seqno) ||
+ !mrp_json_get_string (msg, "name" , &name) ||
+ !mrp_json_get_integer(msg, "ntable", &ntable) ||
+ !mrp_json_get_integer(msg, "nwatch", &nwatch))
+ return NULL;
+
+ reg = mrp_allocz(sizeof(*reg));
+
+ if (reg == NULL)
+ return NULL;
+
+ reg->type = MSG_TYPE_REGISTER;
+ reg->seq = seqno;
+ reg->name = name;
+ reg->tables = mrp_allocz(sizeof(*reg->tables) * ntable);
+ reg->watches = mrp_allocz(sizeof(*reg->watches) * nwatch);
+
+ if ((reg->tables == NULL && ntable) || (reg->watches == NULL && nwatch))
+ goto fail;
+
+ if (!mrp_json_get_array(msg, "tables", &arr))
+ goto fail;
+
+ if (mrp_json_array_length(arr) != ntable)
+ goto fail;
+
+ for (i = 0, t = reg->tables; i < ntable; i++, t++) {
+ if (!mrp_json_array_get_object(arr, i, &tbl))
+ goto fail;
+
+ if (mrp_json_get_string(tbl, "table" , &table) &&
+ mrp_json_get_string(tbl, "columns", &columns) &&
+ mrp_json_get_string(tbl, "index" , &index)) {
+ t->table = table;
+ t->mql_columns = columns;
+ t->mql_index = index;
+ }
+ else
+ goto fail;
+ }
+
+ reg->ntable = ntable;
+
+ if (!mrp_json_get_array(msg, "watches", &arr))
+ goto fail;
+
+ if (mrp_json_array_length(arr) != nwatch)
+ goto fail;
+
+ for (i = 0, w = reg->watches; i < nwatch; i++, w++) {
+ if (!mrp_json_array_get_object(arr, i, &wch))
+ goto fail;
+
+ if (mrp_json_get_string (wch, "table" , &table) &&
+ mrp_json_get_string (wch, "columns", &columns) &&
+ mrp_json_get_string (wch, "where" , &where) &&
+ mrp_json_get_integer(wch, "maxrows", &max_rows)) {
+ w->table = table;
+ w->mql_columns = columns;
+ w->mql_where = where;
+ w->max_rows = max_rows;
+ }
+ else
+ goto fail;
+ }
+
+ reg->nwatch = nwatch;
+
+ reg->wire = mrp_json_ref(msg);
+ reg->unref_wire = json_unref_wire;
+
+ return (msg_t *)reg;
+
+ fail:
+ msg_free_register((msg_t *)reg);
+
+ return NULL;
+}
+
+
+msg_t *json_decode_unregister(mrp_json_t *msg)
+{
+ unregister_msg_t *ureg;
+ int seqno;
+
+ ureg = mrp_allocz(sizeof(*ureg));
+
+ if (ureg != NULL) {
+ if (mrp_json_get_integer(msg, "seq", &seqno)) {
+ ureg->type = MSG_TYPE_UNREGISTER;
+ ureg->seq = seqno;
+
+ return (msg_t *)ureg;
+ }
+
+ msg_free_unregister((msg_t *)ureg);
+ }
+
+ return NULL;
+}
+
+
+mrp_json_t *json_encode_ack(ack_msg_t *ack)
+{
+ mrp_json_t *msg;
+ int seqno;
+
+ msg = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (msg != NULL) {
+ seqno = ack->seq;
+
+ if (mrp_json_add_string (msg, "type", "ack") &&
+ mrp_json_add_integer(msg, "seq" , seqno))
+ return msg;
+ else
+ mrp_json_unref(msg);
+ }
+
+ return NULL;
+}
+
+
+msg_t *json_decode_ack(mrp_json_t *msg)
+{
+ ack_msg_t *ack;
+ int seqno;
+
+ ack = mrp_allocz(sizeof(*ack));
+
+ if (ack != NULL) {
+ if (mrp_json_get_integer(msg, "seq", &seqno)) {
+ ack->type = MSG_TYPE_ACK;
+ ack->seq = seqno;
+
+ return (msg_t *)ack;
+ }
+
+ msg_free_ack((msg_t *)ack);
+ }
+
+ return NULL;
+}
+
+
+mrp_json_t *json_encode_nak(nak_msg_t *nak)
+{
+ mrp_json_t *msg;
+ int seqno, error;
+ const char *errmsg;
+
+ msg = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (msg != NULL) {
+ seqno = nak->seq;
+ error = nak->error;
+ errmsg = nak->msg;
+
+ if (mrp_json_add_string (msg, "type" , "nak") &&
+ mrp_json_add_integer(msg, "seq" , seqno) &&
+ mrp_json_add_integer(msg, "error" , error) &&
+ mrp_json_add_string (msg, "errmsg", errmsg))
+ return msg;
+ else
+ mrp_json_unref(msg);
+ }
+
+ return NULL;
+}
+
+
+msg_t *json_decode_nak(mrp_json_t *msg)
+{
+ nak_msg_t *nak;
+ int seqno, error;
+ const char *errmsg;
+
+ nak = mrp_allocz(sizeof(*nak));
+
+ if (nak != NULL) {
+ if (mrp_json_get_integer(msg, "seqno" , &seqno) &&
+ mrp_json_get_integer(msg, "error" , &error) &&
+ mrp_json_get_string (msg, "errmsg", &errmsg)) {
+ nak->type = MSG_TYPE_NAK;
+ nak->seq = seqno;
+ nak->error = error;
+ nak->msg = errmsg;
+
+ nak->wire = mrp_json_ref(msg);
+ nak->unref_wire = json_unref_wire;
+
+ return (msg_t *)nak;
+ }
+
+ msg_free_nak((msg_t *)nak);
+ }
+
+ return NULL;
+}
+
+
+msg_t *json_decode_set(mrp_json_t *msg)
+{
+ set_msg_t *set;
+ mrp_domctl_data_t *d;
+ mrp_domctl_value_t *values, *v;
+ mrp_json_t *tables, *tbl, *rows, *row, *col;
+ int seqno, ntable, ntotal, nrow, ncol, tblid;
+ int t, r, c;
+
+ if (!mrp_json_get_integer(msg, "seq" , &seqno) ||
+ !mrp_json_get_integer(msg, "nchange", &ntable) ||
+ !mrp_json_get_integer(msg, "ntotal" , &ntotal))
+ return NULL;
+
+ set = mrp_allocz(sizeof(*set));
+
+ if (set == NULL)
+ return NULL;
+
+ values = NULL;
+ set->type = MSG_TYPE_SET;
+ set->seq = seqno;
+ set->tables = mrp_allocz(sizeof(*set->tables) * ntable);
+
+ if (set->tables == NULL)
+ goto fail;
+
+ values = mrp_allocz(sizeof(*values) * ntotal);
+
+ if (values == NULL)
+ goto fail;
+
+ d = set->tables;
+ v = values;
+
+ if (!mrp_json_get_array(msg, "tables", &tables))
+ goto fail;
+
+ for (t = 0; t < ntable; t++) {
+ if (!mrp_json_array_get_object(tables, t, &tbl))
+ goto fail;
+
+ if (!mrp_json_get_integer(tbl, "id" , &tblid) ||
+ !mrp_json_get_integer(tbl, "nrow", &nrow) ||
+ !mrp_json_get_integer(tbl, "ncol", &ncol))
+ goto fail;
+
+ d->id = tblid;
+ d->ncolumn = ncol;
+ d->nrow = nrow;
+ d->rows = mrp_allocz(sizeof(*d->rows) * nrow);
+
+ if (d->rows == NULL && nrow)
+ goto fail;
+
+ if (!mrp_json_get_array(tbl, "rows", &rows))
+ goto fail;
+
+ for (r = 0; r < nrow; r++) {
+ if (!mrp_json_array_get_array(rows, t, &row))
+ goto fail;
+
+ d->rows[r] = v;
+ values = NULL;
+
+ for (c = 0; c < ncol; c++) {
+ col = mrp_json_array_get(row, c);
+
+ if (col == NULL)
+ goto fail;
+
+ switch (mrp_json_get_type(col)) {
+ case MRP_JSON_STRING:
+ v->type = MRP_DOMCTL_STRING;
+ v->str = mrp_json_string_value(col);
+ break;
+
+ case MRP_JSON_INTEGER:
+ v->type = MRP_DOMCTL_INTEGER;
+ v->s32 = mrp_json_integer_value(col);
+ break;
+
+ case MRP_JSON_BOOLEAN:
+ v->type = MRP_DOMCTL_INTEGER;
+ v->s32 = !!mrp_json_boolean_value(col);
+ break;
+
+ case MRP_JSON_DOUBLE:
+ v->type = MRP_DOMCTL_DOUBLE;
+ v->dbl = mrp_json_double_value(col);
+ break;
+
+ default:
+ goto fail;
+ }
+
+ v++;
+ }
+ }
+
+ d++;
+ }
+
+ set->ntable = ntable;
+
+ set->wire = mrp_json_ref(msg);
+ set->unref_wire = json_unref_wire;
+
+ return (msg_t *)set;
+
+ fail:
+ msg_free_set((msg_t *)set);
+ mrp_free(values);
+
+ return NULL;
+}
+
+
+mrp_json_t *json_create_notify(void)
+{
+ mrp_json_t *msg;
+
+ msg = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (msg != NULL) {
+ if (mrp_json_add_string (msg, "type" , "notify") &&
+ mrp_json_add_integer(msg, "seq" , 0))
+ return msg;
+ else
+ mrp_json_unref(msg);
+ }
+
+ return NULL;
+}
+
+
+int json_update_notify(mrp_json_t *msg, int tblid, mql_result_t *r)
+{
+ int nrow, ncol;
+ int types[MQI_COLUMN_MAX];
+ const char *str;
+ uint32_t u32;
+ int32_t s32;
+ double dbl;
+ mrp_json_t *tables, *tbl, *rows, *row;
+ int i, j;
+
+ if (r != NULL) {
+ nrow = mql_result_rows_get_row_count(r);
+ ncol = mql_result_rows_get_row_column_count(r);
+ }
+ else
+ nrow = ncol = 0;
+
+ if (!mrp_json_get_array(msg, "tables", &tables)) {
+ tables = mrp_json_create(MRP_JSON_ARRAY);
+
+ if (tables == NULL)
+ goto fail;
+
+ mrp_json_add(msg, "tables", tables);
+ }
+
+ tbl = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (tbl == NULL || !mrp_json_array_append(tables, tbl)) {
+ mrp_json_unref(tbl);
+ goto fail;
+ }
+
+ if (!mrp_json_add_integer(tbl, "id" , tblid) ||
+ !mrp_json_add_integer(tbl, "nrow", nrow) ||
+ !mrp_json_add_integer(tbl, "ncol", ncol))
+ goto fail;
+
+ rows = mrp_json_create(MRP_JSON_ARRAY);
+
+ if (rows == NULL)
+ goto fail;
+
+ mrp_json_add(tbl, "rows", rows);
+
+ for (i = 0; i < ncol; i++)
+ types[i] = mql_result_rows_get_row_column_type(r, i);
+
+ for (i = 0; i < nrow; i++) {
+ row = mrp_json_create(MRP_JSON_ARRAY);
+
+ if (row == NULL || !mrp_json_array_append(rows, row)) {
+ mrp_json_unref(row);
+ goto fail;
+ }
+
+ for (j = 0; j < ncol; j++) {
+ switch (types[j]) {
+ case mqi_string:
+ str = mql_result_rows_get_string(r, j, i, NULL, 0);
+ if (!mrp_json_array_append_string(row, str))
+ goto fail;
+ break;
+ case mqi_integer:
+ s32 = mql_result_rows_get_integer(r, j, i);
+ if (!mrp_json_array_append_integer(row, s32))
+ goto fail;
+ break;
+ case mqi_unsignd:
+ u32 = mql_result_rows_get_unsigned(r, j, i);
+ /* XXX TODO: check for overflow */
+ if (!mrp_json_array_append_integer(row, u32))
+ goto fail;
+ break;
+ case mqi_floating:
+ dbl = mql_result_rows_get_floating(r, j, i);
+ if (!mrp_json_array_append_double(row, dbl))
+ goto fail;
+ break;
+ default:
+ goto fail;
+ }
+ }
+ }
+
+ return nrow * ncol;
+
+ fail:
+ return -1;
+}
+
+
+msg_t *json_decode_message(mrp_json_t *msg)
+{
+ const char *type;
+
+ if (mrp_json_get_string(msg, "type", &type)) {
+ if (!strcmp(type, "register" )) return json_decode_register(msg);
+ if (!strcmp(type, "unregister")) return json_decode_unregister(msg);
+ if (!strcmp(type, "set" )) return json_decode_set(msg);
+ }
+
+ return NULL;
+}
+
+
+mrp_json_t *json_encode_message(msg_t *msg)
+{
+ switch (msg->any.type) {
+ case MSG_TYPE_ACK: return json_encode_ack(&msg->ack);
+ case MSG_TYPE_NAK: return json_encode_nak(&msg->nak);
+ default: return NULL;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_MESSAGE_H__
+#define __MURPHY_DOMAIN_CONTROL_MESSAGE_H__
+
+#include <murphy/common/msg.h>
+#include <murphy/common/json.h>
+#include <murphy-db/mql.h>
+
+#include "domain-control-types.h"
+
+typedef enum {
+ MSG_TYPE_UNKNOWN = 0,
+ MSG_TYPE_REGISTER,
+ MSG_TYPE_UNREGISTER,
+ MSG_TYPE_SET,
+ MSG_TYPE_NOTIFY,
+ MSG_TYPE_ACK,
+ MSG_TYPE_NAK,
+ MSG_TYPE_INVOKE,
+ MSG_TYPE_RETURN,
+} msg_type_t;
+
+typedef enum {
+ /* fixed common tags */
+ MSGTAG_MSGTYPE = 0x1, /* message type */
+ MSGTAG_MSGSEQ = 0x2, /* sequence number */
+
+ /* fixed tags in registration messages */
+ MSGTAG_NAME = 0x3, /* enforcement point name */
+ MSGTAG_NTABLE = 0x4, /* number of owned tables */
+ MSGTAG_NWATCH = 0x5, /* number of watched tables */
+ MSGTAG_TBLNAME = 0x6, /* table name */
+ MSGTAG_COLUMNS = 0x8, /* column definitions/list */
+ MSGTAG_INDEX = 0x9, /* index definition */
+ MSGTAG_WHERE = 0xa, /* where clause for select */
+ MSGTAG_MAXROWS = 0xb, /* max number of rows to select */
+
+ /* fixed tags in NAKs */
+ MSGTAG_ERRCODE = 0x3, /* error code */
+ MSGTAG_ERRMSG = 0x4, /* error message */
+
+ /* fixed tags in data notification messages */
+ MSGTAG_NCHANGE = 0x3, /* number of tables in notification */
+ MSGTAG_NTOTAL = 0x4, /* total columns in notification */
+ MSGTAG_TBLID = 0x5, /* table id */
+ MSGTAG_NROW = 0x6, /* number of table rows */
+ MSGTAG_NCOL = 0x7, /* number of columns in a row */
+ MSGTAG_DATA = 0x8, /* a data column */
+
+ /* fixed tags in invoke and return messages */
+ MSGTAG_METHOD = 0x3, /* method name */
+ MSGTAG_NORET = 0x4, /* whether return values ignored */
+ MSGTAG_NARG = 0x5, /* number of arguments */
+ MSGTAG_ARG = 0x6, /* argument */
+ MSGTAG_ERROR = 0x7, /* invocation error */
+ MSGTAG_RETVAL = 0x8, /* invocation return value */
+} msgtag_t;
+
+
+#define MSG_UINT8(tag, val) MRP_MSG_TAG_UINT8(MSGTAG_##tag, val)
+#define MSG_SINT8(tag, val) MRP_MSG_TAG_SINT8(MSGTAG_##tag, val)
+#define MSG_UINT16(tag, val) MRP_MSG_TAG_UINT16(MSGTAG_##tag, val)
+#define MSG_SINT16(tag, val) MRP_MSG_TAG_SINT16(MSGTAG_##tag, val)
+#define MSG_UINT32(tag, val) MRP_MSG_TAG_UINT32(MSGTAG_##tag, val)
+#define MSG_SINT32(tag, val) MRP_MSG_TAG_SINT32(MSGTAG_##tag, val)
+#define MSG_UINT64(tag, val) MRP_MSG_TAG_UINT64(MSGTAG_##tag, val)
+#define MSG_SINT64(tag, val) MRP_MSG_TAG_SINT64(MSGTAG_##tag, val)
+#define MSG_DOUBLE(tag, val) MRP_MSG_TAG_DOUBLE(MSGTAG_##tag, val)
+#define MSG_STRING(tag, val) MRP_MSG_TAG_STRING(MSGTAG_##tag, val)
+#define MSG_BOOL(tag, val) MRP_MSG_TAG_BOOL(MSGTAG_##tag, val)
+#define MSG_ANY(tag, typep, valp) MRP_MSG_TAG_ANY(MSGTAG_##tag, typep, valp)
+#define MSG_ARRAY(tag, type, size, arr) \
+ MRP_MSG_TAGGED(MSGTAG_##tag, type, size, arr)
+
+#define MSG_END MRP_MSG_END
+
+#define COMMON_MSG_FIELDS /* common message fields */ \
+ msg_type_t type; /* message type */ \
+ uint32_t seq; /* message sequence number */ \
+ void *wire; /* associated on-wire message */ \
+ void (*unref_wire)(void *) /* function to unref message */
+
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+ char *name; /* domain controller name */
+ mrp_domctl_table_t *tables; /* owned tables */
+ int ntable; /* number of tables */
+ mrp_domctl_watch_t *watches; /* watched tables */
+ int nwatch; /* number of watches */
+} register_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+} unregister_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+ mrp_domctl_data_t *tables; /* data for tables to set */
+ int ntable; /* number of tables */
+} set_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+ mrp_domctl_data_t *tables; /* data in changed tables */
+ int ntable; /* number of changed tables */
+} notify_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+} ack_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+ int32_t error;
+ const char *msg;
+} nak_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+ const char *name;
+ int noret;
+ uint32_t narg;
+ mrp_domctl_arg_t *args;
+} invoke_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+ uint32_t error;
+ int32_t retval;
+ uint32_t narg;
+ mrp_domctl_arg_t *args;
+} return_msg_t;
+
+
+typedef struct {
+ COMMON_MSG_FIELDS;
+} any_msg_t;
+
+
+union msg_u {
+ any_msg_t any;
+ register_msg_t reg;
+ unregister_msg_t unreg;
+ set_msg_t set;
+ notify_msg_t notify;
+ ack_msg_t ack;
+ nak_msg_t nak;
+ invoke_msg_t invoke;
+ return_msg_t ret;
+};
+
+
+mrp_msg_t *msg_encode_message(msg_t *msg);
+msg_t *msg_decode_message(mrp_msg_t *msg);
+mrp_json_t *json_encode_message(msg_t *msg);
+msg_t *json_decode_message(mrp_json_t *msg);
+void msg_free_message(msg_t *msg);
+
+mrp_msg_t *msg_create_notify(void);
+int msg_update_notify(mrp_msg_t *msg, int tblid, mql_result_t *r);
+
+mrp_json_t *json_create_notify(void);
+int json_update_notify(mrp_json_t *msg, int tblid, mql_result_t *r);
+
+#endif /* __MURPHY_DOMAIN_CONTROL_MESSAGE_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-domain-controller
+Description: Murphy policy framework, domain controller library.
+Requires: murphy-common = @PACKAGE_VERSION@
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-domain-controller
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include <murphy-db/mql-result.h>
+
+#include "domain-control-types.h"
+#include "message.h"
+#include "table.h"
+#include "notify.h"
+
+
+static void prepare_proxy_notification(pep_proxy_t *proxy)
+{
+ proxy->notify_ntable = 0;
+ proxy->notify_ncolumn = 0;
+ proxy->notify_fail = false;
+}
+
+
+static int collect_watch_notification(pep_watch_t *w)
+{
+ pep_proxy_t *proxy = w->proxy;
+ mql_result_t *r = NULL;
+ int n;
+
+ mrp_debug("updating %s watch for %s", w->table->name, proxy->name);
+
+ if (proxy->notify_msg == NULL) {
+ if (!proxy->ops->create_notify(proxy))
+ goto fail;
+ }
+
+ if (w->table->h != MQI_HANDLE_INVALID) {
+ if (!exec_mql(mql_result_rows, &r, "select %s from %s%s%s",
+ w->mql_columns, w->table->name,
+ w->mql_where[0] ? " where " : "", w->mql_where)) {
+ mrp_debug("select from table %s failed", w->table->name);
+ goto fail;
+ }
+ }
+
+ n = proxy->ops->update_notify(proxy, w->id, r);
+
+ if (r != NULL)
+ mql_result_free(r);
+
+ if (n >= 0)
+ return TRUE;
+ else {
+ fail:
+ proxy->ops->free_notify(proxy);
+ proxy->notify_fail = true;
+
+ return FALSE;
+ }
+}
+
+
+static int send_proxy_notification(pep_proxy_t *proxy)
+{
+ if (proxy->notify_msg == NULL)
+ return TRUE;
+
+ if (!proxy->notify_fail) {
+ mrp_debug("notifying client %s", proxy->name);
+
+ proxy->ops->send_notify(proxy);
+ proxy->ops->free_notify(proxy);
+ }
+ else
+ mrp_log_error("Failed to generate/send notification to %s.",
+ proxy->name);
+
+ proxy->notify_msg = NULL;
+ proxy->notify_ntable = 0;
+ proxy->notify_ncolumn = 0;
+ proxy->notify_fail = false;
+
+ return TRUE;
+}
+
+
+void notify_table_changes(pdp_t *pdp)
+{
+ mrp_list_hook_t *p, *n, *wp, *wn;
+ pep_proxy_t *proxy;
+ pep_table_t *t;
+ pep_watch_t *w;
+
+ mrp_debug("notifying clients about table changes");
+
+ mrp_list_foreach(&pdp->proxies, p, n) {
+ proxy = mrp_list_entry(p, typeof(*proxy), hook);
+ prepare_proxy_notification(proxy);
+ }
+
+ mrp_list_foreach(&pdp->tables, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+
+ mrp_debug("table '%s' has %s changes", t->name,
+ t->changed ? "unsynced" : "no");
+
+ if (!t->changed)
+ continue;
+
+ mrp_list_foreach(&t->watches, wp, wn) {
+ w = mrp_list_entry(wp, typeof(*w), tbl_hook);
+ w->proxy->notify = true;
+ }
+ }
+
+ mrp_list_foreach(&pdp->proxies, p, n) {
+ proxy = mrp_list_entry(p, typeof(*proxy), hook);
+
+ mrp_debug("proxy %s needs %supdate", proxy->name,
+ proxy->notify ? "" : "no ");
+
+ if (proxy->notify) {
+ mrp_list_foreach(&proxy->watches, wp, wn) {
+ w = mrp_list_entry(wp, typeof(*w), pep_hook);
+ if (!collect_watch_notification(w))
+ break;
+ }
+
+ send_proxy_notification(proxy);
+
+ proxy->notify = false;
+ }
+ }
+
+ mrp_list_foreach(&pdp->tables, p, n) {
+ t = mrp_list_entry(p, typeof(*t), hook);
+ t->changed = false;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_NOTIFY_H__
+#define __MURPHY_DOMAIN_CONTROL_NOTIFY_H__
+
+#include "domain-control-types.h"
+
+void notify_table_changes(pdp_t *pdp);
+
+#endif /* __MURPHY_DOMAIN_CONTROL_NOTIFY_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/macros.h>
+
+#include <murphy/core/plugin.h>
+#include <murphy/core/console.h>
+
+#include "domain-control-types.h"
+#include "domain-control.h"
+#include "client.h"
+
+#define DEFAULT_EXTADDR MRP_DEFAULT_DOMCTL_ADDRESS
+#define NO_ADDR NULL
+
+#ifdef MURPHY_DATADIR
+# define DEFAULT_HTTPDIR MURPHY_DATADIR"/domain-control"
+#else
+# define DEFAULT_HTTPDIR "/usr/share/murphy/domain-control"
+#endif
+
+enum {
+ ARG_EXTADDR, /* external transport address */
+ ARG_INTADDR, /* internal transport address */
+ ARG_WRTADDR, /* WRT transport address */
+ ARG_HTTPDIR /* content directory for HTTP */
+};
+
+
+static int plugin_init(mrp_plugin_t *plugin)
+{
+ const char *extaddr = plugin->args[ARG_EXTADDR].str;
+ const char *intaddr = plugin->args[ARG_INTADDR].str;
+ const char *wrtaddr = plugin->args[ARG_WRTADDR].str;
+ const char *httpdir = plugin->args[ARG_HTTPDIR].str;
+
+ plugin->data = create_domain_control(plugin->ctx,
+ extaddr && *extaddr ? extaddr : NULL,
+ intaddr && *intaddr ? intaddr : NULL,
+ wrtaddr && *wrtaddr ? wrtaddr : NULL,
+ httpdir);
+
+ return (plugin->data != NULL);
+}
+
+
+static void plugin_exit(mrp_plugin_t *plugin)
+{
+ pdp_t *pdp = (pdp_t *)plugin->data;
+
+ destroy_domain_control(pdp);
+}
+
+
+static void cmd_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ printf("domctl:%s() called...\n", __FUNCTION__);
+}
+
+
+#define DOMCTL_DESCRIPTION "Murphy domain-control plugin."
+#define DOMCTL_HELP \
+ "The domain-control plugin provides a control interface for Murphy\n" \
+ "domain controllers. A domain controller is an entity capable of\n" \
+ "enforcing domain-specific policies in a certain resource domain, eg.\n" \
+ "audio, video, CPU-scheduling, etc. The domain-control plugin allows\n" \
+ "such entities to export and import domain-specific data to and from\n" \
+ "Murphy. Domain controllers typically import either ready decisions\n" \
+ "for their domain or data necessary for local decision making in\n" \
+ "the controller itself. The controllers typically export also some\n" \
+ "domain-specific data to Murphy which can then be used for decision\n" \
+ "making in other domains other domains.\n"
+
+#define DOMCTL_VERSION MRP_VERSION_INT(0, 0, 2)
+#define DOMCTL_AUTHORS "Krisztian Litkey <krisztian.litkey@intel.com>"
+
+MRP_CONSOLE_GROUP(domctl_commands, "domain-control", NULL, NULL, {
+ MRP_TOKENIZED_CMD("cmd", cmd_cb, TRUE,
+ "cmd [args]", "a command", "A command..."),
+});
+
+static mrp_plugin_arg_t domctl_args[] = {
+ MRP_PLUGIN_ARGIDX(ARG_EXTADDR, STRING, "external_address", DEFAULT_EXTADDR),
+ MRP_PLUGIN_ARGIDX(ARG_INTADDR, STRING, "internal_address", NO_ADDR ),
+ MRP_PLUGIN_ARGIDX(ARG_WRTADDR, STRING, "wrt_address" , NO_ADDR ),
+ MRP_PLUGIN_ARGIDX(ARG_HTTPDIR, STRING, "httpdir", DEFAULT_HTTPDIR)
+};
+
+MURPHY_REGISTER_PLUGIN("domain-control",
+ DOMCTL_VERSION, DOMCTL_DESCRIPTION,
+ DOMCTL_AUTHORS, DOMCTL_HELP, MRP_MULTIPLE,
+ plugin_init, plugin_exit,
+ domctl_args, MRP_ARRAY_SIZE(domctl_args),
+ NULL, 0, NULL, 0, &domctl_commands);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+
+#include "domain-control-types.h"
+#include "table.h"
+#include "proxy.h"
+
+
+/*
+ * a pending proxied invocation
+ */
+
+typedef struct {
+ mrp_list_hook_t hook; /* to pending list */
+ uint32_t id; /* request id */
+ mrp_domain_return_cb_t cb; /* return callback */
+ void *user_data; /* opaque callback data */
+} pending_t;
+
+static void purge_pending(pep_proxy_t *proxy);
+
+
+int init_proxies(pdp_t *pdp)
+{
+ mrp_list_init(&pdp->proxies);
+
+ return TRUE;
+}
+
+
+void destroy_proxies(pdp_t *pdp)
+{
+ MRP_UNUSED(pdp);
+
+ return;
+}
+
+
+pep_proxy_t *create_proxy(pdp_t *pdp)
+{
+ pep_proxy_t *proxy;
+
+ proxy = mrp_allocz(sizeof(*proxy));
+
+ if (proxy != NULL) {
+ mrp_list_init(&proxy->hook);
+ mrp_list_init(&proxy->watches);
+ mrp_list_init(&proxy->pending);
+
+ proxy->pdp = pdp;
+ proxy->seqno = 1;
+
+ mrp_list_append(&pdp->proxies, &proxy->hook);
+ }
+
+ return proxy;
+}
+
+
+void destroy_proxy(pep_proxy_t *proxy)
+{
+ int i;
+
+ if (proxy != NULL) {
+ mrp_list_delete(&proxy->hook);
+
+ for (i = 0; i < proxy->ntable; i++)
+ destroy_proxy_table(proxy->tables + i);
+
+ destroy_proxy_watches(proxy);
+
+ purge_pending(proxy);
+
+ mrp_free(proxy);
+ }
+}
+
+
+int register_proxy(pep_proxy_t *proxy, char *name,
+ mrp_domctl_table_t *tables, int ntable,
+ mrp_domctl_watch_t *watches, int nwatch,
+ int *error, const char **errmsg)
+{
+ pep_table_t *t;
+ mrp_domctl_watch_t *w;
+ int i;
+
+ proxy->name = mrp_strdup(name);
+ proxy->tables = mrp_allocz_array(typeof(*proxy->tables) , ntable);
+ proxy->ntable = ntable;
+ proxy->notify = true;
+
+ if (proxy->name == NULL || (ntable && proxy->tables == NULL)) {
+ *error = ENOMEM;
+ *errmsg = "failed to allocate proxy table";
+
+ return FALSE;
+ }
+
+ for (i = 0, t = proxy->tables; i < ntable; i++, t++) {
+ t->h = MQI_HANDLE_INVALID;
+ t->name = mrp_strdup(tables[i].table);
+ t->mql_columns = mrp_strdup(tables[i].mql_columns);
+ t->mql_index = mrp_strdup(tables[i].mql_index);
+
+ if (t->name == NULL || t->mql_columns == NULL || t->mql_index == NULL) {
+ mrp_log_error("Failed to allocate proxy table %s for %s.",
+ tables[i].table, name);
+ *error = ENOMEM;
+ *errmsg = "failed to allocate proxy table";
+
+ return FALSE;
+ }
+
+ if (create_proxy_table(t, error, errmsg))
+ mrp_log_info("Client %s created table %s.", proxy->name,
+ tables[i].table);
+ else {
+ mrp_log_error("Client %s failed to create table %s (%d: %s).",
+ proxy->name, tables[i].table, *error, *errmsg);
+ return FALSE;
+ }
+ }
+
+ for (i = 0, w = watches; i < nwatch; i++, w++) {
+ if (create_proxy_watch(proxy, i, w->table, w->mql_columns,
+ w->mql_where, w->max_rows, error, errmsg))
+ mrp_log_info("Client %s subscribed for table %s.", proxy->name,
+ w->table);
+ else
+ mrp_log_error("Client %s failed to subscribe for table %s.",
+ proxy->name, w->table);
+ }
+
+ return TRUE;
+}
+
+
+int unregister_proxy(pep_proxy_t *proxy)
+{
+ destroy_proxy(proxy);
+
+ return TRUE;
+}
+
+
+pep_proxy_t *find_proxy(pdp_t *pdp, const char *name)
+{
+ mrp_list_hook_t *p, *n;
+ pep_proxy_t *proxy;
+
+ mrp_list_foreach(&pdp->proxies, p, n) {
+ proxy = mrp_list_entry(p, typeof(*proxy), hook);
+
+ if (!strcmp(proxy->name, name))
+ return proxy;
+ }
+
+ return NULL;
+}
+
+
+uint32_t proxy_queue_pending(pep_proxy_t *proxy,
+ mrp_domain_return_cb_t return_cb, void *user_data)
+{
+ pending_t *pending;
+
+ if (return_cb == NULL)
+ return proxy->seqno++;
+
+ pending = mrp_allocz(sizeof(*pending));
+
+ if (pending == NULL)
+ return 0;
+
+ mrp_list_init(&pending->hook);
+
+ pending->id = proxy->seqno++;
+ pending->cb = return_cb;
+ pending->user_data = user_data;
+
+ mrp_list_append(&proxy->pending, &pending->hook);
+
+ return pending->id;
+}
+
+
+int proxy_dequeue_pending(pep_proxy_t *proxy, uint32_t id,
+ mrp_domain_return_cb_t *cbp, void **user_datap)
+{
+ mrp_list_hook_t *p, *n;
+ pending_t *pending;
+
+ mrp_list_foreach(&proxy->pending, p, n) {
+ pending = mrp_list_entry(p, typeof(*pending), hook);
+
+ if (pending->id == id) {
+ mrp_list_delete(&pending->hook);
+ *cbp = pending->cb;
+ *user_datap = pending->user_data;
+
+ mrp_free(pending);
+
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void purge_pending(pep_proxy_t *proxy)
+{
+ mrp_list_hook_t *p, *n;
+ pending_t *pending;
+
+ mrp_list_foreach(&proxy->pending, p, n) {
+ pending = mrp_list_entry(p, typeof(*pending), hook);
+
+ mrp_list_delete(&pending->hook);
+ mrp_free(pending);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_PROXY_H__
+#define __MURPHY_DOMAIN_CONTROL_PROXY_H__
+
+#include <murphy/core/domain.h>
+
+#include "domain-control-types.h"
+
+int init_proxies(pdp_t *pdp);
+void destroy_proxies(pdp_t *pdp);
+
+pep_proxy_t *create_proxy(pdp_t *pdp);
+void destroy_proxy(pep_proxy_t *proxy);
+
+int register_proxy(pep_proxy_t *proxy, char *name,
+ mrp_domctl_table_t *tables, int ntable,
+ mrp_domctl_watch_t *watches, int nwatch,
+ int *error, const char **errmsg);
+int unregister_proxy(pep_proxy_t *proxy);
+
+pep_proxy_t *find_proxy(pdp_t *pdp, const char *name);
+
+uint32_t proxy_queue_pending(pep_proxy_t *proxy,
+ mrp_domain_return_cb_t return_cb, void *user_data);
+int proxy_dequeue_pending(pep_proxy_t *proxy, uint32_t id,
+ mrp_domain_return_cb_t *cb, void **user_datap);
+
+#endif /* __MURPHY_DOMAIN_CONTROL_PROXY_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdarg.h>
+
+#include <murphy/common/debug.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+
+#include <murphy-db/mqi.h>
+#include <murphy-db/mql.h>
+#include <murphy-db/mdb.h>
+
+#include "domain-control.h"
+#include "table.h"
+
+#define FAIL(ec, msg) do { \
+ *errcode = ec; \
+ *errmsg = msg; \
+ goto fail; \
+ } while (0)
+
+static pep_table_t *lookup_watch_table(pdp_t *pdp, const char *name);
+
+/*
+ * proxied and tracked tables
+ */
+
+
+static void table_change_cb(mqi_event_t *e, void *tptr)
+{
+ static const char *events[] = {
+ "unknown (?)",
+ "column change",
+ "row insert",
+ "row delete",
+ "table create",
+ "table drop",
+ "transaction start (?)",
+ "transaction end (?)",
+ };
+ pep_table_t *t = (pep_table_t *)tptr;
+
+ if (!t->changed) {
+ t->changed = true;
+ mrp_debug("table '%s' changed by %s event", t->name, events[e->event]);
+ }
+}
+
+
+static int add_table_triggers(pep_table_t *t)
+{
+ mdb_table_t *tbl;
+ mqi_column_def_t cols[256];
+ int ncol, i;
+
+ if (t->h == MQI_HANDLE_INVALID) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ if ((tbl = mdb_table_find(t->name)) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((ncol = mdb_table_describe(tbl, &cols[0], sizeof(cols))) <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (mdb_trigger_add_row_callback(tbl, table_change_cb, t, NULL)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ for (i = 0; i < ncol; i++) {
+ if (mdb_trigger_add_column_callback(tbl, i, table_change_cb,
+ t, NULL) < 0) {
+ mdb_trigger_delete_row_callback(tbl, table_change_cb, t);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void del_table_triggers(pep_table_t *t)
+{
+ mdb_table_t *tbl;
+ mqi_column_def_t cols[256];
+ int ncol, i;
+
+ if (t->h == MQI_HANDLE_INVALID)
+ return;
+
+ if ((tbl = mdb_table_find(t->name)) == NULL)
+ return;
+
+ ncol = mdb_table_describe(tbl, &cols[0], sizeof(cols));
+
+ mdb_trigger_delete_row_callback(tbl, table_change_cb, t);
+
+ for (i = 0; i < ncol; i++)
+ mdb_trigger_delete_column_callback(tbl, i, table_change_cb, t);
+}
+
+
+static void table_event_cb(mqi_event_t *e, void *user_data)
+{
+ pdp_t *pdp = (pdp_t *)user_data;
+ const char *name = e->table.table.name;
+ mqi_handle_t h = e->table.table.handle;
+ pep_table_t *t;
+
+ switch (e->event) {
+ case mqi_table_created:
+ mrp_debug("table %s (0x%x) created", name, h);
+ break;
+
+ case mqi_table_dropped:
+ mrp_debug("table %s (0x%x) dropped", name, h);
+ break;
+
+ default:
+ return;
+ }
+
+ t = lookup_watch_table(pdp, name);
+
+ if (t != NULL) {
+ t->changed = true;
+
+ if (e->event == mqi_table_created) {
+ t->h = h;
+ add_table_triggers(t);
+ }
+ else {
+ t->h = MQI_HANDLE_INVALID;
+ del_table_triggers(t);
+ }
+ }
+
+ schedule_notification(pdp);
+}
+
+
+static void transaction_event_cb(mqi_event_t *e, void *user_data)
+{
+ pdp_t *pdp = (pdp_t *)user_data;
+ int depth = e->transact.depth;
+
+ switch (e->event) {
+ case mqi_transaction_end:
+ if (depth == 1) {
+ mrp_debug("outermost transaction ended");
+
+ if (pdp->ractive) {
+ mrp_debug("resolver active, delaying client notifications");
+ pdp->rblocked = true;
+ }
+ else
+ schedule_notification(pdp);
+ }
+ else
+ mrp_debug("nested transaction (#%d) ended", depth);
+ break;
+
+ case mqi_transaction_start:
+ if (depth == 1)
+ mrp_debug("outermost transaction started");
+ else
+ mrp_debug("nested transaction (#%d) started", depth);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static int open_db(pdp_t *pdp)
+{
+ static bool done = false;
+
+ if (done)
+ return TRUE;
+
+ if (mqi_open() == 0) {
+ if (mqi_create_transaction_trigger(transaction_event_cb, pdp) == 0 &&
+ mqi_create_table_trigger(table_event_cb, pdp) == 0) {
+ done = true;
+ return TRUE;
+ }
+
+ mqi_drop_transaction_trigger(transaction_event_cb, pdp);
+ }
+
+ return FALSE;
+}
+
+
+static void close_db(pdp_t *pdp)
+{
+ mqi_drop_table_trigger(table_event_cb, pdp);
+ mqi_drop_transaction_trigger(transaction_event_cb, pdp);
+}
+
+
+static void purge_watch_table_cb(void *key, void *entry);
+
+
+
+int init_tables(pdp_t *pdp)
+{
+ mrp_htbl_config_t hcfg;
+
+ if (open_db(pdp)) {
+ mrp_list_init(&pdp->tables);
+
+ mrp_clear(&hcfg);
+ hcfg.comp = mrp_string_comp;
+ hcfg.hash = mrp_string_hash;
+ hcfg.free = purge_watch_table_cb;
+
+ pdp->watched = mrp_htbl_create(&hcfg);
+ }
+
+ return (pdp->watched != NULL);
+}
+
+
+void destroy_tables(pdp_t *pdp)
+{
+ close_db(pdp);
+ mrp_htbl_destroy(pdp->watched, TRUE);
+
+ pdp->watched = NULL;
+}
+
+
+int exec_mql(mql_result_type_t type, mql_result_t **resultp,
+ const char *format, ...)
+{
+ mql_result_t *r;
+ char buf[4096];
+ va_list ap;
+ int success, n;
+
+ va_start(ap, format);
+ n = vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+
+ if (n < (int)sizeof(buf)) {
+ r = mql_exec_string(type, buf);
+ success = (r == NULL || mql_result_is_success(r));
+
+ if (resultp != NULL) {
+ *resultp = r;
+ return success;
+ }
+ else {
+ mql_result_free(r);
+ return success;
+ }
+ }
+ else {
+ errno = EOVERFLOW;
+ if (resultp != NULL)
+ *resultp = NULL;
+
+ return FALSE;
+ }
+}
+
+
+static int get_table_description(pep_table_t *t)
+{
+ mqi_column_def_t columns[MQI_COLUMN_MAX];
+ mrp_domctl_value_t *values = NULL;
+ int ncolumn, i;
+
+ if (t->h == MQI_HANDLE_INVALID)
+ t->h = mqi_get_table_handle((char *)t->name);
+
+ if (t->h != MQI_HANDLE_INVALID) {
+ ncolumn = mqi_describe(t->h, columns, MRP_ARRAY_SIZE(columns));
+
+ if (ncolumn > 0) {
+ t->columns = mrp_allocz_array(typeof(*t->columns), ncolumn);
+ t->coldesc = mrp_allocz_array(typeof(*t->coldesc), ncolumn + 1);
+
+ if (t->columns != NULL && t->coldesc != NULL) {
+ memcpy(t->columns, columns, ncolumn * sizeof(*t->columns));
+ t->ncolumn = ncolumn;
+
+ for (i = 0; i < t->ncolumn; i++) {
+ t->coldesc[i].cindex = i;
+ t->coldesc[i].offset = (int)(ptrdiff_t)&values[i].str;
+ }
+
+ t->coldesc[i].cindex = -1;
+ t->coldesc[i].offset = 0;
+
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
+
+int create_proxy_table(pep_table_t *t, int *errcode, const char **errmsg)
+{
+ mrp_list_init(&t->hook);
+ mrp_list_init(&t->watches);
+
+ if (mqi_get_table_handle((char *)t->name) != MQI_HANDLE_INVALID)
+ FAIL(EEXIST, "DB error: table already exists");
+
+ if (exec_mql(mql_result_dontcare, NULL,
+ "create temporary table %s (%s)", t->name, t->mql_columns)) {
+ if (t->mql_index && t->mql_index[0]) {
+ if (!exec_mql(mql_result_dontcare, NULL,
+ "create index on %s (%s)", t->name, t->mql_index))
+ FAIL(EINVAL, "DB error: failed to create table index");
+ }
+
+ if (!get_table_description(t))
+ FAIL(EINVAL, "DB error: failed to get table description");
+
+ return TRUE;
+ }
+ else
+ FAIL(ENOMEM, "DB error: failed to create table");
+
+ fail:
+ return FALSE;
+}
+
+
+void destroy_proxy_table(pep_table_t *t)
+{
+ mrp_debug("destroying table %s", t->name ? t->name : "<unknown>");
+
+ if (t->h != MQI_HANDLE_INVALID)
+ mqi_drop_table(t->h);
+
+ mrp_free(t->mql_columns);
+ mrp_free(t->mql_index);
+
+ mrp_free(t->columns);
+ mrp_free(t->coldesc);
+ mrp_free(t->name);
+
+ t->name = NULL;
+ t->h = MQI_HANDLE_INVALID;
+ t->columns = NULL;
+ t->ncolumn = 0;
+}
+
+
+void destroy_proxy_tables(pep_proxy_t *proxy)
+{
+ mqi_handle_t tx;
+ int i;
+
+ mrp_debug("destroying tables of client %s", proxy->name);
+
+ tx = mqi_begin_transaction();
+ for (i = 0; i < proxy->ntable; i++)
+ destroy_proxy_table(proxy->tables + i);
+ mqi_commit_transaction(tx);
+
+ proxy->tables = NULL;
+ proxy->ntable = 0;
+}
+
+
+pep_table_t *create_watch_table(pdp_t *pdp, const char *name)
+{
+ pep_table_t *t;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ mrp_list_init(&t->hook);
+ mrp_list_init(&t->watches);
+
+ t->h = MQI_HANDLE_INVALID;
+ t->name = mrp_strdup(name);
+
+ if (t->name == NULL)
+ goto fail;
+
+ get_table_description(t);
+
+ if (t->h != MQI_HANDLE_INVALID)
+ add_table_triggers(t);
+
+ if (!mrp_htbl_insert(pdp->watched, t->name, t))
+ goto fail;
+
+ mrp_list_append(&pdp->tables, &t->hook);
+ }
+
+ return t;
+
+ fail:
+ destroy_watch_table(pdp, t);
+
+ return FALSE;
+}
+
+
+static void destroy_table_watches(pep_table_t *t)
+{
+ pep_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ if (t != NULL) {
+ del_table_triggers(t);
+
+ mrp_list_foreach(&t->watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), tbl_hook);
+
+ mrp_list_delete(&w->tbl_hook);
+ mrp_list_delete(&w->pep_hook);
+
+ mrp_free(w->mql_columns);
+ mrp_free(w->mql_where);
+ mrp_free(w);
+ }
+ }
+}
+
+
+void destroy_watch_table(pdp_t *pdp, pep_table_t *t)
+{
+ mrp_list_delete(&t->hook);
+ t->h = MQI_HANDLE_INVALID;
+
+ if (pdp != NULL)
+ mrp_htbl_remove(pdp->watched, t->name, FALSE);
+
+ destroy_table_watches(t);
+}
+
+
+static pep_table_t *lookup_watch_table(pdp_t *pdp, const char *name)
+{
+ return mrp_htbl_lookup(pdp->watched, (void *)name);
+}
+
+
+static void purge_watch_table_cb(void *key, void *entry)
+{
+ pep_table_t *t = (pep_table_t *)entry;
+
+ MRP_UNUSED(key);
+
+ destroy_watch_table(NULL, t);
+}
+
+
+int create_proxy_watch(pep_proxy_t *proxy, int id,
+ const char *table, const char *mql_columns,
+ const char *mql_where, int max_rows,
+ int *error, const char **errmsg)
+{
+ pdp_t *pdp = proxy->pdp;
+ pep_table_t *t;
+ pep_watch_t *w;
+
+ t = lookup_watch_table(pdp, table);
+
+ if (t == NULL) {
+ t = create_watch_table(pdp, table);
+
+ if (t == NULL) {
+ *error = EINVAL;
+ *errmsg = "failed to watch table";
+ }
+ }
+
+ w = mrp_allocz(sizeof(*w));
+
+ if (w != NULL) {
+ mrp_list_init(&w->tbl_hook);
+ mrp_list_init(&w->pep_hook);
+
+ w->table = t;
+ w->mql_columns = mrp_strdup(mql_columns);
+ w->mql_where = mrp_strdup(mql_where ? mql_where : "");
+ w->max_rows = max_rows;
+ w->proxy = proxy;
+ w->id = id;
+ w->notify = true;
+
+ if (w->mql_columns == NULL || w->mql_where == NULL)
+ goto fail;
+
+ mrp_list_append(&t->watches, &w->tbl_hook);
+ mrp_list_append(&proxy->watches, &w->pep_hook);
+
+ return TRUE;
+ }
+ else {
+ *error = ENOMEM;
+ *errmsg = "failed to allocate table watch";
+ }
+
+ fail:
+ if (w != NULL) {
+ mrp_free(w->mql_columns);
+ mrp_free(w->mql_where);
+ mrp_free(w);
+ }
+
+ return FALSE;
+}
+
+
+void destroy_proxy_watches(pep_proxy_t *proxy)
+{
+ pep_watch_t *w;
+ mrp_list_hook_t *p, *n;
+
+ if (proxy != NULL) {
+ mrp_list_foreach(&proxy->watches, p, n) {
+ w = mrp_list_entry(p, typeof(*w), pep_hook);
+
+ mrp_list_delete(&w->tbl_hook);
+ mrp_list_delete(&w->pep_hook);
+
+ mrp_free(w);
+ }
+ }
+}
+
+
+static void reset_proxy_tables(pep_proxy_t *proxy)
+{
+ int i;
+
+ for (i = 0; i < proxy->ntable; i++)
+ mqi_delete_from(proxy->tables[i].h, NULL);
+}
+
+
+static int insert_into_table(pep_table_t *t,
+ mrp_domctl_value_t **rows, int nrow)
+{
+ void *data[2];
+ int i;
+
+ data[1] = NULL;
+
+ for (i = 0; i < nrow; i++) {
+ data[0] = rows[i];
+ if (mqi_insert_into(t->h, 0, t->coldesc, data) != 1)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+int set_proxy_tables(pep_proxy_t *proxy, mrp_domctl_data_t *tables, int ntable,
+ int *error, const char **errmsg)
+{
+ mqi_handle_t tx;
+ pep_table_t *t;
+ int i, id;
+
+ tx = mqi_begin_transaction();
+
+ if (tx != MQI_HANDLE_INVALID) {
+ reset_proxy_tables(proxy);
+
+ for (i = 0; i < ntable; i++) {
+ id = tables[i].id;
+
+ if (id < 0 || id >= proxy->ntable)
+ goto fail;
+
+ t = proxy->tables + id;
+
+ if (tables[i].ncolumn != t->ncolumn)
+ goto fail;
+
+#if 0
+ if (!delete_from_table(t, tables[i].rows, tables[i].nrow))
+ goto fail;
+#endif
+
+ if (!insert_into_table(t, tables[i].rows, tables[i].nrow))
+ goto fail;
+
+
+ }
+
+ mqi_commit_transaction(tx);
+
+ return TRUE;
+
+ fail:
+ *error = EINVAL;
+ *errmsg = "failed to set tables";
+ mqi_rollback_transaction(tx);
+ }
+
+ return FALSE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DOMAIN_CONTROL_TABLE_H__
+#define __MURPHY_DOMAIN_CONTROL_TABLE_H__
+
+#include <murphy-db/mql-result.h>
+
+#include "client.h"
+#include "domain-control-types.h"
+
+int init_tables(pdp_t *pdp);
+void destroy_tables(pdp_t *pdp);
+
+int create_proxy_table(pep_table_t *t, int *errcode, const char **errmsg);
+
+int create_proxy_watch(pep_proxy_t *proxy, int id,
+ const char *table, const char *mql_columns,
+ const char *mql_where, int max_rows,
+ int *error, const char **errmsg);
+
+void destroy_watch_table(pdp_t *pdp, pep_table_t *t);
+
+void destroy_proxy_table(pep_table_t *t);
+void destroy_proxy_tables(pep_proxy_t *proxy);
+
+void destroy_proxy_watches(pep_proxy_t *proxy);
+
+int set_proxy_tables(pep_proxy_t *proxy, mrp_domctl_data_t *tables, int ntable,
+ int *error, const char **errmsg);
+
+int exec_mql(mql_result_type_t type, mql_result_t **resultp,
+ const char *format, ...);
+
+
+#endif /* __MURPHY_DOMAIN_CONTROL_TABLE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <alloca.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <breedline/breedline-murphy.h>
+
+#include "client.h"
+
+#define DEFAULT_PROMPT "test-controller"
+
+
+/*
+ * client context
+ */
+
+typedef struct {
+ const char *addrstr; /* server address */
+ int zone; /* run in zone control mode */
+ int verbose; /* verbose mode */
+ int audio; /* subscribe for audio_playback_* */
+ mrp_mainloop_t *ml; /* murphy mainloop */
+ void *dc; /* domain controller */
+ brl_t *brl; /* breedline for terminal input */
+} client_t;
+
+
+#define NVALUE 512
+
+
+/*
+ * device and stream definitions
+ */
+
+#define NDEVICE (MRP_ARRAY_SIZE(devices) - 1)
+#define DEVICE_NCOLUMN 4
+
+typedef struct {
+ const char *name;
+ const char *type;
+ int public;
+ int available;
+} device_t;
+
+static device_t devices[] = {
+ { "builtin-speaker" , "speaker" , TRUE , TRUE },
+ { "builtin-earpiece", "speaker" , FALSE, TRUE },
+ { "usb-speaker" , "speaker" , TRUE , FALSE },
+ { "a2dp-speaker" , "speaker" , TRUE , FALSE },
+ { "wired-headset" , "headset" , FALSE, FALSE },
+ { "usb-headphone" , "headphone", FALSE, FALSE },
+ { "a2dp-headphone" , "headphone", FALSE, FALSE },
+ { "sco-headset" , "headset" , FALSE, FALSE },
+ { NULL , NULL , FALSE, FALSE }
+};
+
+#define NSTREAM (MRP_ARRAY_SIZE(streams) - 1)
+#define STREAM_NCOLUMN 4
+
+typedef struct {
+ const char *name;
+ const char *role;
+ pid_t owner;
+ int playing;
+} stream_t;
+
+static stream_t streams[] = {
+ { "player1", "player" , 1234, FALSE },
+ { "player2", "player" , 4321, FALSE },
+ { "navit" , "navigator", 5432, FALSE },
+ { "phone" , "call" , 6666, FALSE },
+ { NULL , NULL , 0 , FALSE }
+};
+
+
+/*
+ * device and stream descriptors
+ */
+
+#define DEVICE_COLUMNS \
+ "name varchar(32), " \
+ "type varchar(32), " \
+ "public integer , " \
+ "available integer"
+
+#define DEVICE_INDEX "name"
+
+#define DEVICE_SELECT "*"
+
+#define DEVICE_WHERE NULL
+
+#define STREAM_COLUMNS \
+ "name varchar(32)," \
+ "role varchar(32)," \
+ "owner unsigned ," \
+ "playing integer"
+
+#define STREAM_INDEX "name"
+
+#define STREAM_SELECT "*"
+#define STREAM_WHERE NULL
+
+#define SELECT_ALL "*"
+#define ANY_WHERE NULL
+
+mrp_domctl_table_t media_tables[] = {
+ MRP_DOMCTL_TABLE("test-devices", DEVICE_COLUMNS, DEVICE_INDEX),
+ MRP_DOMCTL_TABLE("test-streams", STREAM_COLUMNS, STREAM_INDEX),
+};
+
+mrp_domctl_watch_t media_watches[] = {
+ MRP_DOMCTL_WATCH("test-devices", DEVICE_SELECT, DEVICE_WHERE, 0),
+ MRP_DOMCTL_WATCH("test-streams", STREAM_SELECT, STREAM_WHERE, 0),
+ MRP_DOMCTL_WATCH("audio_playback_owner", SELECT_ALL, ANY_WHERE, 0),
+ MRP_DOMCTL_WATCH("audio_playback_users", SELECT_ALL, ANY_WHERE, 0),
+};
+
+
+/*
+ * zone and call definitions
+ */
+
+#define NZONE (MRP_ARRAY_SIZE(zones) - 1)
+#define ZONE_NCOLUMN 3
+
+typedef struct {
+ const char *name;
+ int occupied;
+ int active;
+} zone_t;
+
+static zone_t zones[] = {
+ { "driver" , TRUE , FALSE },
+ { "fearer" , FALSE, TRUE },
+ { "back-left" , TRUE , FALSE },
+ { "back-center", FALSE, FALSE },
+ { "back-right" , TRUE , TRUE },
+ { NULL , FALSE, FALSE }
+};
+
+
+#define NCALL (MRP_ARRAY_SIZE(calls) - 1)
+#define CALL_NCOLUMN 3
+
+typedef struct {
+ int id;
+ const char *state;
+ const char *modem;
+} call_t;
+
+static call_t calls[] = {
+ { 1, "active" , "modem1" },
+ { 2, "ringing" , "modem1" },
+ { 3, "held" , "modem2" },
+ { 4, "alerting", "modem2" },
+ { 0, NULL , NULL }
+};
+
+
+/*
+ * zone and call descriptors
+ */
+
+#define ZONE_COLUMNS \
+ "name varchar(32), " \
+ "occupied integer , " \
+ "active integer"
+
+#define ZONE_INDEX "name"
+
+#define ZONE_SELECT "*"
+
+#define ZONE_WHERE NULL
+
+#define CALL_COLUMNS \
+ "id integer , " \
+ "state varchar(32), " \
+ "modem varchar(32)"
+
+#define CALL_INDEX "id"
+
+#define CALL_SELECT "*"
+
+#define CALL_WHERE NULL
+
+mrp_domctl_table_t zone_tables[] = {
+ MRP_DOMCTL_TABLE("test-zones", ZONE_COLUMNS, ZONE_INDEX),
+ MRP_DOMCTL_TABLE("test-calls", CALL_COLUMNS, CALL_INDEX),
+};
+
+mrp_domctl_watch_t zone_watches[] = {
+ MRP_DOMCTL_WATCH("test-zones", ZONE_SELECT, ZONE_WHERE, 0),
+ MRP_DOMCTL_WATCH("test-calls", CALL_SELECT, CALL_WHERE, 0),
+ MRP_DOMCTL_WATCH("audio_playback_owner", SELECT_ALL, ANY_WHERE, 0),
+ MRP_DOMCTL_WATCH("audio_playback_users", SELECT_ALL, ANY_WHERE, 0),
+};
+
+mrp_domctl_table_t *exports;
+int nexport;
+mrp_domctl_watch_t *imports;
+int nimport;
+
+
+static client_t *client;
+
+
+static void fatal_msg(int error, const char *format, ...);
+static void error_msg(const char *format, ...);
+static void info_msg(const char *format, ...);
+
+static void export_data(client_t *c);
+
+
+static void plug_device(client_t *c, const char *name, int plug)
+{
+ device_t *d;
+ int changed;
+
+ if (c->zone) {
+ error_msg("cannot plug/unplug, client is in zone mode");
+ return;
+ }
+
+ changed = FALSE;
+
+ for (d = devices; d->name != NULL; d++) {
+ if (!strcmp(d->name, name)) {
+ changed = plug ^ d->available;
+ d->available = plug;
+ break;
+ }
+ }
+
+ if (changed) {
+ info_msg("device '%s' is now %splugged", d->name, plug ? "" : "un");
+ export_data(c);
+ }
+}
+
+
+static void list_devices(void)
+{
+ device_t *d;
+ int n;
+
+ for (d = devices, n = 0; d->name != NULL; d++, n++) {
+ info_msg("device '%s': (%s, %s), %s",
+ d->name, d->type, d->public ? "public" : "private",
+ d->available ? "available" : "currently unplugged");
+ }
+
+ if (n == 0)
+ info_msg("devices: none");
+}
+
+
+static void play_stream(client_t *c, const char *name, int play)
+{
+ stream_t *s;
+ int changed;
+
+ if (c->zone) {
+ error_msg("cannot control streams, client is in zone mode");
+ return;
+ }
+
+ changed = FALSE;
+
+ for (s = streams; s->name != NULL; s++) {
+ if (!strcmp(s->name, name)) {
+ changed = play ^ s->playing;
+ s->playing = play;
+ break;
+ }
+ }
+
+ if (changed) {
+ info_msg("stream '%s' is now %s", s->name, play ? "playing":"stopped");
+ export_data(c);
+ }
+}
+
+
+static void list_streams(void)
+{
+ stream_t *s;
+ int n;
+
+ for (s = streams, n = 0; s->name != NULL; s++, n++) {
+ info_msg("stream '%s': role %s, owner %u, currently %splaying",
+ s->name, s->role, s->owner, s->playing ? "" : "not ");
+ }
+
+ if (n == 0)
+ info_msg("streams: none");
+}
+
+
+static void set_zone_state(client_t *c, const char *config)
+{
+ zone_t *z;
+ int occupied, active, changed, len;
+ char name[256], *end;
+
+ if (!c->zone) {
+ error_msg("cannot control zones, client is not in zone mode");
+ return;
+ }
+
+ while (*config == ' ' || *config == '\t')
+ config++;
+
+ end = strchr(config, ' ');
+ if (end == NULL)
+ return;
+
+ len = end - config;
+ strncpy(name, config, len);
+ name[len] = '\0';
+
+ config = end + 1;
+ while (*config == ' ' || *config == '\t')
+ config++;
+
+ occupied = FALSE;
+ active = FALSE;
+ changed = FALSE;
+
+ if (strstr(config, "occupied"))
+ occupied = TRUE;
+ if (strstr(config, "active"))
+ active = TRUE;
+
+ for (z = zones; z->name != NULL; z++) {
+ if (!strcmp(z->name, name)) {
+ changed = (active ^ z->active) | (occupied ^ z->occupied);
+ z->active = active;
+ z->occupied = occupied;
+ break;
+ }
+ }
+
+ if (changed) {
+ info_msg("zone '%s' is now %s and %s", z->name,
+ z->occupied ? "occupied" : "free",
+ z->active ? "active" : "idle");
+ export_data(c);
+ }
+}
+
+
+static void list_zones(void)
+{
+ zone_t *z;
+ int n;
+
+ for (z = zones, n = 0; z->name != NULL; z++, n++) {
+ info_msg("zone '%s' is now %s and %s", z->name,
+ z->occupied ? "occupied" : "free",
+ z->active ? "active" : "idle");
+ }
+
+ if (n == 0)
+ info_msg("zones: none");
+}
+
+
+static void set_call_state(client_t *c, const char *config)
+{
+ call_t *call;
+ char idstr[64], *state, *end;
+ int id, changed, len;
+
+ if (!c->zone) {
+ error_msg("cannot control calls, client is not in zone mode");
+ return;
+ }
+
+ while (*config == ' ' || *config == '\t')
+ config++;
+
+ end = strchr(config, ' ');
+ if (end == NULL)
+ return;
+
+ len = end - config;
+ strncpy(idstr, config, len);
+ idstr[len] = '\0';
+
+ config = end + 1;
+ while (*config == ' ' || *config == '\t')
+ config++;
+ state = (char *)config;
+
+ id = strtoul(idstr, &end, 10);
+
+ if (end && *end) {
+ error_msg("invalid call id '%s'", idstr);
+ return;
+ }
+
+ changed = FALSE;
+ for (call = calls; call->id > 0; call++) {
+ if (call->id == id) {
+ if (strcmp(call->state, state)) {
+ mrp_free((char *)call->state);
+ call->state = mrp_strdup(state);
+ changed = TRUE;
+ break;
+ }
+ }
+ }
+
+ if (changed) {
+ info_msg("call #%d is now %s", call->id, call->state);
+ export_data(c);
+ }
+}
+
+
+static void list_calls(void)
+{
+ call_t *c;
+ int n;
+
+ for (c = calls, n = 0; c->id > 0; c++, n++) {
+ info_msg("call #%d: %s (on modem %s)", c->id, c->state, c->modem);
+ }
+
+ if (n == 0)
+ info_msg("calls: none");
+}
+
+
+static void init_devices(void)
+{
+ mrp_clear(&devices);
+}
+
+
+static void reset_devices(void)
+{
+ int i;
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(devices); i++) {
+ mrp_free((char *)devices[i].name);
+ mrp_free((char *)devices[i].type);
+ }
+
+ mrp_clear(&devices);
+}
+
+
+void update_devices(mrp_domctl_data_t *data)
+{
+ device_t *d;
+ mrp_domctl_value_t *v;
+ int i;
+
+ if (data->nrow != 0 && data->ncolumn != DEVICE_NCOLUMN) {
+ error_msg("incorrect number of columns in device update (%d != %d)",
+ data->ncolumn, DEVICE_NCOLUMN);
+ return;
+ }
+
+ if (data->nrow > (int)NDEVICE) {
+ error_msg("too many rows (%d) in device update", data->nrow);
+ return;
+ }
+
+ if (data->nrow == 0)
+ reset_devices();
+ else {
+ d = devices;
+
+ for (i = 0; i < data->nrow; i++) {
+ mrp_free((char *)d->name);
+ mrp_free((char *)d->type);
+
+ v = data->rows[i];
+ d->name = mrp_strdup(v[0].str);
+ d->type = mrp_strdup(v[1].str);
+ d->public = v[2].s32;
+ d->available = v[3].s32;
+
+ d += 1;
+ }
+ }
+
+ list_devices();
+}
+
+
+static void init_streams(void)
+{
+ mrp_clear(&streams);
+}
+
+
+static void reset_streams(void)
+{
+ int i;
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(streams); i++) {
+ mrp_free((char *)streams[i].name);
+ mrp_free((char *)streams[i].role);
+ }
+
+ mrp_clear(&streams);
+}
+
+
+void update_streams(mrp_domctl_data_t *data)
+{
+ stream_t *s;
+ mrp_domctl_value_t *v;
+ int i;
+
+ if (data->nrow != 0 && data->ncolumn != STREAM_NCOLUMN) {
+ error_msg("incorrect number of columns in stream update (%d != %d)",
+ data->ncolumn, STREAM_NCOLUMN);
+ return;
+ }
+
+ if (data->nrow > (int)NSTREAM) {
+ error_msg("too many rows (%d) in stream update", data->nrow);
+ return;
+ }
+
+ if (data->nrow == 0)
+ reset_streams();
+ else {
+ s = streams;
+
+ for (i = 0; i < data->nrow; i++) {
+ mrp_free((char *)s->name);
+ mrp_free((char *)s->role);
+
+ v = data->rows[i];
+ s->name = mrp_strdup(v[0].str);
+ s->role = mrp_strdup(v[1].str);
+ s->owner = v[2].u32;
+ s->playing = v[3].s32;
+
+ s += 1;
+ }
+ }
+
+ list_streams();
+}
+
+
+static void init_zones(void)
+{
+ mrp_clear(&zones);
+}
+
+
+static void reset_zones(void)
+{
+ int i;
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(zones); i++)
+ mrp_free((char *)zones[i].name);
+
+ mrp_clear(&zones);
+}
+
+
+void update_zones(mrp_domctl_data_t *data)
+{
+ zone_t *z;
+ mrp_domctl_value_t *v;
+ int i;
+
+ if (data->nrow != 0 && data->ncolumn != ZONE_NCOLUMN) {
+ error_msg("incorrect number of columns in zone update (%d != %d)",
+ data->ncolumn, ZONE_NCOLUMN);
+ return;
+ }
+
+ if (data->nrow > (int)NZONE) {
+ error_msg("too many rows (%d) in zone update", data->nrow);
+ return;
+ }
+
+ if (data->nrow == 0)
+ reset_zones();
+ else {
+ z = zones;
+
+ for (i = 0; i < data->nrow; i++) {
+ mrp_free((char *)z->name);
+
+ v = data->rows[i];
+ z->name = mrp_strdup(v[0].str);
+ z->occupied = v[1].s32;
+ z->active = v[2].s32;
+
+ z += 1;
+ }
+ }
+
+ list_zones();
+}
+
+
+static void init_calls(void)
+{
+ mrp_clear(&calls);
+}
+
+
+static void reset_calls(void)
+{
+ int i;
+
+ for (i = 0; i < (int)MRP_ARRAY_SIZE(calls); i++) {
+ mrp_free((char *)calls[i].state);
+ mrp_free((char *)calls[i].modem);
+ }
+
+ mrp_clear(&calls);
+}
+
+
+void update_calls(mrp_domctl_data_t *data)
+{
+ call_t *c;
+ mrp_domctl_value_t *v;
+ int i;
+
+ if (data->nrow != 0 && data->ncolumn != CALL_NCOLUMN) {
+ error_msg("incorrect number of columns in call update (%d != %d)",
+ data->ncolumn, CALL_NCOLUMN);
+ return;
+ }
+
+ if (data->nrow > (int)NCALL) {
+ error_msg("too many rows (%d) in call update", data->nrow);
+ return;
+ }
+
+ if (data->nrow == 0)
+ reset_calls();
+ else {
+ c = calls;
+
+ for (i = 0; i < data->nrow; i++) {
+ mrp_free((char *)c->state);
+ mrp_free((char *)c->modem);
+
+ v = data->rows[i];
+ c->id = v[0].s32;
+ c->state = mrp_strdup(v[1].str);
+ c->modem = mrp_strdup(v[2].str);
+
+ c += 1;
+ }
+ }
+
+ list_calls();
+}
+
+
+void update_imports(client_t *c, mrp_domctl_data_t *data, int ntable)
+{
+ int i;
+
+ MRP_UNUSED(ntable);
+
+ for (i = 0; i < 2; i++) {
+ if (c->zone) {
+ if (data[i].id == 0)
+ update_devices(data + i);
+ else
+ update_streams(data + i);
+ }
+ else {
+ if (data[i].id == 0)
+ update_zones(data + i);
+ else
+ update_calls(data + i);
+ }
+ }
+}
+
+
+static int ping_cb(mrp_domctl_t *dc, uint32_t narg, mrp_domctl_arg_t *args,
+ uint32_t *nout, mrp_domctl_arg_t *outs, void *user_data)
+{
+ client_t *c = (client_t *)user_data;
+ int i;
+
+ MRP_UNUSED(dc);
+ MRP_UNUSED(c);
+
+ info_msg("pinged with %d arguments", narg);
+
+ for (i = 0; i < (int)narg; i++) {
+ switch (args[i].type) {
+ case MRP_DOMCTL_STRING:
+ info_msg(" #%d: %s", i, args[i].str);
+ break;
+ case MRP_DOMCTL_UINT32:
+ info_msg(" #%d: %u", i, args[i].u32);
+ break;
+ default:
+ if (MRP_DOMCTL_IS_ARRAY(args[i].type)) {
+ uint32_t j;
+
+ info_msg(" #%d: array of %u items:", i, args[i].size);
+ for (j = 0; j < args[i].size; j++) {
+ switch (MRP_DOMCTL_ARRAY_TYPE(args[i].type)) {
+ case MRP_DOMCTL_STRING:
+ info_msg(" #%d: '%s'", j,
+ ((char **)args[i].arr)[j]);
+ break;
+ case MRP_DOMCTL_UINT32:
+ info_msg(" #%d: %u", j,
+ ((uint32_t *)args[i].arr)[j]);
+ break;
+ default:
+ info_msg(" #%d: <type 0x%x", j,
+ MRP_DOMCTL_ARRAY_TYPE(args[i].type));
+ break;
+ }
+ }
+ }
+ else
+ info_msg(" <type 0x%x>", args[i].type);
+ }
+ }
+
+
+ for (i = 0; i < (int)*nout; i++) {
+ if (i < (int)narg) {
+ if (MRP_DOMCTL_IS_ARRAY(args[i].type)) {
+ int j;
+
+ if (i & 0x1) {
+ outs[i].type = MRP_DOMCTL_ARRAY(STRING);
+ outs[i].arr = mrp_allocz(sizeof(char *) * 5);
+ for (j = 0; j < 5; j++) {
+ char entry[32];
+ snprintf(entry, sizeof(entry), "xyzzy #%d.%d", i, j);
+ ((char **)outs[i].arr)[j] = mrp_strdup(entry);
+ }
+ outs[i].size = 5;
+ }
+ else {
+ outs[i].type = MRP_DOMCTL_ARRAY(UINT32);
+ outs[i].arr = mrp_allocz(sizeof(uint32_t) * 5);
+ for (j = 0; j < 5; j++)
+ ((uint32_t*)outs[i].arr)[j] = 3141 + i * j;
+ outs[i].size = 5;
+ }
+ }
+ else {
+ outs[i] = args[i];
+
+ if (outs[i].type == MRP_DOMCTL_STRING)
+ outs[i].str = mrp_strdup(outs[i].str);
+ }
+ }
+ else {
+ outs[i].type = MRP_DOMCTL_UINT32;
+ outs[i].u32 = i;
+ }
+ }
+
+ return 0;
+}
+
+
+void init_methods(client_t *c)
+{
+ mrp_domctl_method_def_t methods[] = {
+ { "ping", 32, ping_cb, c },
+ };
+ int nmethod = MRP_ARRAY_SIZE(methods);
+
+ mrp_domctl_register_methods(c->dc, methods, nmethod);
+}
+
+
+static void show_help(void)
+{
+#define P info_msg
+
+ P("Available commands:");
+ P(" help show this help");
+ P(" list list all data");
+ P(" list {devices|streams|zones|calls} list the requested data");
+ P(" plug <device> update <device> as plugged");
+ P(" unplug <device> update <device> as unplugged");
+ P(" play <stream> update <stream> as playing");
+ P(" stop <stream> update <stream> as stopped");
+ P(" call <call> <state> update state of <call>");
+ P(" zone <zone> [occupied,[active]] update state of <zone>");
+
+#undef P
+}
+
+
+static void input_cb(brl_t *brl, const char *input, void *user_data)
+{
+ int len;
+
+ MRP_UNUSED(user_data);
+
+ brl_add_history(brl, input);
+
+ if (input == NULL || !strcmp(input, "exit")) {
+ brl_destroy(brl);
+ exit(0);
+ }
+ else if (!strcmp(input, "help")) {
+ show_help();
+ }
+ else if (!strcmp(input, "list")) {
+ list_devices();
+ list_streams();
+ list_zones();
+ list_calls();
+ }
+ else if (!strcmp(input, "list devices"))
+ list_devices();
+ else if (!strcmp(input, "list streams"))
+ list_streams();
+ else if (!strcmp(input, "list zones"))
+ list_zones();
+ else if (!strcmp(input, "list calls"))
+ list_calls();
+ else if (!strncmp(input, "plug " , len=sizeof("plug ") - 1) ||
+ !strncmp(input, "unplug ", len=sizeof("unplug ") - 1)) {
+ plug_device(client, input + len, *input == 'p');
+ }
+ else if (!strncmp(input, "play " , len=sizeof("play ") - 1) ||
+ !strncmp(input, "stop ", len=sizeof("stop ") - 1)) {
+ play_stream(client, input + len, *input == 'p');
+ }
+ else if (!strncmp(input, "call " , len=sizeof("call ") - 1)) {
+ set_call_state(client, input + len);
+ }
+ else if (!strncmp(input, "zone " , len=sizeof("zone ") - 1)) {
+ set_zone_state(client, input + len);
+ }
+}
+
+
+static void terminal_setup(client_t *c)
+{
+ int fd;
+ const char *prompt;
+
+ fd = fileno(stdin);
+ prompt = DEFAULT_PROMPT;
+ c->brl = brl_create_with_murphy(fd, prompt, c->ml, input_cb, c);
+
+ if (c->brl != NULL) {
+ brl_show_prompt(c->brl);
+ }
+ else {
+ mrp_log_error("Failed to breedline for console input.");
+ exit(1);
+ }
+}
+
+
+static void terminal_cleanup(client_t *c)
+{
+ if (c->brl != NULL) {
+ brl_destroy(c->brl);
+ c->brl = NULL;
+ }
+}
+
+
+static void fatal_msg(int error, const char *format, ...)
+{
+ va_list ap;
+
+ if (client && client->brl)
+ brl_hide_prompt(client->brl);
+
+ fprintf(stderr, "fatal error: ");
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+
+ exit(error);
+}
+
+
+static void error_msg(const char *format, ...)
+{
+ va_list ap;
+
+ if (client && client->brl)
+ brl_hide_prompt(client->brl);
+
+ fprintf(stderr, "error: ");
+ va_start(ap, format);
+ vfprintf(stderr, format, ap);
+ va_end(ap);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+
+ if (client && client->brl)
+ brl_show_prompt(client->brl);
+}
+
+
+static void info_msg(const char *format, ...)
+{
+ va_list ap;
+
+ if (client && client->brl)
+ brl_hide_prompt(client->brl);
+
+ va_start(ap, format);
+ vfprintf(stdout, format, ap);
+ va_end(ap);
+ fprintf(stdout, "\n");
+ fflush(stdout);
+
+ if (client && client->brl)
+ brl_show_prompt(client->brl);
+}
+
+
+static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+
+ MRP_UNUSED(user_data);
+
+ switch (signum) {
+ case SIGINT:
+ info_msg("Got SIGINT, stopping...");
+ if (ml != NULL)
+ mrp_mainloop_quit(ml, 0);
+ else
+ exit(0);
+ break;
+ }
+}
+
+
+static void connect_notify(mrp_domctl_t *dc, int connected, int errcode,
+ const char *errmsg, void *user_data)
+{
+ MRP_UNUSED(dc);
+ MRP_UNUSED(user_data);
+
+ if (connected) {
+ info_msg("Successfully registered to server.");
+ export_data(client);
+ }
+ else
+ error_msg("No connection to server (%d: %s).", errcode, errmsg);
+}
+
+
+static void dump_data(mrp_domctl_data_t *table)
+{
+ mrp_domctl_value_t *row;
+ int i, j;
+ char buf[1024], *p;
+ const char *t;
+ int n, l;
+
+ info_msg("Table #%d: %d rows x %d columns", table->id,
+ table->nrow, table->ncolumn);
+
+ for (i = 0; i < table->nrow; i++) {
+ row = table->rows[i];
+ p = buf;
+ n = sizeof(buf);
+
+ for (j = 0, t = ""; j < table->ncolumn; j++, t = ", ") {
+ switch (row[j].type) {
+ case MRP_DOMCTL_STRING:
+ l = snprintf(p, n, "%s'%s'", t, row[j].str);
+ p += l;
+ n -= l;
+ break;
+ case MRP_DOMCTL_INTEGER:
+ l = snprintf(p, n, "%s%d", t, row[j].s32);
+ p += l;
+ n -= l;
+ break;
+ case MRP_DOMCTL_UNSIGNED:
+ l = snprintf(p, n, "%s%u", t, row[j].u32);
+ p += l;
+ n -= l;
+ break;
+ case MRP_DOMCTL_DOUBLE:
+ l = snprintf(p, n, "%s%f", t, row[j].dbl);
+ p += l;
+ n -= l;
+ break;
+ default:
+ l = snprintf(p, n, "%s<invalid column 0x%x>",
+ t, row[j].type);
+ p += l;
+ n -= l;
+ }
+ }
+
+ info_msg("row #%d: { %s }", i, buf);
+ }
+}
+
+
+static void data_notify(mrp_domctl_t *dc, mrp_domctl_data_t *tables,
+ int ntable, void *user_data)
+{
+ client_t *client = (client_t *)user_data;
+
+ MRP_UNUSED(dc);
+
+ if (client->verbose) {
+ int i;
+
+ for (i = 0; i < ntable; i++) {
+ dump_data(tables + i);
+ }
+ }
+
+ update_imports(client, tables, ntable);
+}
+
+
+static void export_notify(mrp_domctl_t *dc, int errcode, const char *errmsg,
+ void *user_data)
+{
+ MRP_UNUSED(dc);
+ MRP_UNUSED(user_data);
+
+ if (errcode != 0) {
+ error_msg("Data set request failed (%d: %s).", errcode, errmsg);
+ }
+ else
+ info_msg("Sucessfully set data.");
+}
+
+
+static void export_data(client_t *c)
+{
+ mrp_domctl_data_t *tables;
+ int ntable = 2;
+ mrp_domctl_value_t *values, *v;
+ int i, id;
+
+ tables = alloca(sizeof(*tables) * ntable);
+ values = alloca(sizeof(*values) * NVALUE);
+ v = values;
+
+ if (!c->zone) {
+ id = 0;
+
+ tables[id].id = id;
+ tables[id].ncolumn = 4;
+ tables[id].nrow = NDEVICE;
+ tables[id].rows = alloca(sizeof(*tables[id].rows) * tables[id].nrow);
+
+ for (i = 0; i < (int)NDEVICE; i++) {
+ tables[id].rows[i] = v;
+ v[0].type = MRP_DOMCTL_STRING ; v[0].str = devices[i].name;
+ v[1].type = MRP_DOMCTL_STRING ; v[1].str = devices[i].type;
+ v[2].type = MRP_DOMCTL_INTEGER; v[2].s32 = devices[i].public;
+ v[3].type = MRP_DOMCTL_INTEGER; v[3].s32 = devices[i].available;
+ v += 4;
+ }
+
+ id++;
+
+ tables[id].id = id;
+ tables[id].ncolumn = 4;
+ tables[id].nrow = NSTREAM;
+ tables[id].rows = alloca(sizeof(*tables[id].rows) * tables[id].nrow);
+
+ for (i = 0; i < (int)NSTREAM; i++) {
+ tables[id].rows[i] = v;
+ v[0].type = MRP_DOMCTL_STRING ; v[0].str = streams[i].name;
+ v[1].type = MRP_DOMCTL_STRING ; v[1].str = streams[i].role;
+ v[2].type = MRP_DOMCTL_UNSIGNED; v[2].s32 = streams[i].owner;
+ v[3].type = MRP_DOMCTL_INTEGER ; v[3].u32 = streams[i].playing;
+ v += 4;
+ }
+ }
+ else {
+ id = 0;
+
+ tables[id].id = id;
+ tables[id].ncolumn = 3;
+ tables[id].nrow = NZONE;
+ tables[id].rows = alloca(sizeof(*tables[id].rows) * tables[id].nrow);
+
+ for (i = 0; i < (int)NZONE; i++) {
+ tables[id].rows[i] = v;
+ v[0].type = MRP_DOMCTL_STRING ; v[0].str = zones[i].name;
+ v[1].type = MRP_DOMCTL_INTEGER; v[1].s32 = zones[i].occupied;
+ v[2].type = MRP_DOMCTL_INTEGER; v[2].s32 = zones[i].active;
+ v += 3;
+ }
+
+ id++;
+
+ tables[id].id = id;
+ tables[id].ncolumn = 3;
+ tables[id].nrow = NCALL;
+ tables[id].rows = alloca(sizeof(*tables[0].rows) * tables[id].nrow);
+
+ for (i = 0; i < (int)NCALL; i++) {
+ tables[id].rows[i] = v;
+ v[0].type = MRP_DOMCTL_INTEGER; v[0].s32 = calls[i].id;
+ v[1].type = MRP_DOMCTL_STRING ; v[1].str = calls[i].state;
+ v[2].type = MRP_DOMCTL_STRING ; v[2].str = calls[i].modem;
+ v += 3;
+ }
+ }
+
+ if (!mrp_domctl_set_data(c->dc, tables, ntable, export_notify, c))
+ error_msg("Failed to send data set request to server.");
+}
+
+
+static void client_setup(client_t *c)
+{
+ mrp_mainloop_t *ml;
+ mrp_domctl_t *dc;
+
+ ml = mrp_mainloop_create();
+
+ if (ml != NULL) {
+ if (!c->zone) {
+ exports = media_tables;
+ nexport = MRP_ARRAY_SIZE(media_tables);
+ imports = zone_watches;
+ nimport = MRP_ARRAY_SIZE(zone_watches) - (c->audio ? 0 : 2);
+ }
+ else {
+ exports = zone_tables;
+ nexport = MRP_ARRAY_SIZE(zone_tables);
+ imports = media_watches;
+ nimport = MRP_ARRAY_SIZE(media_watches) - (c->audio ? 0 : 2);
+ }
+
+ if (c->audio)
+ info_msg("Will subscribe for audio_playback_* tables.");
+
+ dc = mrp_domctl_create(c->zone ? "zone-ctrl" : "media-ctrl", ml,
+ exports, nexport, imports, nimport,
+ connect_notify, data_notify, c);
+
+ if (dc != NULL) {
+ c->ml = ml;
+ c->dc = dc;
+
+ mrp_add_sighandler(ml, SIGINT, signal_handler, c);
+
+ if (c->zone) {
+ zone_t *z;
+ call_t *call;
+
+ for (z = zones; z->name != NULL; z++) {
+ z->name = mrp_strdup(z->name);
+ }
+
+ for (call = calls; call->id > 0; call++) {
+ call->state = mrp_strdup(call->state);
+ call->modem = mrp_strdup(call->modem);
+ }
+
+ init_devices();
+ init_streams();
+ }
+ else {
+ device_t *d;
+ stream_t *s;
+
+ for (d = devices; d->name != NULL; d++) {
+ d->name = mrp_strdup(d->name);
+ d->type = mrp_strdup(d->type);
+ }
+
+ for (s = streams; s->name != NULL; s++) {
+ s->name = mrp_strdup(s->name);
+ s->role = mrp_strdup(s->role);
+ }
+
+ init_zones();
+ init_calls();
+ }
+ }
+ else
+ fatal_msg(1, "Failed to create enforcement point.");
+ }
+ else
+ fatal_msg(1, "Failed to create mainloop.");
+
+ init_methods(c);
+}
+
+
+static void client_cleanup(client_t *c)
+{
+ if (c->zone) {
+ reset_devices();
+ reset_streams();
+ }
+ else {
+ reset_zones();
+ reset_calls();
+ }
+
+ mrp_mainloop_destroy(c->ml);
+ mrp_domctl_destroy(c->dc);
+
+ c->ml = NULL;
+ c->dc = NULL;
+}
+
+
+static void client_run(client_t *c)
+{
+ if (mrp_domctl_connect(c->dc, c->addrstr, 0))
+ info_msg("Trying to connect to server at %s...", c->addrstr);
+ else
+ error_msg("Failed to connect to server at %s.", c->addrstr);
+
+ mrp_mainloop_run(c->ml);
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -s, --server <address> connect to murphy at given address\n"
+ " -z, --zone run as zone controller\n"
+ " -A, --audio subscribe for audio_playback*\n"
+ " -v, --verbose run in verbose mode\n"
+ " -h, --help show this help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void client_set_defaults(client_t *c)
+{
+ mrp_clear(c);
+ c->addrstr = MRP_DEFAULT_DOMCTL_ADDRESS;
+ c->zone = FALSE;
+ c->verbose = FALSE;
+ c->audio = FALSE;
+}
+
+
+int parse_cmdline(client_t *c, int argc, char **argv)
+{
+# define OPTIONS "vAzhs:"
+ struct option options[] = {
+ { "zone" , no_argument , NULL, 'z' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "audio" , no_argument , NULL, 'A' },
+ { "server" , required_argument, NULL, 's' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ client_set_defaults(c);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'z':
+ c->zone = TRUE;
+ break;
+
+ case 'A':
+ c->audio = TRUE;
+ c->verbose = TRUE;
+ break;
+
+ case 'v':
+ c->verbose = TRUE;
+ break;
+
+ case 's':
+ c->addrstr = optarg;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ client_t c;
+
+ client_set_defaults(&c);
+ parse_cmdline(&c, argc, argv);
+
+ client_setup(&c);
+ terminal_setup(&c);
+
+ client = &c;
+ client_run(&c);
+
+ terminal_cleanup(&c);
+ client_cleanup(&c);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <dbus/dbus.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/plugin.h>
+
+typedef struct dbus_glue_s dbus_glue_t;
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_io_watch_t *mw;
+ DBusWatch *dw;
+ mrp_list_hook_t hook;
+} watch_t;
+
+
+typedef struct {
+ dbus_glue_t *glue;
+ mrp_timer_t *mt;
+ DBusTimeout *dt;
+ mrp_list_hook_t hook;
+} timeout_t;
+
+
+struct dbus_glue_s {
+ DBusConnection *conn;
+ mrp_mainloop_t *ml;
+ mrp_list_hook_t watches;
+ mrp_list_hook_t timers;
+ mrp_deferred_t *pump;
+};
+
+
+static dbus_int32_t data_slot = -1;
+
+static void dispatch_watch(mrp_io_watch_t *mw, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ watch_t *watch = (watch_t *)user_data;
+ DBusConnection *conn = watch->glue->conn;
+ unsigned int mask = 0;
+
+ MRP_UNUSED(mw);
+ MRP_UNUSED(fd);
+
+ if (events & MRP_IO_EVENT_IN)
+ mask |= DBUS_WATCH_READABLE;
+ if (events & MRP_IO_EVENT_OUT)
+ mask |= DBUS_WATCH_WRITABLE;
+ if (events & MRP_IO_EVENT_HUP)
+ mask |= DBUS_WATCH_HANGUP;
+ if (events & MRP_IO_EVENT_ERR)
+ mask |= DBUS_WATCH_ERROR;
+
+ dbus_connection_ref(conn);
+ dbus_watch_handle(watch->dw, mask);
+ dbus_connection_unref(conn);
+}
+
+
+static void watch_freed_cb(void *data)
+{
+ watch_t *watch = (watch_t *)data;
+
+ if (watch != NULL) {
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+ mrp_free(watch);
+ }
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *dw, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ watch_t *watch;
+ mrp_io_watch_t *mw;
+ mrp_io_event_t mask;
+ int fd;
+ unsigned int flags;
+
+ if (!dbus_watch_get_enabled(dw))
+ return TRUE;
+
+ fd = dbus_watch_get_unix_fd(dw);
+ flags = dbus_watch_get_flags(dw);
+ mask = MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+
+ if (flags & DBUS_WATCH_READABLE)
+ mask |= MRP_IO_EVENT_IN;
+ if (flags & DBUS_WATCH_WRITABLE)
+ mask |= MRP_IO_EVENT_OUT;
+
+ if ((watch = mrp_allocz(sizeof(*watch))) != NULL) {
+ mrp_list_init(&watch->hook);
+ mw = mrp_add_io_watch(glue->ml, fd, mask, dispatch_watch, watch);
+
+ if (mw != NULL) {
+ watch->glue = glue;
+ watch->mw = mw;
+ watch->dw = dw;
+ dbus_watch_set_data(dw, watch, watch_freed_cb);
+ mrp_list_append(&glue->watches, &watch->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(watch);
+ }
+
+ return FALSE;
+}
+
+
+static void del_watch(DBusWatch *dw, void *data)
+{
+ watch_t *watch = (watch_t *)dbus_watch_get_data(dw);
+
+ MRP_UNUSED(data);
+
+ if (watch != NULL) {
+ mrp_del_io_watch(watch->mw);
+ watch->mw = NULL;
+ }
+}
+
+
+static void toggle_watch(DBusWatch *dw, void *data)
+{
+ if (dbus_watch_get_enabled(dw))
+ add_watch(dw, data);
+ else
+ del_watch(dw, data);
+}
+
+
+static void dispatch_timeout(mrp_timer_t *mt, void *user_data)
+{
+ timeout_t *timer = (timeout_t *)user_data;
+
+ MRP_UNUSED(mt);
+
+ dbus_timeout_handle(timer->dt);
+}
+
+
+static void timeout_freed_cb(void *data)
+{
+ timeout_t *timer = (timeout_t *)data;
+
+ if (timer != NULL) {
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *dt, void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ timeout_t *timer;
+ mrp_timer_t *mt;
+ unsigned int msecs;
+
+ if ((timer = mrp_allocz(sizeof(*timer))) != NULL) {
+ mrp_list_init(&timer->hook);
+ msecs = dbus_timeout_get_interval(dt);
+ mt = mrp_add_timer(glue->ml, msecs, dispatch_timeout, timer);
+
+ if (mt != NULL) {
+ timer->glue = glue;
+ timer->mt = mt;
+ timer->dt = dt;
+ dbus_timeout_set_data(dt, timer, timeout_freed_cb);
+ mrp_list_append(&glue->timers, &timer->hook);
+
+ return TRUE;
+ }
+ else
+ mrp_free(timer);
+ }
+
+ return FALSE;
+}
+
+
+static void del_timeout(DBusTimeout *dt, void *data)
+{
+ timeout_t *timer = (timeout_t *)dbus_timeout_get_data(dt);
+
+ MRP_UNUSED(data);
+
+ if (timer != NULL) {
+ mrp_del_timer(timer->mt);
+ timer->mt = NULL;
+ }
+}
+
+
+static void toggle_timeout(DBusTimeout *dt, void *data)
+{
+ if (dbus_timeout_get_enabled(dt))
+ add_timeout(dt, data);
+ else
+ del_timeout(dt, data);
+}
+
+
+static void wakeup_mainloop(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+
+ mrp_enable_deferred(glue->pump);
+}
+
+
+static void glue_free_cb(void *data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)data;
+ mrp_list_hook_t *p, *n;
+ watch_t *watch;
+ timeout_t *timer;
+
+ mrp_list_foreach(&glue->watches, p, n) {
+ watch = mrp_list_entry(p, typeof(*watch), hook);
+
+ mrp_list_delete(&watch->hook);
+ mrp_del_io_watch(watch->mw);
+
+ mrp_free(watch);
+ }
+
+ mrp_list_foreach(&glue->timers, p, n) {
+ timer = mrp_list_entry(p, typeof(*timer), hook);
+
+ mrp_list_delete(&timer->hook);
+ mrp_del_timer(timer->mt);
+
+ mrp_free(timer);
+ }
+
+ mrp_free(glue);
+}
+
+
+static void pump_cb(mrp_deferred_t *d, void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ if (dbus_connection_dispatch(glue->conn) == DBUS_DISPATCH_COMPLETE)
+ mrp_disable_deferred(d);
+}
+
+
+static void dispatch_status_cb(DBusConnection *conn, DBusDispatchStatus status,
+ void *user_data)
+{
+ dbus_glue_t *glue = (dbus_glue_t *)user_data;
+
+ MRP_UNUSED(conn);
+
+ switch (status) {
+ case DBUS_DISPATCH_COMPLETE:
+ mrp_disable_deferred(glue->pump);
+ break;
+
+ case DBUS_DISPATCH_DATA_REMAINS:
+ case DBUS_DISPATCH_NEED_MEMORY:
+ default:
+ mrp_enable_deferred(glue->pump);
+ break;
+ }
+}
+
+
+int mrp_setup_dbus_connection(mrp_mainloop_t *ml, DBusConnection *conn)
+{
+ dbus_glue_t *glue;
+
+ if (!dbus_connection_allocate_data_slot(&data_slot))
+ return FALSE;
+
+ if (dbus_connection_get_data(conn, data_slot) != NULL)
+ return FALSE;
+
+ if ((glue = mrp_allocz(sizeof(*glue))) != NULL) {
+ mrp_list_init(&glue->watches);
+ mrp_list_init(&glue->timers);
+ glue->pump = mrp_add_deferred(ml, pump_cb, glue);
+
+ if (glue->pump == NULL) {
+ mrp_free(glue);
+ return FALSE;
+ }
+
+ glue->ml = ml;
+ glue->conn = conn;
+ }
+ else
+ return FALSE;
+
+ if (!dbus_connection_set_data(conn, data_slot, glue, glue_free_cb))
+ return FALSE;
+
+ dbus_connection_set_dispatch_status_function(conn, dispatch_status_cb,
+ glue, NULL);
+
+ dbus_connection_set_wakeup_main_function(conn, wakeup_mainloop,
+ glue, NULL);
+
+ return
+ dbus_connection_set_watch_functions(conn, add_watch, del_watch,
+ toggle_watch, glue, NULL) &&
+ dbus_connection_set_timeout_functions(conn, add_timeout, del_timeout,
+ toggle_timeout, glue, NULL);
+}
+
+
+
+static int dbus_init(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ mrp_log_info("%s() called...", __FUNCTION__);
+
+ return TRUE;
+}
+
+
+static void dbus_exit(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ mrp_log_info("%s() called...", __FUNCTION__);
+}
+
+
+#define DBPLG_DESCRIPTION "A plugin to pump DBusConnections."
+#define DBPLG_HELP "DBUS pump plugin (DBUS-mainloop integration)."
+#define DBPLG_VERSION MRP_VERSION_INT(0, 0, 1)
+#define DBPLG_AUTHORS "Krisztian Litkey <krisztian.litkey@intel.com>"
+
+MURPHY_REGISTER_CORE_PLUGIN("dbus",
+ DBPLG_VERSION, DBPLG_DESCRIPTION, DBPLG_AUTHORS,
+ DBPLG_HELP, MRP_SINGLETON,
+ dbus_init, dbus_exit,
+ NULL, 0, NULL, 0, NULL, 0, NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/plugin.h>
+
+
+/*
+ * A simple glue layer to pump GMainLoop from mrp_mainloop_t. This
+ * will pretty much be turned into a murphy plugin as such...
+ */
+
+
+typedef struct {
+ GMainLoop *ml;
+ GMainContext *mc;
+ gint maxprio;
+ mrp_subloop_t *sl;
+} glib_glue_t;
+
+static glib_glue_t *glib_glue;
+
+
+static int glib_prepare(void *user_data)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_prepare(glue->mc, &glue->maxprio);
+}
+
+
+static int glib_query(void *user_data, struct pollfd *fds, int nfd,
+ int *timeout)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_query(glue->mc, glue->maxprio, timeout,
+ (GPollFD *)fds, nfd);
+}
+
+
+static int glib_check(void *user_data, struct pollfd *fds, int nfd)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ return g_main_context_check(glue->mc, glue->maxprio, (GPollFD *)fds, nfd);
+
+}
+
+
+static void glib_dispatch(void *user_data)
+{
+ glib_glue_t *glue = (glib_glue_t *)user_data;
+
+ g_main_context_dispatch(glue->mc);
+
+}
+
+
+static int glib_pump_setup(mrp_mainloop_t *ml)
+{
+ static mrp_subloop_ops_t glib_ops = {
+ .prepare = glib_prepare,
+ .query = glib_query,
+ .check = glib_check,
+ .dispatch = glib_dispatch
+ };
+
+ GMainContext *main_context;
+ GMainLoop *main_loop;
+
+ if (sizeof(GPollFD) != sizeof(struct pollfd)) {
+ mrp_log_error("sizeof(GPollFD:%zd) != sizeof(struct pollfd:%zd)\n",
+ sizeof(GPollFD), sizeof(struct pollfd));
+ return FALSE;
+ }
+
+ main_context = NULL;
+ main_loop = NULL;
+ glib_glue = NULL;
+
+ if ((main_context = g_main_context_default()) != NULL &&
+ (main_loop = g_main_loop_new(main_context, FALSE)) != NULL &&
+ (glib_glue = mrp_allocz(sizeof(*glib_glue))) != NULL) {
+
+ glib_glue->mc = main_context;
+ glib_glue->ml = main_loop;
+ glib_glue->sl = mrp_add_subloop(ml, &glib_ops, glib_glue);
+
+ if (glib_glue->sl != NULL)
+ return TRUE;
+ else
+ mrp_log_error("glib-pump failed to register subloop.");
+ }
+
+ /* all of these handle a NULL argument gracefully... */
+ g_main_loop_unref(main_loop);
+ g_main_context_unref(main_context);
+
+ mrp_free(glib_glue);
+ glib_glue = NULL;
+
+ return FALSE;
+}
+
+
+static void glib_pump_cleanup(void)
+{
+ if (glib_glue != NULL) {
+ mrp_del_subloop(glib_glue->sl);
+
+ g_main_loop_unref(glib_glue->ml);
+ g_main_context_unref(glib_glue->mc);
+
+ mrp_free(glib_glue);
+ glib_glue = NULL;
+ }
+}
+
+
+
+static int plugin_init(mrp_plugin_t *plugin)
+{
+ mrp_log_info("%s() called...", __FUNCTION__);
+
+ return glib_pump_setup(plugin->ctx->ml);
+}
+
+
+static void plugin_exit(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ mrp_log_info("%s() called...", __FUNCTION__);
+
+ glib_pump_cleanup();
+}
+
+
+#define GLIB_DESCRIPTION "Glib mainloop pump plugin."
+#define GLIB_HELP "Glib pump plugin (GMainLoop integration)."
+#define GLIB_VERSION MRP_VERSION_INT(0, 0, 1)
+#define GLIB_AUTHORS "Krisztian Litkey <krisztian.litkey@intel.com>"
+
+MURPHY_REGISTER_PLUGIN("glib", GLIB_VERSION, GLIB_DESCRIPTION, GLIB_AUTHORS,
+ GLIB_HELP, MRP_SINGLETON,
+ plugin_init, plugin_exit,
+ NULL, 0, NULL, 0, NULL, 0, NULL);
+
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#define LUAR_INTERPRETER_NAME "lua"
+
+
+enum {
+ ARG_CONFIG, /* configuration file */
+ ARG_RESOLVER, /* enable resolver lua support */
+};
+
+
+static int load_config(lua_State *L, const char *path)
+{
+ int success;
+
+ if (!luaL_loadfile(L, path) && !lua_pcall(L, 0, 0, 0))
+ success = TRUE;
+ else {
+ mrp_log_error("plugin-lua: failed to load config file %s.", path);
+ mrp_log_error("%s", lua_tostring(L, -1));
+ lua_settop(L, 0);
+
+ success = FALSE;
+ }
+
+ return success;
+}
+
+
+static int luaR_compile(mrp_scriptlet_t *script)
+{
+ mrp_interpreter_t *i = script->interpreter;
+ lua_State *L = i->data;
+ const char *code = script->source;
+ int len = strlen(code);
+ int status;
+
+ if (!luaL_loadbuffer(L, code, len, "<resolver Lua scriptlet>")) {
+ script->data = (void *)(ptrdiff_t)luaL_ref(L, LUA_REGISTRYINDEX);
+ status = 0;
+ }
+ else {
+ mrp_log_error("plugin-lua: failed to compile scriptlet.");
+ mrp_log_error("%s", lua_tostring(L, -1));
+ status = -EINVAL;
+ }
+
+ lua_settop(L, 0);
+
+ return status;
+}
+
+
+static int luaR_prepare(mrp_scriptlet_t *script)
+{
+ MRP_UNUSED(script);
+
+ return 0;
+}
+
+
+static int luaR_execute(mrp_scriptlet_t *script, mrp_context_tbl_t *ctbl)
+{
+ mrp_interpreter_t *i = script->interpreter;
+ lua_State *L = i->data;
+ int ref = (ptrdiff_t)script->data;
+ int top, success;
+
+ MRP_UNUSED(ctbl);
+
+ success = FALSE;
+ top = lua_gettop(L);
+
+ lua_rawgeti(L, LUA_REGISTRYINDEX, ref);
+
+ if (lua_isfunction(L, -1)) {
+ if (!lua_pcall(L, 0, 0, 0))
+ success = TRUE;
+ }
+ else {
+ mrp_log_error("plugin-lua: failed to execute scriptlet.");
+ mrp_log_error("error: %s", lua_tostring(L, -1));
+ }
+
+ lua_settop(L, top);
+
+ return success;
+}
+
+
+static void luaR_cleanup(mrp_scriptlet_t *script)
+{
+ mrp_interpreter_t *i = script->interpreter;
+ lua_State *L = i->data;
+ int ref = (ptrdiff_t)script->data;
+
+ luaL_unref(L, LUA_REGISTRYINDEX, ref);
+}
+
+static int plugin_init(mrp_plugin_t *plugin)
+{
+ static mrp_interpreter_t interpreter = {
+ .name = LUAR_INTERPRETER_NAME,
+ .compile = luaR_compile,
+ .prepare = luaR_prepare,
+ .execute = luaR_execute,
+ .cleanup = luaR_cleanup,
+ .data = NULL
+ };
+ mrp_plugin_arg_t *args = plugin->args;
+ const char *cfg = args[ARG_CONFIG].str;
+ int res = args[ARG_RESOLVER].bln;
+ lua_State *L;
+
+ L = mrp_lua_set_murphy_context(plugin->ctx);
+
+ if (L != NULL) {
+ if (res) {
+ interpreter.data = L;
+
+ if (!mrp_register_interpreter(&interpreter)) {
+ mrp_log_error("plugin-lua: failed to register interpreter.");
+
+ return FALSE;
+ }
+ }
+ else
+ mrp_log_info("plugin-lua: resolver Lua support disabled.");
+
+ mrp_lua_set_murphy_lua_config_file(cfg);
+
+ if (load_config(L, cfg))
+ return TRUE;
+ else
+ if (res)
+ mrp_unregister_interpreter(LUAR_INTERPRETER_NAME);
+ }
+
+ return FALSE;
+}
+
+
+static void plugin_exit(mrp_plugin_t *plugin)
+{
+ mrp_plugin_arg_t *args = plugin->args;
+
+ if (args[ARG_RESOLVER].bln)
+ mrp_unregister_interpreter(LUAR_INTERPRETER_NAME);
+}
+
+
+#define PLUGIN_DESCRIPTION "Lua bindings for Murphy."
+#define PLUGIN_HELP "Enable Lua bindings for Murphy."
+#define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
+#define PLUGIN_VERSION MRP_VERSION_INT(0, 0, 1)
+
+#define DEFAULT_CONFIG "/etc/murphy/murphy.lua"
+
+static mrp_plugin_arg_t plugin_args[] = {
+ MRP_PLUGIN_ARGIDX(ARG_CONFIG , STRING, "config", DEFAULT_CONFIG),
+ MRP_PLUGIN_ARGIDX(ARG_RESOLVER, BOOL , "resolver",TRUE),
+};
+
+MURPHY_REGISTER_PLUGIN("lua",
+ PLUGIN_VERSION, PLUGIN_DESCRIPTION, PLUGIN_AUTHORS,
+ PLUGIN_HELP, MRP_SINGLETON, plugin_init, plugin_exit,
+ plugin_args, MRP_ARRAY_SIZE(plugin_args),
+ NULL, 0,
+ NULL, 0,
+ NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include <murphy/common.h>
+#include <murphy/core.h>
+#include <murphy/common/dbus-libdbus.h>
+
+#include <murphy/resource/client-api.h>
+
+
+#define MURPHY_PATH_BASE "/org/murphy/resource"
+
+#define MANAGER_IFACE "org.murphy.manager"
+#define RSET_IFACE "org.murphy.resourceset"
+#define RESOURCE_IFACE "org.murphy.resource"
+
+#define MAX_PATH_LENGTH 64
+#define MAX_DBUS_SIG_LENGTH 8
+
+
+#define MANAGER_CREATE_RESOURCE_SET "createResourceSet"
+#define MANAGER_GET_PROPERTIES "getProperties"
+
+#define RSET_SET_PROPERTY "setProperty"
+#define RSET_GET_PROPERTIES "getProperties"
+#define RSET_ADD_RESOURCE "addResource"
+#define RSET_REQUEST "request"
+#define RSET_RELEASE "release"
+#define RSET_DELETE "delete"
+
+#define RESOURCE_SET_PROPERTY "setProperty"
+#define RESOURCE_GET_PROPERTIES "getProperties"
+#define RESOURCE_DELETE "delete"
+
+#define PROP_RESOURCE_SETS "resourceSets"
+#define PROP_AVAILABLE_CLASSES "availableClasses"
+#define PROP_AVAILABLE_RESOURCES "availableResources"
+#define PROP_NAME "name"
+#define PROP_SHARED "shared"
+#define PROP_MANDATORY "mandatory"
+#define PROP_CLASS "class"
+#define PROP_RESOURCES "resources"
+#define PROP_STATUS "status"
+#define PROP_ATTRIBUTES "attributes"
+#define PROP_ATTRIBUTES_CONF "attributes_conf"
+
+#define SIG_PROPERTYCHANGED "propertyChanged"
+
+enum {
+ ARG_DR_BUS,
+ ARG_DR_SERVICE,
+ ARG_DR_TRACK_CLIENTS,
+ ARG_DR_DEFAULT_ZONE,
+ ARG_DR_DEFAULT_CLASS,
+};
+
+typedef struct manager_o_s manager_o_t;
+
+typedef struct {
+ /* configuration */
+ mrp_dbus_t *dbus;
+ const char *addr;
+ const char *bus;
+ const char *default_zone;
+ const char *default_class;
+
+ bool tracking;
+
+ int has_classes;
+
+ /* resource management */
+ manager_o_t *mgr;
+
+ /* murphy integration */
+ mrp_mainloop_t *ml;
+} dbus_data_t;
+
+typedef struct property_o_s {
+ /* dbus properties */
+ char *path;
+ char *interface;
+ char *dbus_sig;
+
+ /* data */
+ char *name;
+ void *value;
+ bool writable; /* used later when we allow more access to properties */
+
+ dbus_data_t *ctx;
+
+ /* function to free the value */
+ void (*free_data)(void *data);
+
+ /* may be needed in the future? maybe not in this form */
+ int (*compare)(struct property_o_s *a, struct property_o_s *b);
+} property_o_t;
+
+struct manager_o_s {
+ uint32_t next_id; /* next resource set id */
+
+ dbus_data_t *ctx;
+ mrp_htbl_t *rsets;
+
+ property_o_t *rsets_prop;
+ property_o_t *available_classes_prop;
+
+ /* resource library */
+ const char *zone;
+ mrp_resource_client_t *client;
+};
+
+typedef struct {
+ uint32_t next_id; /* next resource id */
+ char *path;
+ char *owner;
+
+ manager_o_t *mgr; /* backpointer */
+
+ mrp_htbl_t *resources;
+
+ property_o_t *resources_prop;
+ property_o_t *available_resources_prop;
+ property_o_t *class_prop;
+ property_o_t *status_prop;
+
+ /* resource library */
+ bool locked; /* if the library allows the settings to be changed */
+ bool committed; /* set to true when we are committing the resource set */
+ mrp_resource_set_t *set;
+
+ /* pending properties for events that have been received in wrong order */
+ bool update_needed;
+ mrp_resource_mask_t pending_grant;
+ mrp_resource_mask_t pending_advice;
+
+ /* whether we have encountered an error in the library calls */
+ bool error;
+} resource_set_o_t;
+
+typedef struct {
+ char *path;
+
+ resource_set_o_t *rset; /* backpointer */
+
+ property_o_t *status_prop;
+ property_o_t *mandatory_prop;
+ property_o_t *shared_prop;
+ property_o_t *name_prop;
+ property_o_t *arguments_prop;
+ property_o_t *conf_prop;
+} resource_o_t;
+
+static int mgr_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data);
+static int rset_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data);
+static int resource_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data);
+static void dbus_name_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data);
+
+
+/* copy the keys in a hash map to a NULL-terminated array */
+
+struct key_data_s {
+ int curr_key;
+ char **keys;
+};
+
+struct deferred_rset_data_s {
+ char *rset_path;
+ manager_o_t *mgr;
+};
+
+static int copy_keys_cb(void *key, void *object, void *user_data)
+{
+ struct key_data_s *kd = user_data;
+
+ MRP_UNUSED(object);
+
+ kd->keys[kd->curr_key] = mrp_strdup((char *) key);
+ kd->curr_key++;
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static int count_keys_cb(void *key, void *object, void *user_data)
+{
+ int *count = user_data;
+
+ MRP_UNUSED(key);
+ MRP_UNUSED(object);
+
+ *count = *count + 1;
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static char **htbl_keys(mrp_htbl_t *ht)
+{
+ char **keys;
+ int len = 0;
+ struct key_data_s kd;
+
+ if (!ht)
+ return NULL;
+
+ mrp_htbl_foreach(ht, count_keys_cb, &len);
+
+ keys = mrp_alloc_array(char *, len+1);
+
+ kd.curr_key = 0;
+ kd.keys = keys;
+
+ mrp_htbl_foreach(ht, copy_keys_cb, &kd);
+
+ keys[len] = NULL;
+
+ return keys;
+}
+
+/* functions for freeing property values */
+
+static void free_value(void *val) {
+ mrp_free(val);
+}
+
+
+static void free_string_array(void *array) {
+
+ char **i = array;
+
+ if (!array)
+ return;
+
+ while (*i) {
+ mrp_free(*i);
+ i++;
+ }
+
+ mrp_free(array);
+}
+
+
+static void free_attr_array(mrp_attr_t *arr)
+{
+ /* only free the allocated members */
+ mrp_attr_t *i = arr;
+
+ while (i->name) {
+
+ if (i->type == mqi_string)
+ mrp_free((void *) i->value.string);
+
+ mrp_free((void *) i->name);
+ i++;
+ }
+}
+
+
+static char **copy_string_array(const char **array)
+{
+ int count = 0, i;
+ char **tmp = (char **) array;
+ char **ret;
+
+ if (!array)
+ return NULL;
+
+ while (*tmp) {
+ count++;
+ tmp++;
+ }
+
+ ret = mrp_alloc_array(char *, count+1);
+
+ if (!ret)
+ return NULL;
+
+ for (i = 0; i < count; i++) {
+ ret[i] = mrp_strdup(array[i]);
+ if (!ret[i]) {
+ free_string_array(ret);
+ return NULL;
+ }
+ }
+
+ ret[i] = NULL;
+
+ return ret;
+}
+
+static const char *get_dbus_type(mrp_attr_t *v)
+{
+ switch(v->type) {
+ case mqi_string:
+ return MRP_DBUS_TYPE_STRING_AS_STRING;
+ case mqi_integer:
+ return MRP_DBUS_TYPE_INT32_AS_STRING;
+ case mqi_unsignd:
+ return MRP_DBUS_TYPE_UINT32_AS_STRING;
+ case mqi_floating:
+ return MRP_DBUS_TYPE_DOUBLE_AS_STRING;
+ default:
+ goto end;
+ }
+
+end:
+ return NULL;
+}
+
+static int dbus_value_cb(void *key, void *object, void *user_data)
+{
+ mrp_dbus_msg_t *reply = user_data;
+ char *arg_name = key;
+ mrp_attr_t *arg_value = object;
+ const char *sig = get_dbus_type(arg_value);
+ char dsig[3];
+ int ret;
+
+ if (!sig) {
+ mrp_log_error("unknown database type");
+ goto end;
+ }
+
+ ret = snprintf(dsig, sizeof(dsig), "%s%s", sig, MRP_DBUS_TYPE_VARIANT_AS_STRING);
+
+ if (ret < 0 || ret == sizeof(dsig)) {
+ mrp_log_error("invalid signature");
+ goto end;
+ }
+
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_DICT_ENTRY, NULL)) {
+ mrp_log_error("failed to open dict container with sig '%s'", dsig);
+ goto end;
+ }
+
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_STRING, arg_name)) {
+ mrp_log_error("failed to append argument name '%s'", arg_name);
+ goto end_close_dict;
+ }
+
+ switch(arg_value->type) {
+ case mqi_string:
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_VARIANT, sig))
+ goto end_close_dict;
+ if (!mrp_dbus_msg_append_basic(reply, sig[0],
+ (char *) arg_value->value.string))
+ goto end_close_variant;
+ break;
+ case mqi_integer:
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_VARIANT, sig))
+ goto end_close_dict;
+ if (!mrp_dbus_msg_append_basic(reply, sig[0],
+ &arg_value->value.integer))
+ goto end_close_variant;
+ break;
+ case mqi_unsignd:
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_VARIANT, sig))
+ goto end_close_dict;
+ if (!mrp_dbus_msg_append_basic(reply, sig[0],
+ &arg_value->value.unsignd))
+ goto end_close_variant;
+ break;
+ case mqi_floating:
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_VARIANT, sig))
+ goto end_close_dict;
+ if (!mrp_dbus_msg_append_basic(reply, sig[0],
+ &arg_value->value.floating))
+ goto end_close_variant;
+ break;
+ default:
+ mrp_log_error("unknown type %d in attributes", arg_value->type);
+ break;
+ }
+
+end_close_variant:
+ mrp_dbus_msg_close_container(reply); /* variant container */
+end_close_dict:
+ mrp_dbus_msg_close_container(reply); /* dict container */
+end:
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static bool get_property_entry(property_o_t *prop, mrp_dbus_msg_t *reply)
+{
+ /* FIXME: check return values */
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_STRING, prop->name))
+ goto error;
+
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_VARIANT, prop->dbus_sig))
+ goto error;
+
+ /* TODO: this might be remade to be generic? */
+
+ if (strcmp(prop->dbus_sig, "s") == 0) {
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_STRING, prop->value))
+ goto error_close_variant;
+ }
+ else if (strcmp(prop->dbus_sig, "b") == 0) {
+ bool value = *(bool *) prop->value;
+ uint32_t v = value;
+ mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_BOOLEAN, &v);
+ }
+ else if (strcmp(prop->dbus_sig, "as") == 0) {
+ char **i = prop->value;
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_ARRAY, "s"))
+ goto error_close_variant;
+
+ while (*i) {
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_STRING, *i)) {
+ mrp_dbus_msg_close_container(reply); /* array */
+ goto error_close_variant;
+ }
+ i++;
+ }
+ mrp_dbus_msg_close_container(reply); /* array */
+ }
+ else if (strcmp(prop->dbus_sig, "ao") == 0) {
+ char **i = prop->value;
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_ARRAY, "o"))
+ goto error_close_variant;
+
+ while (*i) {
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_OBJECT_PATH, *i)) {
+ mrp_dbus_msg_close_container(reply); /* array */
+ goto error_close_variant;
+ }
+ i++;
+ }
+ mrp_dbus_msg_close_container(reply); /* array */
+ }
+ else if (strcmp(prop->dbus_sig, "a{sv}") == 0) {
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_ARRAY, "{sv}"))
+ goto error_close_variant;
+
+ /* iterate through the elements in the map */
+ mrp_htbl_foreach(prop->value, dbus_value_cb, reply);
+
+ mrp_dbus_msg_close_container(reply); /* array */
+ }
+ else {
+ mrp_log_error("Unknown sig '%s'", prop->dbus_sig);
+ goto error_close_variant;
+ }
+
+ mrp_dbus_msg_close_container(reply); /* variant */
+
+ return TRUE;
+
+error_close_variant:
+ mrp_dbus_msg_close_container(reply); /* variant */
+error:
+ return FALSE;
+}
+
+
+static bool get_property_dict_entry(property_o_t *prop, mrp_dbus_msg_t *reply)
+{
+ bool ret;
+
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_DICT_ENTRY, NULL))
+ return FALSE;
+
+ ret = get_property_entry(prop, reply);
+
+ mrp_dbus_msg_close_container(reply);
+
+ return ret;
+}
+
+
+static void trigger_property_changed_signal(dbus_data_t *ctx,
+ property_o_t *prop)
+{
+ mrp_dbus_msg_t *sig;
+
+ if (!prop)
+ return;
+
+ mrp_log_info("propertyChanged signal (%s)", prop->name);
+
+ sig = mrp_dbus_msg_signal(ctx->dbus, NULL, prop->path,
+ prop->interface, SIG_PROPERTYCHANGED);
+
+ if (!sig)
+ return;
+
+ get_property_entry(prop, sig);
+
+ mrp_dbus_send_msg(ctx->dbus, sig);
+ mrp_dbus_msg_unref(sig);
+}
+
+
+static void destroy_property(property_o_t *prop)
+{
+ if (!prop)
+ return;
+
+ mrp_free(prop->dbus_sig);
+ mrp_free(prop->interface);
+ mrp_free(prop->path);
+ mrp_free(prop->name);
+
+ if (prop->free_data)
+ prop->free_data(prop->value);
+
+ mrp_free(prop);
+}
+
+
+static property_o_t *create_property(dbus_data_t *ctx, char *path,
+ const char *interface, const char *sig, const char *name, void *value,
+ void (*free_data)(void *data))
+{
+ property_o_t *prop = mrp_allocz(sizeof(property_o_t));
+
+ if (!prop)
+ goto error;
+
+ prop->dbus_sig = mrp_strdup(sig);
+ prop->interface = mrp_strdup(interface);
+ prop->path = mrp_strdup(path);
+ prop->name = mrp_strdup(name);
+ prop->writable = FALSE;
+ prop->value = value;
+
+ prop->ctx = ctx;
+
+ prop->free_data = free_data;
+
+ if (!prop->dbus_sig || !prop->name || !prop->value)
+ goto error;
+
+ trigger_property_changed_signal(ctx, prop);
+
+ return prop;
+
+error:
+ if (prop) {
+ destroy_property(prop);
+ }
+ else {
+ if (free_data)
+ free_data(value);
+ }
+
+ return NULL;
+}
+
+
+static void update_property(property_o_t *prop, void *value)
+{
+ /* the value is of the same type so we'll use the same function for
+ * freeing it */
+
+ if (prop->free_data)
+ prop->free_data(prop->value);
+
+ prop->value = value;
+
+ trigger_property_changed_signal(prop->ctx, prop);
+}
+
+
+static void destroy_resource(resource_o_t *resource)
+{
+ if (!resource)
+ return;
+
+ mrp_log_info("destroy resource %s", resource->path);
+
+ mrp_dbus_remove_method(resource->rset->mgr->ctx->dbus, resource->path,
+ RESOURCE_IFACE, RESOURCE_GET_PROPERTIES, resource_cb,
+ resource->rset->mgr->ctx);
+ mrp_dbus_remove_method(resource->rset->mgr->ctx->dbus, resource->path,
+ RESOURCE_IFACE, RESOURCE_SET_PROPERTY, resource_cb,
+ resource->rset->mgr->ctx);
+ mrp_dbus_remove_method(resource->rset->mgr->ctx->dbus, resource->path,
+ RESOURCE_IFACE, RESOURCE_DELETE, resource_cb,
+ resource->rset->mgr->ctx);
+
+ destroy_property(resource->mandatory_prop);
+ destroy_property(resource->shared_prop);
+ destroy_property(resource->name_prop);
+ destroy_property(resource->status_prop);
+ destroy_property(resource->arguments_prop);
+ destroy_property(resource->conf_prop);
+
+ /* FIXME: resource library doesn't allow destroying resources? */
+
+ mrp_free(resource->path);
+
+ mrp_free(resource);
+}
+
+
+struct search_data_s {
+ const char *name;
+ resource_o_t *resource;
+};
+
+
+static int find_resource_cb(void *key, void *object, void *user_data)
+{
+ resource_o_t *r = object;
+ struct search_data_s *s = user_data;
+ MRP_UNUSED(key);
+
+ if (strcmp(r->name_prop->value, s->name) == 0) {
+ s->resource = r;
+ return MRP_HTBL_ITER_STOP;
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static resource_o_t *get_resource_by_name(resource_set_o_t *rset,
+ const char *name)
+{
+ struct search_data_s s;
+
+ s.name = name;
+ s.resource = NULL;
+
+ mrp_htbl_foreach(rset->resources, find_resource_cb, &s);
+
+ return s.resource;
+}
+
+static void update_resources(resource_set_o_t *rset, mrp_resource_mask_t grant,
+ mrp_resource_mask_t advice)
+{
+ mrp_resource_t *resource;
+ void *iter = NULL;
+
+ if (!rset->set || !rset->committed) {
+ mrp_log_error("resource-dbus: update_resources with invalid rset");
+ return;
+ }
+
+ if (rset->update_needed) {
+ /* process pending events first */
+ rset->update_needed = FALSE;
+ update_resources(rset, rset->pending_grant, rset->pending_advice);
+ }
+
+ /* the resource API is "bit" awkward here */
+
+ while ((resource = mrp_resource_set_iterate_resources(rset->set, &iter))) {
+ mrp_resource_mask_t mask;
+ const char *name;
+ resource_o_t *res;
+
+ mask = mrp_resource_get_mask(resource);
+ name = mrp_resource_get_name(resource);
+
+ /* search the matching resource set object */
+
+ res = get_resource_by_name(rset, name);
+
+ if (!res) {
+ mrp_log_error("Resource %s not found", name);
+ continue;
+ }
+
+ if (mask & grant) {
+ update_property(res->status_prop, "acquired");
+ }
+ else if (mask & advice) {
+ update_property(res->status_prop, "available");
+ }
+ else {
+ update_property(res->status_prop, "lost");
+ }
+ }
+
+ if (grant) {
+ update_property(rset->status_prop, "acquired");
+ }
+ else if (advice) {
+ update_property(rset->status_prop, "available");
+ }
+ else {
+ update_property(rset->status_prop, "lost");
+ }
+}
+
+static void update_later_cb(mrp_deferred_t *d, void *data)
+{
+ struct deferred_rset_data_s *r_data = (struct deferred_rset_data_s *) data;
+ manager_o_t *mgr = r_data->mgr;
+
+ resource_set_o_t *rset = mrp_htbl_lookup(mgr->rsets, r_data->rset_path);
+
+ if (rset && rset->update_needed)
+ update_resources(rset, rset->pending_grant, rset->pending_advice);
+
+ mrp_free(r_data->rset_path);
+ mrp_free(r_data);
+
+ mrp_del_deferred(d);
+}
+
+static void event_cb(uint32_t request_id, mrp_resource_set_t *set, void *data)
+{
+ resource_set_o_t *rset = data;
+
+ mrp_resource_mask_t grant = mrp_get_resource_set_grant(set);
+ mrp_resource_mask_t advice = mrp_get_resource_set_advice(set);
+
+ MRP_UNUSED(request_id);
+
+ mrp_log_info("Event for %s: grant 0x%08x, advice 0x%08x",
+ rset->path, grant, advice);
+
+ if (!rset->set || !rset->committed) {
+
+ struct deferred_rset_data_s *r_data =
+ mrp_allocz(sizeof(struct deferred_rset_data_s));
+
+ if (!r_data) {
+ return;
+ }
+
+ r_data->mgr = rset->mgr;
+ r_data->rset_path = mrp_strdup(rset->path);
+
+ if (!r_data->rset_path) {
+ mrp_free(r_data);
+ return;
+ }
+
+ /* We haven't yet returned from the create_set call, and this is before
+ * acquiring the set, or we haven't started the acquitision yet. Filter
+ * out! */
+
+ mrp_log_info("Filtering out the event, trying again soon");
+
+ rset->update_needed = TRUE;
+ rset->pending_grant = grant;
+ rset->pending_advice = advice;
+
+ mrp_add_deferred(rset->mgr->ctx->ml, update_later_cb, r_data);
+
+ return;
+ }
+
+ update_resources(rset, grant, advice);
+}
+
+
+static void htbl_free_resources(void *key, void *object)
+{
+ resource_o_t *resource = object;
+
+ MRP_UNUSED(key);
+
+ destroy_resource(resource);
+}
+
+
+static void htbl_free_args(void *key, void *object)
+{
+ mrp_attr_t *attr = object;
+
+ MRP_UNUSED(key);
+
+ if (attr->type == mqi_string)
+ mrp_free((void *) attr->value.string);
+
+ mrp_free((void *) attr->name);
+ mrp_free(attr);
+}
+
+
+static void free_map(void *object)
+{
+ mrp_htbl_t *ht = object;
+ mrp_htbl_destroy(ht, TRUE);
+}
+
+
+static resource_o_t * create_resource(resource_set_o_t *rset,
+ const char *resource_name, uint32_t id)
+{
+ char buf[MAX_PATH_LENGTH];
+ int ret;
+ char *name = NULL;
+ bool *mandatory = NULL, *shared = NULL;
+
+ /* attribute handling */
+ mrp_attr_t attr_buf[128];
+ mrp_attr_t *i;
+ uint32_t resource_id;
+ mrp_attr_t *attrs;
+ mrp_attr_t *copy;
+
+ mrp_htbl_config_t map_conf;
+ mrp_htbl_t *conf;
+
+ resource_o_t *resource = mrp_allocz(sizeof(resource_o_t));
+
+ if (!resource)
+ goto error;
+
+ ret = snprintf(buf, MAX_PATH_LENGTH, "%s/%u", rset->path, id);
+
+ if (ret < 0 || ret >= MAX_PATH_LENGTH)
+ goto error;
+
+ mandatory = mrp_allocz(sizeof(bool));
+ shared = mrp_allocz(sizeof(bool));
+ name = mrp_strdup(resource_name);
+
+ if (!mandatory || !shared || !name) {
+ mrp_free(mandatory);
+ mrp_free(shared);
+ mrp_free(name);
+ goto error;
+ }
+
+ *mandatory = TRUE;
+ *shared = FALSE;
+
+ map_conf.comp = mrp_string_comp;
+ map_conf.hash = mrp_string_hash;
+ map_conf.free = htbl_free_args;
+ map_conf.nbucket = 0;
+ map_conf.nentry = 10;
+
+ resource->mandatory_prop = create_property(rset->mgr->ctx, buf,
+ RESOURCE_IFACE, "b", PROP_MANDATORY, mandatory, free_value);
+
+ if (!resource->mandatory_prop) {
+ mrp_free(mandatory);
+ mrp_free(shared);
+ mrp_free(name);
+ goto error;
+ }
+
+ resource->mandatory_prop->writable = TRUE;
+
+ resource->shared_prop = create_property(rset->mgr->ctx, buf,
+ RESOURCE_IFACE, "b", PROP_SHARED, shared, free_value);
+
+ if (!resource->shared_prop) {
+ mrp_free(shared);
+ mrp_free(name);
+ goto error;
+ }
+
+ resource->shared_prop->writable = TRUE;
+
+ resource->name_prop = create_property(rset->mgr->ctx, buf,
+ RESOURCE_IFACE, "s", PROP_NAME, name, free_value);
+
+ if (!resource->name_prop) {
+ mrp_free(name);
+ goto error;
+ }
+
+ resource->status_prop = create_property(rset->mgr->ctx, buf,
+ RESOURCE_IFACE, "s", PROP_STATUS, "pending", NULL);
+
+ if (!resource->status_prop)
+ goto error;
+
+ resource_id = mrp_resource_definition_get_resource_id_by_name(name);
+
+ attrs = mrp_resource_definition_read_all_attributes(resource_id, 128,
+ attr_buf);
+ i = attrs;
+
+ resource->rset = rset;
+ resource->path = mrp_strdup(buf);
+
+ if (!resource->path)
+ goto error;
+
+ conf = mrp_htbl_create(&map_conf);
+
+ if (!conf)
+ goto error;
+
+ while (i->name != NULL) {
+
+ copy = mrp_allocz(sizeof(mrp_attr_t));
+
+ if (!copy)
+ goto error_delete_conf;
+
+ memcpy(copy, i, sizeof(mrp_attr_t));
+ copy->name = mrp_strdup(i->name);
+
+ if (!copy->name) {
+ mrp_free(copy);
+ goto error_delete_conf;
+ }
+
+ if (i->type == mqi_string) {
+ copy->value.string = mrp_strdup(i->value.string);
+ if (!copy->value.string) {
+ mrp_free((void *) copy->name);
+ mrp_free(copy);
+ goto error_delete_conf;
+ }
+ }
+ mrp_htbl_insert(conf, (void *) copy->name, copy);
+ i++;
+ }
+
+ resource->conf_prop = create_property(rset->mgr->ctx, buf,
+ RESOURCE_IFACE, "a{sv}", PROP_ATTRIBUTES_CONF, conf, free_map);
+
+ if (!resource->conf_prop) {
+ goto error;
+ }
+
+ resource->arguments_prop = create_property(rset->mgr->ctx, buf,
+ RESOURCE_IFACE, "a{sv}", PROP_ATTRIBUTES, conf, NULL);
+
+ if (!resource->arguments_prop) {
+ goto error;
+ }
+
+ return resource;
+
+error_delete_conf:
+ mrp_htbl_destroy(conf, TRUE);
+
+error:
+ if (resource)
+ destroy_resource(resource);
+
+ return NULL;
+}
+
+
+static void destroy_rset(resource_set_o_t *rset)
+{
+ dbus_data_t *ctx;
+
+ if (!rset)
+ return;
+
+ ctx = rset->mgr->ctx;
+
+ mrp_log_info("destroy rset %s", rset->path);
+
+ mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_DELETE,
+ rset_cb, ctx);
+ mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_RELEASE,
+ rset_cb, ctx);
+ mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_REQUEST,
+ rset_cb, ctx);
+ mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_ADD_RESOURCE,
+ rset_cb, ctx);
+ mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE, RSET_SET_PROPERTY,
+ rset_cb, ctx);
+ mrp_dbus_remove_method(ctx->dbus, rset->path, RSET_IFACE,
+ RSET_GET_PROPERTIES, rset_cb, ctx);
+
+ if (rset->resources)
+ mrp_htbl_destroy(rset->resources, TRUE);
+
+ destroy_property(rset->class_prop);
+ destroy_property(rset->status_prop);
+ destroy_property(rset->resources_prop);
+ destroy_property(rset->available_resources_prop);
+
+ if (ctx->tracking)
+ mrp_dbus_forget_name(ctx->dbus, rset->owner, dbus_name_cb, rset);
+
+ if (rset->set) {
+ mrp_resource_set_destroy(rset->set);
+ rset->set = NULL;
+ }
+
+ mrp_free(rset->path);
+ mrp_free(rset->owner);
+
+ mrp_free(rset);
+}
+
+
+static resource_set_o_t * create_rset(manager_o_t *mgr, uint32_t id,
+ const char *sender)
+{
+ char buf[MAX_PATH_LENGTH];
+ char *resbuf[128];
+ int ret;
+ mrp_htbl_config_t resources_conf;
+ resource_set_o_t *rset = NULL;
+ char **resources_arr;
+ char **available_resources_arr;
+
+ if (!sender)
+ goto error;
+
+ rset = mrp_allocz(sizeof(resource_set_o_t));
+
+ if (!rset)
+ goto error;
+
+ ret = snprintf(buf, MAX_PATH_LENGTH, "%s/%u", MURPHY_PATH_BASE, id);
+
+ if (ret < 0 || ret >= MAX_PATH_LENGTH)
+ goto error;
+
+ rset->mgr = mgr;
+ rset->path = mrp_strdup(buf);
+
+ if (!rset->path)
+ goto error;
+
+ resources_conf.comp = mrp_string_comp;
+ resources_conf.hash = mrp_string_hash;
+ resources_conf.free = htbl_free_resources;
+ resources_conf.nbucket = 0;
+ resources_conf.nentry = 10;
+
+ rset->resources = mrp_htbl_create(&resources_conf);
+
+ if (!rset->resources)
+ goto error;
+
+ resources_arr = mrp_allocz(sizeof(char **));
+ if (!resources_arr)
+ goto error;
+ resources_arr[0] = NULL;
+
+ rset->resources_prop = create_property(mgr->ctx, rset->path,
+ RSET_IFACE, "ao", PROP_RESOURCES, resources_arr, free_string_array);
+
+ if (!rset->resources_prop)
+ goto error;
+
+ rset->class_prop = create_property(mgr->ctx, rset->path,
+ RSET_IFACE, "s", PROP_CLASS,
+ mrp_strdup(rset->mgr->ctx->default_class), free_value);
+
+ if (!rset->class_prop)
+ goto error;
+
+ rset->class_prop->writable = TRUE;
+
+ rset->status_prop = create_property(mgr->ctx, rset->path,
+ RSET_IFACE, "s", PROP_STATUS, "pending", NULL);
+
+ if (!rset->status_prop)
+ goto error;
+
+ available_resources_arr = copy_string_array(
+ mrp_resource_definition_get_all_names(128,
+ (const char **) resbuf));
+
+ if (!available_resources_arr)
+ goto error;
+
+ rset->available_resources_prop = create_property(mgr->ctx,
+ rset->path, RSET_IFACE, "as", PROP_AVAILABLE_RESOURCES,
+ available_resources_arr, free_string_array);
+
+ if (!rset->available_resources_prop)
+ goto error;
+
+ rset->owner = mrp_strdup(sender);
+
+ if (!rset->owner)
+ goto error;
+
+ /* start following the owner */
+ if (mgr->ctx->tracking)
+ mrp_dbus_follow_name(mgr->ctx->dbus, rset->owner, dbus_name_cb, rset);
+
+ rset->set = mrp_resource_set_create(mgr->client, 0, 0, 0, event_cb,
+ rset);
+
+ if (!rset->set) {
+ mrp_log_error("Failed to create resource set");
+ goto error;
+ }
+
+ rset->error = FALSE;
+
+ return rset;
+
+error:
+ if (rset) {
+ destroy_rset(rset);
+ }
+
+ return NULL;
+}
+
+
+static void dbus_name_cb(mrp_dbus_t *dbus, const char *name, int up,
+ const char *owner, void *user_data)
+{
+ mrp_log_info("dbus_name_cb: %s status %d, owner %s", name, up, owner);
+
+ MRP_UNUSED(dbus);
+
+ if (up == 0) {
+ /* a client that we've been tracking has just died */
+ resource_set_o_t *rset = user_data;
+ manager_o_t *mgr = rset->mgr;
+ mrp_htbl_remove(mgr->rsets, (void *) rset->path, TRUE);
+ update_property(mgr->rsets_prop, htbl_keys(mgr->rsets));
+ }
+}
+
+
+static void htbl_free_rsets(void *key, void *object)
+{
+ resource_set_o_t *rset = object;
+
+ MRP_UNUSED(key);
+
+ destroy_rset(rset);
+}
+
+
+static int parse_path(const char *path, uint32_t *rset_id,
+ uint32_t *resource_id)
+{
+ *rset_id = -1;
+ *resource_id = -1;
+
+ int base_len = strlen(MURPHY_PATH_BASE);
+ int path_len = strlen(path);
+
+ char *p = (char *) path;
+ char *first_sep = NULL;
+ char *second_sep = NULL;
+ char *guard = (char *) path + path_len;
+
+ if (base_len < 3)
+ return FALSE; /* parsing corner case */
+
+ if (path_len < base_len + 4)
+ return FALSE; /* need to have at least "/1/2" */
+
+ if (strncmp(path, MURPHY_PATH_BASE, base_len) != 0)
+ return FALSE;
+
+ p += base_len;
+
+ if (*p != '/')
+ return FALSE;
+
+ first_sep = p;
+
+ p++;
+
+ while (p != guard) {
+ if (*p == '/') {
+ second_sep = p;
+ }
+ p++;
+ }
+
+ if (!second_sep)
+ return FALSE;
+
+ if (second_sep + 1 == guard)
+ return FALSE; /* missing resource id */
+
+ /* ok, the rset_id is between first_sep and second_sep, and
+ * resource_id is between second_sep and guard */
+
+ p = NULL;
+
+ *rset_id = strtol(first_sep + 1, &p, 10);
+
+ if (p != second_sep)
+ return FALSE;
+
+ *resource_id = strtol(second_sep + 1, &p, 10);
+
+ if (p != guard)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+struct attr_iter_s {
+ mrp_attr_t *attrs;
+ int count;
+};
+
+
+static int collect_attrs_cb(void *key, void *object, void *user_data)
+{
+ mrp_attr_t *attr = object;
+ mrp_attr_t *copy;
+ struct attr_iter_s *s = user_data;
+ MRP_UNUSED(key);
+
+ copy = &s->attrs[s->count];
+
+ memcpy(copy, attr, sizeof(mrp_attr_t));
+
+ if (attr->type == mqi_string) {
+ copy->value.string = mrp_strdup(attr->value.string);
+ }
+ copy->name = mrp_strdup(attr->name);
+
+ s->count++;
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+
+static void update_attributes(const char *resource_name,
+ mrp_resource_set_t *set, mrp_htbl_t *attr_map)
+{
+ int count = 0;
+ mrp_htbl_foreach(attr_map, count_keys_cb, &count);
+
+ {
+ struct attr_iter_s iter;
+ mrp_attr_t attrs[count+1];
+
+ memset(attrs, 0, (count+1)*sizeof(mrp_attr_t));
+
+ iter.count = 0;
+ iter.attrs = attrs;
+
+ /* add the attributes */
+ mrp_htbl_foreach(attr_map, collect_attrs_cb, &iter);
+
+ /* FIXME: this breaks down if there are two resources of the same name
+ * in a resource set */
+
+ mrp_resource_set_write_attributes(set, resource_name, attrs);
+ free_attr_array(attrs);
+ }
+}
+
+
+static int update_conf_cb(void *key, void *object, void *user_data)
+{
+ mrp_htbl_t **confs = user_data;
+ mrp_attr_t *old_attr = object;
+
+ mrp_htbl_t *new_conf = confs[0];
+
+ if (mrp_htbl_lookup(new_conf, key) == NULL) {
+ /* copy the attribute */
+ mrp_attr_t *attr = mrp_allocz(sizeof(mrp_attr_t));
+
+ if (!attr) {
+ goto error;
+ }
+ attr->name = mrp_strdup(old_attr->name);
+ if (!attr->name) {
+ mrp_free(attr);
+ goto error;
+ }
+ attr->type = old_attr->type;
+ switch (attr->type) {
+ case mqi_string:
+ attr->value.string = mrp_strdup(old_attr->value.string);
+ if (!attr->value.string) {
+ mrp_free((void *) attr->name);
+ mrp_free(attr);
+ goto error;
+ }
+ break;
+ case mqi_integer:
+ attr->value.integer = old_attr->value.integer;
+ break;
+ case mqi_unsignd:
+ attr->value.unsignd = old_attr->value.unsignd;
+ break;
+ case mqi_floating:
+ attr->value.floating = old_attr->value.floating;
+ break;
+ default:
+ goto error;
+ }
+ /* add the value to the conf */
+ mrp_htbl_insert(new_conf, (void *) attr->name, attr);
+ }
+
+ confs[1] = new_conf; /* indicate success */
+
+ return MRP_HTBL_ITER_MORE;
+
+error:
+ confs[1] = NULL; /* indicate error */
+ return MRP_HTBL_ITER_STOP;
+}
+
+
+
+static int resource_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
+{
+ const char *member = mrp_dbus_msg_member(msg);
+ const char *iface = mrp_dbus_msg_interface(msg);
+ const char *path = mrp_dbus_msg_path(msg);
+ char *error_msg = "Received invalid message";
+ mrp_dbus_msg_t *reply = NULL;
+ char buf[MAX_PATH_LENGTH];
+
+ dbus_data_t *ctx = data;
+
+ uint32_t rset_id, resource_id;
+
+ resource_set_o_t *rset;
+ resource_o_t *resource;
+
+ int ret;
+
+ mrp_log_info("Resource callback called -- member: '%s', path: '%s',"
+ " interface: '%s'", member, path, iface);
+
+ /* parse the rset id and resource id */
+
+ if (!parse_path(path, &rset_id, &resource_id)) {
+ mrp_log_error("Failed to parse path");
+ goto error_reply;
+ }
+
+ ret = snprintf(buf, MAX_PATH_LENGTH, "%s/%u", MURPHY_PATH_BASE, rset_id);
+
+ if (ret < 0 || ret >= MAX_PATH_LENGTH)
+ goto error_reply;
+
+ rset = mrp_htbl_lookup(ctx->mgr->rsets, buf);
+
+ if (!rset)
+ goto error_reply;
+
+ resource = mrp_htbl_lookup(rset->resources, (void *) path);
+
+ if (!resource)
+ goto error_reply;
+
+ if (strcmp(member, RESOURCE_GET_PROPERTIES) == 0) {
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply)
+ goto error;
+
+ mrp_log_info("getProperties of resource %s", path);
+
+ mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_ARRAY, "{sv}");
+
+ if (!(get_property_dict_entry(resource->name_prop, reply) &&
+ get_property_dict_entry(resource->status_prop, reply) &&
+ get_property_dict_entry(resource->mandatory_prop, reply) &&
+ get_property_dict_entry(resource->shared_prop, reply) &&
+ get_property_dict_entry(resource->arguments_prop, reply) &&
+ get_property_dict_entry(resource->conf_prop, reply))) {
+ goto error_reply;
+ }
+
+ mrp_dbus_msg_close_container(reply);
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ else if (strcmp(member, RESOURCE_SET_PROPERTY) == 0) {
+ const char *name;
+ char *sig;
+
+ mrp_log_info("setProperty of resource %s", path);
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &name)) {
+ goto error_reply;
+ }
+
+ /* get the type of key 'name' */
+ if (strcmp(name, PROP_MANDATORY) == 0) {
+ uint32_t v = 0;
+ bool *value;
+ sig = "b";
+
+ value = mrp_allocz(sizeof(bool));
+ if (!value) {
+ error_msg = "internal error";
+ goto error_reply;
+ }
+
+ mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, sig);
+
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &v);
+ *value = !!v;
+
+ update_property(resource->mandatory_prop, value);
+
+ mrp_dbus_msg_exit_container(msg);
+ }
+ else if (strcmp(name, PROP_SHARED) == 0) {
+ uint32_t v = 0;
+ bool *value;
+ sig = "b";
+ value = mrp_allocz(sizeof(bool));
+
+ if (!value) {
+ error_msg = "Internal error";
+ goto error_reply;
+ }
+
+ mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, sig);
+
+ mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &v);
+ *value = !!v;
+
+ update_property(resource->shared_prop, value);
+
+ mrp_dbus_msg_exit_container(msg);
+ }
+ else if (strcmp(name, PROP_ATTRIBUTES_CONF) == 0) {
+ mrp_htbl_config_t map_conf;
+ mrp_htbl_t *conf;
+ int new_count = 0;
+ int old_count = 0;
+
+ sig = "a{sv}";
+
+ if (resource->rset->locked != 0) {
+ error_msg = "Resource set cannot be changed after requesting";
+ goto error_reply;
+ }
+
+ if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, sig)) {
+ error_msg = "Invalid message";
+ goto error_reply;
+ }
+
+ if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, "{sv}")) {
+ error_msg = "Invalid message";
+ goto error_reply;
+ }
+
+ map_conf.comp = mrp_string_comp;
+ map_conf.hash = mrp_string_hash;
+ map_conf.free = htbl_free_args;
+ map_conf.nbucket = 0;
+ map_conf.nentry = 10;
+
+ conf = mrp_htbl_create(&map_conf);
+
+ if (!conf) {
+ error_msg = "Internal error";
+ goto error_reply;
+ }
+
+ while (mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_DICT_ENTRY, "sv")) {
+ char *key;
+ mrp_attr_t *prev_value;
+ mrp_attr_t *new_value;
+ const char *value_sig;
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &key)) {
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+ prev_value = mrp_htbl_lookup(resource->conf_prop->value, key);
+
+ if (!prev_value) {
+ mrp_log_error("no previous value %s in attributes", key);
+ error_msg = "Configuration attribute definition missing";
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+
+ value_sig = get_dbus_type(prev_value);
+
+ if (!value_sig) {
+ error_msg = "Failed to map database value to D-Bus signature";
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+
+ if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, value_sig)) {
+ error_msg = "Invalid message";
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+
+ new_value = mrp_allocz(sizeof(mrp_attr_t));
+ if (!new_value) {
+ error_msg = "Internal error";
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+
+ switch(prev_value->type) {
+ case mqi_string:
+ {
+ char *value;
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &value)) {
+ mrp_free(new_value);
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+ new_value->name = mrp_strdup(key);
+ new_value->type = mqi_string;
+ new_value->value.string = mrp_strdup(value);
+ break;
+ }
+ case mqi_unsignd:
+ {
+ uint32_t value;
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &value)) {
+ mrp_free(new_value);
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+ new_value->name = mrp_strdup(key);
+ new_value->type = mqi_unsignd;
+ new_value->value.unsignd = value;
+ break;
+ }
+ case mqi_integer:
+ {
+ int32_t value;
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_INT32, &value)) {
+ mrp_free(new_value);
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+ new_value->name = mrp_strdup(key);
+ new_value->type = mqi_integer;
+ new_value->value.integer = value;
+ break;
+ }
+ case mqi_floating:
+ {
+ double value;
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_DOUBLE, &value)) {
+ mrp_free(new_value);
+ mrp_htbl_destroy(conf, TRUE);
+ goto error_reply;
+ }
+ new_value->name = mrp_strdup(key);
+ new_value->type = mqi_floating;
+ new_value->value.floating = value;
+ break;
+ }
+ default:
+ mrp_htbl_destroy(conf, TRUE);
+ mrp_free(new_value);
+ error_msg = "Attribute value unknown";
+ goto error_reply;
+ }
+
+ mrp_dbus_msg_exit_container(msg);
+
+ mrp_htbl_insert(conf, (void *) new_value->name, new_value);
+ new_count++;
+ }
+
+ /* What about if not all properties were set? Maybe
+ * update_property should merge the old map with the new map.
+ * For now, just check the the size is the same. */
+
+ mrp_htbl_foreach(resource->conf_prop->value, count_keys_cb,
+ &old_count);
+
+ if (old_count > new_count) {
+ /* for every key in old conf, add the key to new conf if it's
+ * not there already */
+
+ /* the second value is return value for errors */
+ mrp_htbl_t *confs[2] = { conf, NULL };
+
+ mrp_htbl_foreach(resource->conf_prop->value, update_conf_cb,
+ confs);
+
+ if (confs[1] == NULL) {
+ mrp_htbl_destroy(conf, TRUE);
+ error_msg = "attribute merging failed";
+ goto error_reply;
+ }
+ }
+ else if (old_count < new_count) {
+ mrp_htbl_destroy(conf, TRUE);
+ error_msg = "setting too many attributes";
+ goto error_reply;
+ }
+
+ update_property(resource->conf_prop, conf);
+ update_property(resource->arguments_prop, conf);
+
+ if (resource->rset->locked) {
+ /* if the resource set is already created for the library,
+ * we can set the attributes */
+ update_attributes(resource->name_prop->value,
+ resource->rset->set, conf);
+ }
+
+ mrp_dbus_msg_exit_container(msg);
+ }
+ else {
+ error_msg = "Resource property read-only or missing";
+ goto error_reply;
+ }
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply)
+ goto error;
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ else if (strcmp(member, RESOURCE_DELETE) == 0) {
+ mrp_log_info("Deleting resource %s", path);
+
+ mrp_htbl_remove(rset->resources, (void *) path, TRUE);
+ update_property(rset->resources_prop, htbl_keys(rset->resources));
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply)
+ goto error;
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+
+ return TRUE;
+
+error_reply:
+ {
+ mrp_dbus_err_t err;
+ mrp_dbus_error_init(&err);
+ mrp_dbus_error_set(&err, "org.freedesktop.DBus.Error.Failed", error_msg);
+
+ if (reply) {
+ /* something was already done -- free some memory */
+ mrp_dbus_msg_unref(reply);
+ }
+
+ reply = mrp_dbus_msg_error(dbus, msg, &err);
+
+ if (reply) {
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ }
+ return TRUE;
+
+error:
+ return TRUE;
+}
+
+
+static int add_resource_cb(void *key, void *object, void *user_data)
+{
+ resource_o_t *r = object;
+ resource_set_o_t *rset = user_data;
+
+ bool shared = *(bool *) r->shared_prop->value;
+ bool mandatory = *(bool *) r->mandatory_prop->value;
+ char *name = r->name_prop->value;
+
+ int count = 0;
+
+ MRP_UNUSED(key);
+
+ /* count the attributes */
+ mrp_htbl_foreach(r->conf_prop->value, count_keys_cb, &count);
+
+ if (mrp_resource_set_add_resource(rset->set, name, shared, NULL, mandatory)
+ >= 0) {
+ update_attributes(name, rset->set, r->conf_prop->value);
+ }
+ else {
+ mrp_log_error("Error adding the resource to resource set!");
+ rset->error = TRUE;
+ }
+
+ return MRP_HTBL_ITER_MORE;
+}
+
+static inline int initialize_resource_set(resource_set_o_t *rset)
+{
+ /* add the resources */
+ mrp_htbl_foreach(rset->resources, add_resource_cb, rset);
+ if (rset->error) {
+ /* could not add the resource to resource set */
+ rset->error = FALSE;
+ return FALSE;
+ }
+
+ if (mrp_application_class_add_resource_set(
+ (char *) rset->class_prop->value,
+ rset->mgr->zone, rset->set, 0) < 0) {
+ /* This is actually quite serious, since most likely we cannot
+ * ever get this to work. The zone is most likely not defined.
+ * The resource library is known to crash if the rset->set
+ * pointer is used for acquiring.
+ */
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static int rset_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
+{
+ const char *member = mrp_dbus_msg_member(msg);
+ const char *iface = mrp_dbus_msg_interface(msg);
+ const char *path = mrp_dbus_msg_path(msg);
+ char *error_msg = "Received invalid message";
+ int requesting = 0;
+
+ mrp_dbus_msg_t *reply;
+
+ dbus_data_t *ctx = data;
+
+ resource_set_o_t *rset = mrp_htbl_lookup(ctx->mgr->rsets, (void *) path);
+
+ mrp_log_info("Resource set callback called -- member: '%s', path: '%s',"
+ " interface: '%s'", member, path, iface);
+
+ if (!rset) {
+ mrp_log_error("Resource set '%s' not found, ignoring", path);
+ goto error;
+ }
+
+ if (strcmp(member, RSET_GET_PROPERTIES) == 0) {
+
+ /* FIXME: check return values */
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply)
+ goto error;
+
+ mrp_log_info("getProperties of rset %s", path);
+
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_ARRAY, "{sv}")) {
+ mrp_dbus_msg_unref(reply);
+ goto error_reply;
+ }
+
+ get_property_dict_entry(rset->class_prop, reply);
+ get_property_dict_entry(rset->status_prop, reply);
+ get_property_dict_entry(rset->resources_prop, reply);
+ get_property_dict_entry(rset->available_resources_prop, reply);
+
+ mrp_dbus_msg_close_container(reply);
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ else if (strcmp(member, RSET_ADD_RESOURCE) == 0) {
+ const char *name;
+
+ resource_o_t *resource;
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &name)) {
+ goto error_reply;
+ }
+
+ resource = create_resource(rset, name, rset->next_id++);
+
+ if (!resource)
+ goto error_reply;
+
+ if (!mrp_dbus_export_method(ctx->dbus, resource->path,
+ RESOURCE_IFACE, RESOURCE_GET_PROPERTIES, resource_cb,
+ ctx)) {
+ destroy_resource(resource);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, resource->path,
+ RESOURCE_IFACE, RESOURCE_SET_PROPERTY, resource_cb, ctx)) {
+ destroy_resource(resource);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, resource->path,
+ RESOURCE_IFACE, RESOURCE_DELETE, resource_cb, ctx)) {
+ destroy_resource(resource);
+ goto error_reply;
+ }
+
+ mrp_htbl_insert(rset->resources, (void *) resource->path,
+ resource);
+ update_property(rset->resources_prop, htbl_keys(rset->resources));
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply) {
+ mrp_htbl_remove(rset->resources, (void *) path, TRUE);
+ update_property(rset->resources_prop, htbl_keys(rset->resources));
+ goto error;
+ }
+
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_OBJECT_PATH, resource->path)) {
+ mrp_htbl_remove(rset->resources, (void *) path, TRUE);
+ update_property(rset->resources_prop, htbl_keys(rset->resources));
+ mrp_dbus_msg_unref(reply);
+ goto error_reply;
+ }
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+
+ mrp_log_info("created resource %s\n", resource->path);
+ }
+ /* Requesting and releasing sets mostly shares code,
+ * so we use the same code path, and set a variable to
+ * differentiate between the two modes of operation.
+ */
+ else if ((requesting = !strcmp(member, RSET_REQUEST)) ||
+ strcmp(member, RSET_RELEASE) == 0) {
+ if (requesting)
+ mrp_log_info("Requesting rset %s", path);
+ else
+ mrp_log_info("Releasing rset %s", path);
+
+ if (!rset->locked) {
+ if (!initialize_resource_set(rset)) {
+ error_msg = "Could not set up resource set; "
+ "possibly an unknown resource or zone";
+ goto error_reply;
+ }
+ }
+
+ rset->committed = TRUE;
+
+ if (requesting)
+ mrp_resource_set_acquire(rset->set, 0);
+ else
+ mrp_resource_set_release(rset->set, 0);
+
+ /* Due to limitations in resource library, this resource set cannot
+ * be changed anymore. This might change in the future.
+ */
+ rset->locked = TRUE;
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+ if (!reply)
+ goto error;
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ else if (strcmp(member, RSET_DELETE) == 0) {
+ mrp_log_info("Deleting rset %s", path);
+
+ mrp_htbl_remove(ctx->mgr->rsets, (void *) path, TRUE);
+ update_property(ctx->mgr->rsets_prop, htbl_keys(ctx->mgr->rsets));
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+ if (!reply)
+ goto error;
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ else if (strcmp(member, RSET_SET_PROPERTY) == 0) {
+ char *name = NULL;
+ char *value = NULL;
+
+ if (rset->locked) {
+ error_msg = "Resource set cannot be changed after requesting";
+ goto error_reply;
+ }
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &name)) {
+ error_msg = "Message didn't contain the property name";
+ goto error_reply;
+ }
+
+ if (strcmp(name, PROP_CLASS) != 0) {
+ error_msg = "Unknown property name in message";
+ goto error_reply;
+ }
+
+ if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, "s")) {
+ error_msg = "Property value isn't contained inside a variant";
+ goto error_reply;
+ }
+
+ if (!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &value)) {
+ mrp_dbus_msg_exit_container(msg);
+ goto error_reply;
+ }
+
+ update_property(rset->class_prop, mrp_strdup(value));
+
+ mrp_dbus_msg_exit_container(msg);
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply)
+ goto error;
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+
+ return TRUE;
+
+error_reply:
+ {
+ mrp_dbus_err_t err;
+ mrp_dbus_error_init(&err);
+ mrp_dbus_error_set(&err, "org.freedesktop.DBus.Error.Failed", error_msg);
+
+ mrp_log_error("rset_cb failure: %s", error_msg);
+
+ reply = mrp_dbus_msg_error(dbus, msg, &err);
+
+ if (reply) {
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ }
+
+error:
+ return TRUE;
+}
+
+/*
+ * Updates or creates a property that contains the available
+ * application classes. Returns -1 in case of failure (property
+ * is kept as-is), 0 if the requested list is empty, and 1 if the
+ * requested list is not empty.
+ */
+static int update_classes(dbus_data_t *ctx, property_o_t **prop)
+{
+ property_o_t *res_classes_prop = NULL;
+ const char **orig_classes_array = mrp_application_class_get_all_names(0,
+ NULL);
+ char **res_classes_array = NULL;
+ int arr_has_content = -1;
+
+ if (!orig_classes_array) {
+ mrp_log_error("Failed to get application classes");
+ goto error;
+ }
+
+ res_classes_array = copy_string_array(orig_classes_array);
+ if (!res_classes_array) {
+ mrp_log_error("Failed to copy application classes");
+ goto error;
+ }
+
+ res_classes_prop = create_property(ctx, MURPHY_PATH_BASE,
+ MANAGER_IFACE, "as", PROP_AVAILABLE_CLASSES,
+ res_classes_array, free_string_array);
+ if (!res_classes_prop) {
+ mrp_log_error("Failed to create a property");
+ free_string_array(res_classes_array);
+ goto error;
+ }
+
+ if (*res_classes_array) {
+ mrp_log_info("Application class listing is non-empty");
+ arr_has_content = 1;
+ } else {
+ mrp_log_info("Application class listing is empty");
+ arr_has_content = 0;
+ }
+
+ /* Remove the old prop if new is valid */
+ destroy_property(*prop);
+
+ *prop = res_classes_prop;
+
+ /* Clean up and return */
+error:
+ free_value(orig_classes_array);
+
+ return arr_has_content;
+}
+
+static int mgr_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
+{
+ const char *member = mrp_dbus_msg_member(msg);
+ const char *iface = mrp_dbus_msg_interface(msg);
+ const char *path = mrp_dbus_msg_path(msg);
+ int ret = -1;
+
+ mrp_dbus_msg_t *reply;
+
+ dbus_data_t *ctx = data;
+
+ mrp_log_info("Manager callback called -- member: '%s', path: '%s',"
+ " interface: '%s'", member, path, iface);
+
+ if (strcmp(member, MANAGER_GET_PROPERTIES) == 0) {
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+
+ if (!reply)
+ goto error;
+
+ mrp_log_info("getProperties of manager %s", path);
+
+ if (!mrp_dbus_msg_open_container(reply, MRP_DBUS_TYPE_ARRAY, "{sv}")) {
+ goto error_reply;
+ }
+
+ get_property_dict_entry(ctx->mgr->rsets_prop, reply);
+
+ /* Update classes if our array is empty */
+ if (!ctx->has_classes) {
+ mrp_log_info("Updating resource classes as they were not set");
+ ret = update_classes(ctx, &ctx->mgr->available_classes_prop);
+ if (ret < 0 || !ctx->mgr->available_classes_prop) {
+ mrp_log_error("Updating available classes failed (ret=%d, ptr=%p)",
+ ret, ctx->mgr->available_classes_prop);
+ goto error_reply;
+ }
+
+ /* Update the status */
+ ctx->has_classes = ret;
+ }
+
+ get_property_dict_entry(ctx->mgr->available_classes_prop, reply);
+
+ mrp_dbus_msg_close_container(reply);
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ else if (strcmp(member, MANAGER_CREATE_RESOURCE_SET) == 0) {
+ const char *sender = mrp_dbus_msg_sender(msg);
+ resource_set_o_t *rset = create_rset(ctx->mgr, ctx->mgr->next_id++,
+ sender);
+
+ if (!rset)
+ goto error_reply;
+
+ if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+ RSET_IFACE, RSET_GET_PROPERTIES, rset_cb, ctx)) {
+ destroy_rset(rset);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+ RSET_IFACE, RSET_SET_PROPERTY, rset_cb, ctx)) {
+ destroy_rset(rset);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+ RSET_IFACE, RSET_ADD_RESOURCE, rset_cb, ctx)) {
+ destroy_rset(rset);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+ RSET_IFACE, RSET_REQUEST, rset_cb, ctx)) {
+ destroy_rset(rset);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+ RSET_IFACE, RSET_RELEASE, rset_cb, ctx)) {
+ destroy_rset(rset);
+ goto error_reply;
+ }
+ if (!mrp_dbus_export_method(ctx->dbus, rset->path,
+ RSET_IFACE, RSET_DELETE, rset_cb, ctx)) {
+ destroy_rset(rset);
+ goto error_reply;
+ }
+
+ mrp_htbl_insert(ctx->mgr->rsets, (void *) rset->path, rset);
+ update_property(ctx->mgr->rsets_prop, htbl_keys(ctx->mgr->rsets));
+
+ reply = mrp_dbus_msg_method_return(dbus, msg);
+ if (!reply) {
+ mrp_htbl_remove(ctx->mgr->rsets, (void *) rset->path, TRUE);
+ update_property(ctx->mgr->rsets_prop, htbl_keys(ctx->mgr->rsets));
+ goto error;
+ }
+
+ if (!mrp_dbus_msg_append_basic(reply, MRP_DBUS_TYPE_OBJECT_PATH,
+ rset->path)) {
+ mrp_htbl_remove(ctx->mgr->rsets, (void *) rset->path, TRUE);
+ update_property(ctx->mgr->rsets_prop, htbl_keys(ctx->mgr->rsets));
+ mrp_dbus_msg_unref(reply);
+ goto error_reply;
+ }
+
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+
+ mrp_log_info("created resource set %s\n", rset->path);
+ }
+
+ return TRUE;
+
+error_reply:
+ {
+ mrp_dbus_err_t err;
+ mrp_dbus_error_init(&err);
+ mrp_dbus_error_set(&err, "org.freedesktop.DBus.Error.Failed", "Received invalid message");
+
+ reply = mrp_dbus_msg_error(dbus, msg, &err);
+
+ if (reply) {
+ mrp_dbus_send_msg(dbus, reply);
+ mrp_dbus_msg_unref(reply);
+ }
+ }
+
+error:
+ return TRUE;
+}
+
+
+static void destroy_manager(manager_o_t *mgr)
+{
+ if (!mgr)
+ return;
+
+ mrp_dbus_remove_method(mgr->ctx->dbus, MURPHY_PATH_BASE,
+ MANAGER_IFACE, MANAGER_CREATE_RESOURCE_SET, mgr_cb, mgr->ctx);
+
+ mrp_dbus_remove_method(mgr->ctx->dbus, MURPHY_PATH_BASE,
+ MANAGER_IFACE, MANAGER_GET_PROPERTIES, mgr_cb, mgr->ctx);
+
+ mrp_htbl_destroy(mgr->rsets, TRUE);
+ destroy_property(mgr->rsets_prop);
+ destroy_property(mgr->available_classes_prop);
+
+ mrp_resource_client_destroy(mgr->client);
+
+ mrp_free(mgr);
+}
+
+
+static manager_o_t *create_manager(dbus_data_t *ctx)
+{
+ manager_o_t *mgr = mrp_allocz(sizeof(manager_o_t));
+ char **rset_arr = NULL;
+ mrp_htbl_config_t rsets_conf;
+ int ret = -1;
+
+ if (!mgr)
+ goto error;
+
+ mgr->ctx = ctx;
+
+ rset_arr = mrp_allocz(sizeof(char **));
+ if (!rset_arr)
+ goto error;
+ rset_arr[0] = NULL;
+
+ /* FIXME: duplication of code? */
+
+ mgr->rsets_prop = create_property(ctx, MURPHY_PATH_BASE, MANAGER_IFACE,
+ "ao", PROP_RESOURCE_SETS, rset_arr, free_string_array);
+
+ if (!mgr->rsets_prop)
+ goto error;
+
+ ret = update_classes(ctx, &mgr->available_classes_prop);
+ if (ret < 0 || !mgr->available_classes_prop) {
+ mrp_log_error("Failure to get the resource classes (ret=%d, p=%p)",
+ ret, mgr->available_classes_prop);
+ goto error;
+ }
+
+ ctx->has_classes = ret;
+
+ rsets_conf.comp = mrp_string_comp;
+ rsets_conf.hash = mrp_string_hash;
+ rsets_conf.free = htbl_free_rsets;
+ rsets_conf.nbucket = 0;
+ rsets_conf.nentry = 10;
+
+ mgr->rsets = mrp_htbl_create(&rsets_conf);
+
+ if (!mgr->rsets)
+ goto error;
+
+ mgr->client = mrp_resource_client_create("dbus", ctx);
+
+ if (!mgr->client)
+ goto error;
+
+ mgr->zone = ctx->default_zone;
+
+ return mgr;
+
+error:
+
+ destroy_manager(mgr);
+
+ return NULL;
+}
+
+
+static int dbus_resource_init(mrp_plugin_t *plugin)
+{
+ mrp_plugin_arg_t *args = plugin->args;
+ dbus_data_t *ctx = mrp_allocz(sizeof(dbus_data_t));
+ mrp_dbus_err_t err;
+
+ if (!ctx)
+ goto error;
+
+ ctx->ml = plugin->ctx->ml;
+ ctx->addr = args[ARG_DR_SERVICE].str;
+ ctx->tracking = args[ARG_DR_TRACK_CLIENTS].bln;
+ ctx->default_zone = args[ARG_DR_DEFAULT_ZONE].str;
+ ctx->default_class = args[ARG_DR_DEFAULT_CLASS].str;
+ ctx->bus = args[ARG_DR_BUS].str;
+
+ mrp_log_info("Connecting to bus '%s'", ctx->bus);
+
+ ctx->dbus = mrp_dbus_connect(plugin->ctx->ml, ctx->bus,
+ mrp_dbus_error_init(&err));
+
+ if (!ctx->dbus) {
+ mrp_log_error("Failed to connect to D-Bus: %s", err.message);
+ goto error;
+ }
+
+ ctx->mgr = create_manager(ctx);
+
+ if (!ctx->mgr) {
+ mrp_log_error("Failed to create manager");
+ goto error;
+ }
+
+ if (!mrp_dbus_acquire_name(ctx->dbus, ctx->addr, NULL)) {
+ mrp_log_error("Failed to acquire name '%s' on D-Bus", ctx->addr);
+ goto error;
+ }
+
+ /* in the beginning we only export the manager interface -- the
+ * rest is created dynamically
+ */
+
+ if (!mrp_dbus_export_method(ctx->dbus, MURPHY_PATH_BASE,
+ MANAGER_IFACE, MANAGER_CREATE_RESOURCE_SET, mgr_cb, ctx)) {
+ mrp_log_error("Failed to register manager object");
+ goto error;
+ }
+
+ if (!mrp_dbus_export_method(ctx->dbus, MURPHY_PATH_BASE,
+ MANAGER_IFACE, MANAGER_GET_PROPERTIES, mgr_cb, ctx)) {
+ mrp_log_error("Failed to register manager object");
+ goto error;
+ }
+
+ return TRUE;
+
+error:
+ if (ctx) {
+ destroy_manager(ctx->mgr);
+ mrp_free(ctx);
+ }
+
+ return FALSE;
+}
+
+
+static void dbus_resource_exit(mrp_plugin_t *plugin)
+{
+ dbus_data_t *ctx = plugin->data;
+
+ mrp_dbus_release_name(ctx->dbus, ctx->addr, NULL);
+ mrp_dbus_unref(ctx->dbus);
+ ctx->dbus = NULL;
+
+ mrp_htbl_destroy(ctx->mgr->rsets, TRUE);
+ destroy_manager(ctx->mgr);
+ mrp_free(ctx);
+
+ plugin->data = NULL;
+}
+
+
+#define DBUS_RESOURCE_DESCRIPTION "A plugin to implement D-Bus resource API."
+#define DBUS_RESOURCE_HELP "D-Bus resource manager backend"
+#define DBUS_RESOURCE_VERSION MRP_VERSION_INT(0, 0, 1)
+#define DBUS_RESOURCE_AUTHORS "Ismo Puustinen <ismo.puustinen@intel.com>"
+
+/* TODO: more arguments needed, such as:
+ * - security settings?
+ */
+static mrp_plugin_arg_t args[] = {
+ MRP_PLUGIN_ARGIDX(ARG_DR_BUS, STRING, "dbus_bus", "system"),
+ MRP_PLUGIN_ARGIDX(ARG_DR_SERVICE, STRING, "dbus_service", "org.Murphy"),
+ MRP_PLUGIN_ARGIDX(ARG_DR_DEFAULT_ZONE, STRING, "default_zone", "default"),
+ MRP_PLUGIN_ARGIDX(ARG_DR_DEFAULT_CLASS, STRING, "default_class", "default"),
+ MRP_PLUGIN_ARGIDX(ARG_DR_TRACK_CLIENTS, BOOL, "dbus_track", TRUE),
+};
+
+
+MURPHY_REGISTER_PLUGIN("resource-dbus",
+ DBUS_RESOURCE_VERSION, DBUS_RESOURCE_DESCRIPTION,
+ DBUS_RESOURCE_AUTHORS, DBUS_RESOURCE_HELP,
+ MRP_MULTIPLE, dbus_resource_init, dbus_resource_exit,
+ args, MRP_ARRAY_SIZE(args),
+ NULL, 0, NULL, 0, NULL);
--- /dev/null
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <systemd/sd-daemon.h>
+#include <systemd/sd-journal.h>
+
+#include <murphy/common.h>
+#include <murphy/core.h>
+
+static void sdlogger(void *data, mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, va_list ap)
+{
+ va_list cp;
+ int prio;
+ char filebuf[1024], linebuf[64];
+
+ MRP_UNUSED(data);
+
+ va_copy(cp, ap);
+ switch (level) {
+ case MRP_LOG_ERROR: prio = LOG_ERR; break;
+ case MRP_LOG_WARNING: prio = LOG_WARNING; break;
+ case MRP_LOG_INFO: prio = LOG_INFO; break;
+ case MRP_LOG_DEBUG: prio = LOG_DEBUG; break;
+ default: prio = LOG_INFO;
+ }
+
+ snprintf(filebuf, sizeof(filebuf), "CODE_FILE=%s", file);
+ snprintf(linebuf, sizeof(linebuf), "CODE_LINE=%d", line);
+ sd_journal_printv_with_location(prio, filebuf, linebuf, func, format, cp);
+
+ va_end(cp);
+}
+
+
+static int sdlogger_init(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ if (mrp_log_register_target("systemd", sdlogger, NULL))
+ mrp_log_info("systemd: registered logging target.");
+ else
+ mrp_log_error("systemd: failed to register logging target.");
+
+ return TRUE;
+}
+
+
+static void sdlogger_exit(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ mrp_log_unregister_target("systemd");
+
+ return;
+}
+
+#define SDLOGGER_DESCRIPTION "A systemd logger for Murphy."
+#define SDLOGGER_HELP "systemd logger support for Murphy."
+#define SDLOGGER_VERSION MRP_VERSION_INT(0, 0, 1)
+#define SDLOGGER_AUTHORS "Krisztian Litkey <kli@iki.fi>"
+
+MURPHY_REGISTER_PLUGIN("systemd",
+ SDLOGGER_VERSION, SDLOGGER_DESCRIPTION,
+ SDLOGGER_AUTHORS, SDLOGGER_HELP, MRP_SINGLETON,
+ sdlogger_init, sdlogger_exit,
+ NULL, 0, NULL, 0, NULL, 0, NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/json.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/console.h>
+#include <murphy/core/auth.h>
+#include <murphy/core/domain.h>
+
+
+typedef struct {
+ mrp_event_watch_t *w;
+} test_data_t;
+
+
+enum {
+ ARG_STRING1,
+ ARG_STRING2,
+ ARG_BOOLEAN1,
+ ARG_BOOLEAN2,
+ ARG_UINT321,
+ ARG_INT321,
+ ARG_DOUBLE1,
+ ARG_FAILINIT,
+ ARG_FAILEXIT,
+ ARG_OBJECT,
+ ARG_REST,
+};
+
+
+void one_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void two_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void three_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void four_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void resolve_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void auth_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void ping_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+void invoke_cb(mrp_console_t *c, void *user_data, int argc, char **argv);
+
+MRP_CONSOLE_GROUP(test_group, "test", NULL, NULL, {
+ MRP_TOKENIZED_CMD("one" , one_cb , TRUE,
+ "one [args]", "command 1", "description 1"),
+ MRP_TOKENIZED_CMD("two" , two_cb , FALSE,
+ "two [args]", "command 2", "description 2"),
+ MRP_TOKENIZED_CMD("three", three_cb, FALSE,
+ "three [args]", "command 3", "description 3"),
+ MRP_TOKENIZED_CMD("four" , four_cb , TRUE,
+ "four [args]", "command 4", "description 4"),
+ MRP_TOKENIZED_CMD("update" , resolve_cb , TRUE,
+ "update <target>", "update target", "update target"),
+ MRP_TOKENIZED_CMD("auth-test", auth_cb, TRUE,
+ "auth-test [@backend] target mode id [token]",
+ "test authentication", "test authentication"),
+ MRP_TOKENIZED_CMD("ping", ping_cb, FALSE,
+ "ping domain",
+ "ping the given domain", "ping a domain"),
+ MRP_TOKENIZED_CMD("invoke", invoke_cb, TRUE,
+ "invoke domain method [ərguments]",
+ "invoke the given domain method",
+ "invoke a domain method")
+});
+
+
+void one_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ int i;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ for (i = 0; i < argc; i++) {
+ printf("%s(): #%d: '%s'\n", __FUNCTION__, i, argv[i]);
+ }
+}
+
+
+void two_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ int i;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ for (i = 0; i < argc; i++) {
+ printf("%s(): #%d: '%s'\n", __FUNCTION__, i, argv[i]);
+ }
+}
+
+
+void three_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ int i;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ for (i = 0; i < argc; i++) {
+ printf("%s(): #%d: '%s'\n", __FUNCTION__, i, argv[i]);
+ }
+}
+
+
+void four_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ int i;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ for (i = 0; i < argc; i++) {
+ printf("%s(): #%d: '%s'\n", __FUNCTION__, i, argv[i]);
+ }
+}
+
+
+void resolve_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ mrp_context_t *ctx = c->ctx;
+ const char *target;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+
+ if (argc == 3) {
+ target = argv[2];
+
+ if (ctx->r != NULL) {
+ if (mrp_resolver_update_target(ctx->r, target, NULL) > 0)
+ printf("'%s' updated OK.\n", target);
+ else
+ printf("Failed to update '%s'.\n", target);
+ }
+ else
+ printf("Resolver/ruleset is not available.\n");
+ }
+ else {
+ printf("Usage: %s %s <target-name>\n", argv[0], argv[1]);
+ }
+}
+
+
+void auth_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ mrp_context_t *ctx = c->ctx;
+ const char *backend, *target, *id, *token, *p;
+ int idx, mode, status;
+
+ MRP_UNUSED(user_data);
+
+ if (argc < 4) {
+ error:
+ printf("Usage: %s %s [@backend] target mode id [token]\n", argv[0],
+ argv[1]);
+ return;
+ }
+
+ if (argv[2][0] == '@') {
+ if (argc < 5)
+ goto error;
+
+ backend = argv[2] + 1;
+ idx = 3;
+ }
+ else {
+ backend = NULL;
+ idx = 2;
+ }
+
+ target = argv[idx++];
+
+ p = argv[idx++];
+
+ for (mode = 0; *p; p++) {
+ switch(*p) {
+ case 'r': mode |= MRP_AUTH_MODE_READ; break;
+ case 'w': mode |= MRP_AUTH_MODE_WRITE; break;
+ case 'x': mode |= MRP_AUTH_MODE_EXEC; break;
+ case '-': break;
+ default:
+ printf("Invalid character '%c' in mode.\n", *p);
+ goto error;
+ }
+ }
+
+ if (mode == 0)
+ mode = MRP_AUTH_MODE_READ;
+
+ id = argv[idx++];
+
+ if (idx >= argc - 1)
+ token = NULL;
+ else {
+ if (idx == argc - 1)
+ token = argv[idx];
+ else
+ goto error;
+ }
+
+ status = mrp_authenticate(ctx, backend, target, mode, id, token);
+
+ printf("authentication status: %d\n", status);
+}
+
+
+void pong_cb(int error, int retval, int narg, mrp_domctl_arg_t *args,
+ void *user_data)
+{
+ mrp_console_t *c = (mrp_console_t *)user_data;
+ int i;
+
+ MRP_UNUSED(c);
+
+ if (error) {
+ printf("ping failed with error code %d\n", error);
+ }
+
+ printf("pong (return value %d)\n", retval);
+
+ for (i = 0; i < narg; i++) {
+ switch (args[i].type) {
+ case MRP_DOMCTL_STRING:
+ printf(" #%d: %s\n", i, args[i].str);
+ break;
+ case MRP_DOMCTL_UINT32:
+ printf(" #%d: %u\n", i, args[i].u32);
+ break;
+ default:
+ if (MRP_DOMCTL_IS_ARRAY(args[i].type)) {
+ uint32_t j;
+
+ printf(" #%d: array of %u items:\n", i, args[i].size);
+ for (j = 0; j < args[i].size; j++) {
+ switch (MRP_DOMCTL_ARRAY_TYPE(args[i].type)) {
+ case MRP_DOMCTL_STRING:
+ printf(" #%d: '%s'\n", j,
+ ((char **)args[i].arr)[j]);
+ break;
+ case MRP_DOMCTL_UINT32:
+ printf(" #%d: %u\n", j,
+ ((uint32_t *)args[i].arr)[j]);
+ break;
+ default:
+ printf(" #%d: <type 0x%x\n", j,
+ MRP_DOMCTL_ARRAY_TYPE(args[i].type));
+ break;
+ }
+ }
+ }
+ else
+ printf(" <type 0x%x>\n", args[i].type);
+ }
+ }
+}
+
+
+void ping_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ static uint32_t cnt = 1;
+ const char *domain;
+ char *strings[] = { "foo", "bar", "foobar", "barfoo" };
+ uint32_t uints[] = { 69, 96, 696, 969 };
+ mrp_domctl_arg_t args[32];
+ int narg, i;
+
+ MRP_UNUSED(user_data);
+
+ if (argc < 3) {
+ printf("Usage: %s domain\n", argv[0]);
+ return;
+ }
+
+ domain = argv[2];
+ narg = MRP_ARRAY_SIZE(args);
+
+ args[0].type = MRP_DOMCTL_UINT32;
+ args[0].u32 = cnt++;
+ args[1].type = MRP_DOMCTL_ARRAY(STRING);
+ args[1].arr = strings;
+ args[1].size = MRP_ARRAY_SIZE(strings);
+ args[2].type = MRP_DOMCTL_ARRAY(UINT32);
+ args[2].arr = uints;
+ args[2].size = MRP_ARRAY_SIZE(uints);
+
+ for (i = 3; i < narg; i++) {
+ if (i + 2 < argc) {
+ args[i].type = MRP_DOMCTL_STRING;
+ args[i].str = argv[i + 2];
+ }
+ else {
+ args[i].type = MRP_DOMCTL_UINT32;
+ args[i].u32 = i;
+ }
+ }
+
+ if (!mrp_invoke_domain(c->ctx, domain, "ping", narg, args, pong_cb, c))
+ printf("Failed to ping domain '%s'.\n", domain);
+}
+
+
+void invoke_reply(int error, int retval, int narg, mrp_domctl_arg_t *args,
+ void *user_data)
+{
+ mrp_console_t *c = (mrp_console_t *)user_data;
+ int i;
+
+ if (error) {
+ mrp_console_printf(c, "invoked method failed with error code %d\n",
+ error);
+ return;
+ }
+
+ mrp_console_printf(c, "invoked method returned (return value %d)\n", retval);
+
+ for (i = 0; i < narg; i++) {
+ switch (args[i].type) {
+ case MRP_DOMCTL_STRING:
+ mrp_console_printf(c, " #%d: %s\n", i, args[i].str);
+ break;
+ case MRP_DOMCTL_UINT16:
+ mrp_console_printf(c, " #%d: %u\n", i, args[i].u16);
+ break;
+ case MRP_DOMCTL_INT16:
+ mrp_console_printf(c, " #%d: %u\n", i, args[i].s16);
+ break;
+ case MRP_DOMCTL_UINT32:
+ mrp_console_printf(c, " #%d: %u\n", i, args[i].u32);
+ break;
+ case MRP_DOMCTL_INT32:
+ mrp_console_printf(c, " #%d: %u\n", i, args[i].s32);
+ break;
+ default:
+ mrp_console_printf(c, " #%d: <type 0x%x\n", i, args[i].type);
+ break;
+ }
+ }
+}
+
+
+void invoke_cb(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ const char *domain, *method, *type, *value;
+ mrp_domctl_arg_t args[32];
+ int tlen, narg, i;
+
+ MRP_UNUSED(user_data);
+
+ if (argc < 4) {
+ printf("Usage: %s %s <domain> <method> [args]\n", argv[0], argv[1]);
+ return;
+ }
+
+ domain = argv[2];
+ method = argv[3];
+ narg = MRP_ARRAY_SIZE(args);
+
+ for (i = 4, narg = 0;
+ i < argc && narg < (int)MRP_ARRAY_SIZE(args);
+ i++, narg++) {
+ type = argv[i];
+ value = strchr(type, ':');
+
+ if (value == NULL) {
+ value = type;
+ type = "string";
+ tlen = 6;
+ }
+ else {
+ tlen = value - type;
+ value++;
+ }
+
+ if (!strncmp(type, "string", tlen)) {
+ args[narg].type = MRP_DOMCTL_STRING;
+ args[narg].str = value;
+ }
+ else if (!strncmp(type, "u16" , tlen) ||
+ !strncmp(type, "uint16_t", tlen)) {
+ args[narg].type = MRP_DOMCTL_UINT16;
+ args[narg].u16 = (uint16_t)strtoul(value, NULL, 0);
+ }
+ else if (!strncmp(type, "u16" , tlen) ||
+ !strncmp(type, "uint16_t", tlen)) {
+ args[narg].type = MRP_DOMCTL_INT16;
+ args[narg].s16 = (int16_t)strtol(value, NULL, 0);
+ }
+ else if (!strncmp(type, "u32" , tlen) ||
+ !strncmp(type, "uint32_t", tlen)) {
+ args[narg].type = MRP_DOMCTL_UINT32;
+ args[narg].u32 = (uint32_t)strtoul(value, NULL, 0);
+ }
+ else if (!strncmp(type, "u32" , tlen) ||
+ !strncmp(type, "uint32_t", tlen)) {
+ args[narg].type = MRP_DOMCTL_INT32;
+ args[narg].s32 = (int32_t)strtol(value, NULL, 0);
+ }
+ else {
+ printf("invalid typecast in %s\n", argv[i]);
+ return;
+ }
+ }
+
+ printf("Invoking domain method '%s.%s' with %d args...\n", domain, method,
+ narg);
+
+ if (!mrp_invoke_domain(c->ctx, domain, method, narg, args, invoke_reply, c))
+ printf("Failed to invoke '%s.%s'.\n", domain, method);
+}
+
+
+MRP_EXPORTABLE(char *, method1, (int arg1, char *arg2, double arg3))
+{
+ MRP_UNUSED(arg1);
+ MRP_UNUSED(arg2);
+ MRP_UNUSED(arg3);
+
+ mrp_log_info("%s()...", __FUNCTION__);
+
+ return "method1 was here...";
+}
+
+static int boilerplate1(mrp_plugin_t *plugin,
+ const char *name, mrp_script_env_t *env)
+{
+ MRP_UNUSED(plugin);
+ MRP_UNUSED(name);
+ MRP_UNUSED(env);
+
+ method1(1, "foo", 9.81);
+
+ return TRUE;
+}
+
+MRP_EXPORTABLE(int, method2, (char *arg1, double arg2, int arg3))
+{
+ MRP_UNUSED(arg1);
+ MRP_UNUSED(arg2);
+ MRP_UNUSED(arg3);
+
+ mrp_log_info("%s()...", __FUNCTION__);
+
+ return 313;
+}
+
+static int boilerplate2(mrp_plugin_t *plugin,
+ const char *name, mrp_script_env_t *env)
+{
+ MRP_UNUSED(plugin);
+ MRP_UNUSED(name);
+ MRP_UNUSED(env);
+
+ return -1;
+}
+
+
+MRP_IMPORTABLE(char *, method1ptr, (int arg1, char *arg2, double arg3));
+MRP_IMPORTABLE(int, method2ptr, (char *arg1, double arg2, int arg3));
+
+#if 0
+static int export_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t methods[] = {
+ { "method1", "char *(int arg1, char *arg2, double arg3)",
+ method1, boilerplate1, plugin },
+ { "method2", "int (char *arg1, double arg2, int arg3)",
+ method2, boilerplate2, plugin },
+ { NULL, NULL, NULL, NULL, NULL }
+ };
+ mrp_method_descr_t *m;
+
+ for (m = methods; m->name != NULL; m++)
+ if (mrp_export_method(m) < 0)
+ return FALSE;
+ else
+ mrp_log_info("Successfully exported method '%s'...", m->name);
+
+ return TRUE;
+}
+
+
+static int remove_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t methods[] = {
+ { "method1", "char *(int arg1, char *arg2, double arg3)",
+ method1, boilerplate1, plugin },
+ { "method2", "int (char *arg1, double arg2, int arg3)",
+ method2, boilerplate2, plugin },
+ { NULL, NULL, NULL, NULL, NULL }
+ };
+ mrp_method_descr_t *m;
+
+ for (m = methods; m->name != NULL; m++)
+ if (mrp_remove_method(m) < 0)
+ mrp_log_info("Failed to remove method '%s'...", m->name);
+ else
+ mrp_log_info("Failed to remove method '%s'...", m->name);
+
+ return TRUE;
+}
+
+
+static int import_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t methods[] = {
+ { "method1", "char *(int arg1, char *arg2, double arg3)",
+ method1, boilerplate1, plugin },
+ { "method2", "int (char *arg1, double arg2, int arg3)",
+ method2, boilerplate2, plugin },
+ { NULL, NULL, NULL, NULL, NULL }
+ };
+
+ void *native_check[] = { method1, method2 };
+ void *script_check[] = { boilerplate1, boilerplate2 };
+ int i;
+
+ mrp_method_descr_t *m;
+
+ const char *name, *sig;
+ char buf[512];
+ void *native;
+ int (*script)(mrp_plugin_t *, const char *, mrp_script_env_t *);
+
+ for (i = 0, m = methods; m->name != NULL; i++, m++) {
+ name = m->name;
+ sig = m->signature;
+
+ if (mrp_import_method(name, sig, &native, &script) < 0)
+ return FALSE;
+
+ if (native != native_check[i] || script != script_check[i])
+ return FALSE;
+
+ mrp_log_info("%s imported as %p, %p...", name, native, script);
+
+ snprintf(buf, sizeof(buf), "%s.%s", plugin->instance, m->name);
+ name = buf;
+
+ if (mrp_import_method(name, sig, &native, &script) < 0)
+ return FALSE;
+
+ if (native != native_check[i] || script != script_check[i])
+ return FALSE;
+
+ mrp_log_info("%s imported as %p, %p...", name, native, script);
+ }
+
+ return TRUE;
+}
+
+
+static int release_methods(mrp_plugin_t *plugin)
+{
+ mrp_method_descr_t methods[] = {
+ { "method1", "char *(int arg1, char *arg2, double arg3)",
+ method1, boilerplate1, plugin },
+ { "method2", "int (char *arg1, double arg2, int arg3)",
+ method2, boilerplate2, plugin },
+ { NULL, NULL, NULL, NULL, NULL }
+ };
+ const char *name, *sig;
+ char buf[512];
+ mrp_method_descr_t *m;
+ void *native, *natives[] = { method1 , method2 };
+ int (*script)(mrp_plugin_t *, const char *, mrp_script_env_t *);
+ void *scripts[] = { boilerplate1, boilerplate2 };
+ int i;
+
+ for (i = 0, m = methods; m->name != NULL; i++, m++) {
+ name = m->name;
+ sig = m->signature;
+ native = natives[i];
+ script = scripts[i];
+
+ if (mrp_release_method(name, sig, &native, &script) < 0)
+ mrp_log_error("Failed to release method '%s'...", name);
+ else
+ mrp_log_info("Successfully released method '%s'...", name);
+
+ snprintf(buf, sizeof(buf), "%s.%s", plugin->instance, m->name);
+ name = buf;
+ native = natives[i];
+ script = scripts[i];
+
+ if (mrp_release_method(name, sig, &native, &script) < 0)
+ mrp_log_error("Failed to release method '%s'...", name);
+ else
+ mrp_log_info("Successfully released method '%s'...", name);
+ }
+
+ return TRUE;
+}
+
+#endif
+
+int test_imports(void)
+{
+ if (method1ptr == NULL || method2ptr == NULL) {
+ mrp_log_error("Failed to import methods...");
+ return FALSE;
+ }
+
+ mrp_log_info("method1ptr returned '%s'...", method1ptr(1, "foo", 3.141));
+ mrp_log_info("method2ptr returned '%d'...", method2ptr("bar", 9.81, 2));
+
+ return TRUE;
+}
+
+
+static void event_cb(mrp_event_watch_t *w, uint32_t id, int format,
+ void *event_data, void *user_data)
+{
+ mrp_plugin_t *plugin = (mrp_plugin_t *)user_data;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(format);
+
+ mrp_log_info("%s: got event 0x%x (%s):", plugin->instance, id,
+ mrp_event_name(id));
+ mrp_msg_dump(event_data, stdout);
+}
+
+
+static int subscribe_events(mrp_plugin_t *plugin)
+{
+ mrp_mainloop_t *ml = plugin->ctx->ml;
+ mrp_event_bus_t *bus = mrp_event_bus_get(ml, MRP_PLUGIN_BUS);
+ test_data_t *data = (test_data_t *)plugin->data;
+ mrp_event_mask_t events = MRP_MASK_EMPTY;
+
+
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_LOADED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_STARTED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_FAILED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_STOPPING));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_STOPPED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_UNLOADED));
+
+ data->w = mrp_event_add_watch_mask(bus, &events, event_cb, plugin);
+
+ return (data->w != NULL);
+}
+
+
+static void unsubscribe_events(mrp_plugin_t *plugin)
+{
+ test_data_t *data = (test_data_t *)plugin->data;
+
+ mrp_event_del_watch(data->w);
+ data->w = NULL;
+}
+
+
+static int test_init(mrp_plugin_t *plugin)
+{
+ mrp_plugin_arg_t *args, *arg;
+ mrp_json_t *json;
+ test_data_t *data;
+
+ mrp_log_info("%s() called for test instance '%s'...", __FUNCTION__,
+ plugin->instance);
+
+ args = plugin->args;
+ json = args[ARG_OBJECT].obj.json;
+ printf(" string1: %s\n", args[ARG_STRING1].str);
+ printf(" string2: %s\n", args[ARG_STRING2].str);
+ printf("boolean1: %s\n", args[ARG_BOOLEAN1].bln ? "TRUE" : "FALSE");
+ printf("boolean2: %s\n", args[ARG_BOOLEAN2].bln ? "TRUE" : "FALSE");
+ printf(" uint32: %u\n", args[ARG_UINT321].u32);
+ printf(" int32: %d\n", args[ARG_INT321].i32);
+ printf(" double: %f\n", args[ARG_DOUBLE1].dbl);
+ printf("init fail: %s\n", args[ARG_FAILINIT].bln ? "TRUE" : "FALSE");
+ printf("exit fail: %s\n", args[ARG_FAILEXIT].bln ? "TRUE" : "FALSE");
+ printf(" object: %s\n", mrp_json_object_to_string(json));
+
+ mrp_plugin_foreach_undecl_arg(&args[ARG_REST], arg) {
+ mrp_log_info("got argument %s of type 0x%x", arg->key, arg->type);
+ }
+
+ {
+ char *rkeys[] = { "foo", "bar", "foobar", "barfoo", NULL };
+ int i;
+
+ for (i = 0; rkeys[i] != NULL; i++) {
+ arg = mrp_plugin_find_undecl_arg(&args[ARG_REST], rkeys[i], 0);
+
+ if (arg != NULL)
+ mrp_log_info("found undeclared arg '%s' (type 0x%x)", arg->key,
+ arg->type);
+ else
+ mrp_log_info("undeclared arg '%s' not found", rkeys[i]);
+ }
+ }
+
+
+#if 0
+ if (!export_methods(plugin))
+ return FALSE;
+
+ if (!import_methods(plugin))
+ return FALSE;
+#endif
+
+ data = mrp_allocz(sizeof(*data));
+
+ if (data == NULL) {
+ mrp_log_error("Failed to allocate private data for test plugin "
+ "instance %s.", plugin->instance);
+ return FALSE;
+ }
+ else
+ plugin->data = data;
+
+ test_imports();
+
+ subscribe_events(plugin);
+
+ return !args[ARG_FAILINIT].bln;
+}
+
+
+static void test_exit(mrp_plugin_t *plugin)
+{
+ mrp_log_info("%s() called for test instance '%s'...", __FUNCTION__,
+ plugin->instance);
+
+ unsubscribe_events(plugin);
+
+#if 0
+ release_methods(plugin);
+ remove_methods(plugin);
+#endif
+
+ /*return !args[ARG_FAILINIT].bln;*/
+}
+
+
+#define TEST_DESCRIPTION "A primitive plugin just to test the plugin infra."
+#define TEST_HELP "Just a load/unload test."
+#define TEST_VERSION MRP_VERSION_INT(0, 0, 1)
+#define TEST_AUTHORS "D. Duck <donald.duck@ducksburg.org>"
+
+#define DEFAULT_OBJECT "{\n" \
+ " 'foo': 'this is json.foo',\n" \
+ " 'bar': 'this is json.bar',\n" \
+ " 'one': 1,\n" \
+ " 'two': 2,\n" \
+ " 'pi': 3.141,\n" \
+ " 'array': [ 1, 2, 'three', 'four', 5 ]\n" \
+ "}\n"
+
+static mrp_plugin_arg_t args[] = {
+ MRP_PLUGIN_ARGIDX(ARG_STRING1 , STRING, "string1" , "default string1"),
+ MRP_PLUGIN_ARGIDX(ARG_STRING2 , STRING, "string2" , "default string2"),
+ MRP_PLUGIN_ARGIDX(ARG_BOOLEAN1, BOOL , "boolean1", TRUE ),
+ MRP_PLUGIN_ARGIDX(ARG_BOOLEAN2, BOOL , "boolean2", FALSE ),
+ MRP_PLUGIN_ARGIDX(ARG_UINT321 , UINT32, "uint32" , 3141 ),
+ MRP_PLUGIN_ARGIDX(ARG_INT321 , INT32 , "int32" , -3141 ),
+ MRP_PLUGIN_ARGIDX(ARG_DOUBLE1 , DOUBLE, "double" , -3.141 ),
+ MRP_PLUGIN_ARGIDX(ARG_FAILINIT, BOOL , "failinit", FALSE ),
+ MRP_PLUGIN_ARGIDX(ARG_FAILEXIT, BOOL , "failexit", FALSE ),
+ MRP_PLUGIN_ARGIDX(ARG_OBJECT , OBJECT, "object" , DEFAULT_OBJECT ),
+ MRP_PLUGIN_ARGIDX(ARG_REST , UNDECL, NULL , NULL ),
+};
+
+static mrp_method_descr_t exports[] = {
+ MRP_GENERIC_METHOD("method1", method1, boilerplate1),
+ MRP_GENERIC_METHOD("method2", method2, boilerplate2),
+};
+
+static mrp_method_descr_t imports[] = {
+ MRP_IMPORT_METHOD("method1", method1ptr),
+ MRP_IMPORT_METHOD("method2", method2ptr),
+};
+
+
+MURPHY_REGISTER_PLUGIN("test",
+ TEST_VERSION, TEST_DESCRIPTION, TEST_AUTHORS, TEST_HELP,
+ MRP_MULTIPLE, test_init, test_exit,
+ args, MRP_ARRAY_SIZE(args),
+ exports, MRP_ARRAY_SIZE(exports),
+ imports, MRP_ARRAY_SIZE(imports),
+ &test_group);
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<busconfig>
+ <policy user="root">
+ <allow own="org.Murphy"/>
+ <allow receive_sender="org.Murphy"/>
+ <allow send_destination="org.Murphy"/>
+ </policy>
+ <policy context="default">
+ <allow receive_sender="org.Murphy"/>
+ <allow send_destination="org.Murphy"/>
+ </policy>
+</busconfig>
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C ../.. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C ../.. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <murphy/plugins/resource-native/libmurphy-resource/resource-api.h>
+
+typedef struct my_app_data {
+ mrp_res_context_t *cx;
+ mrp_res_resource_set_t *rs;
+} my_app_data;
+
+
+static bool accept_input;
+
+/* state callback for murphy connection */
+static void state_callback(mrp_res_context_t *cx,
+ mrp_res_error_t,
+ void *ud);
+
+/* callback for resource set update */
+static void resource_callback(mrp_res_context_t *cx,
+ const mrp_res_resource_set_t *rs,
+ void *userdata);
+
+void create_resources(my_app_data *app_data)
+{
+ mrp_res_resource_t *resource = NULL;
+ mrp_res_attribute_t *attr;
+
+ /* if we already have a decent set, just re-acquire it */
+ if (app_data->rs) {
+ mrp_res_acquire_resource_set(app_data->rs);
+ return;
+ }
+
+ /* otherwise create resource set and resources */
+ app_data->rs = mrp_res_create_resource_set(app_data->cx,
+ "player",
+ resource_callback,
+ (void*)app_data);
+
+ if (app_data->rs == NULL) {
+ printf("Couldn't create resource set\n");
+ return;
+ }
+
+ if (!mrp_res_set_autorelease(TRUE, app_data->rs)) {
+ printf("Could not set autorelease flag!\n");
+ return;
+ }
+
+ resource = mrp_res_create_resource(app_data->rs,
+ "audio_playback",
+ true,
+ false);
+
+ if (resource == NULL) {
+ printf("Couldn't create audio resource\n");
+ mrp_res_delete_resource_set(app_data->rs);
+ return;
+ }
+
+ /* set a resource attribute */
+
+ attr = mrp_res_get_attribute_by_name(resource, "role");
+
+ if (attr) {
+ mrp_res_set_attribute_string(attr, "call");
+ }
+
+ resource = mrp_res_create_resource(app_data->rs,
+ "video_playback",
+ true,
+ false);
+
+ if (resource == NULL) {
+ printf("Couldn't create video resource\n");
+ mrp_res_delete_resource_set(app_data->rs);
+ return;
+ }
+
+ printf("created the resource set!\n");
+}
+
+void acquire_resources(my_app_data *app_data)
+{
+ /* acquire the resources */
+ if (app_data->rs)
+ mrp_res_acquire_resource_set(app_data->rs);
+ else
+ printf("No release set created!\n");
+}
+
+void giveup_resources(my_app_data *app_data)
+{
+ /* release resources */
+ if (app_data->rs)
+ mrp_res_release_resource_set(app_data->rs);
+ else
+ printf("No release set acquired!\n");
+}
+
+static void state_callback(mrp_res_context_t *context,
+ mrp_res_error_t err,
+ void *userdata)
+{
+ int i = 0, j = 0;
+ const mrp_res_string_array_t *app_classes = NULL;
+ const mrp_res_resource_set_t *rs;
+ mrp_res_string_array_t *attributes = NULL;
+ mrp_res_attribute_t *attr;
+ bool system_handles_audio = FALSE;
+ bool system_handles_video = FALSE;
+ mrp_res_resource_t *resource;
+
+ my_app_data *app_data = (my_app_data *) userdata;
+
+ if (err != MRP_RES_ERROR_NONE) {
+ printf("error message received from Murphy\n");
+ return;
+ }
+
+ switch (context->state) {
+
+ case MRP_RES_CONNECTED:
+
+ printf("connected to murphy\n");
+
+ if ((app_classes =
+ mrp_res_list_application_classes(context)) != NULL) {
+ printf("listing all application classes in the system\n");
+
+ for (i = 0; i < app_classes->num_strings; i++) {
+ printf("app class %d is %s\n", i, app_classes->strings[i]);
+ }
+ }
+
+ if ((rs = mrp_res_list_resources(context)) != NULL) {
+ mrp_res_string_array_t *resource_names;
+
+ printf("listing all resources available in the system\n");
+
+ resource_names = mrp_res_list_resource_names(rs);
+
+ if (!resource_names) {
+ printf("No resources available in the system!\n");
+ return;
+ }
+
+ for (i = 0; i < resource_names->num_strings; i++) {
+
+ resource = mrp_res_get_resource_by_name(rs,
+ resource_names->strings[i]);
+
+ if (!resource)
+ continue;
+
+ printf("resource %d is %s\n", i, resource->name);
+ if (strcmp(resource->name, "audio_playback") == 0)
+ system_handles_audio = TRUE;
+ if (strcmp(resource->name, "video_playback") == 0)
+ system_handles_video = TRUE;
+
+ attributes = mrp_res_list_attribute_names(resource);
+
+ if (!attributes)
+ continue;
+
+ for (j = 0; j < attributes->num_strings; j++) {
+ attr = mrp_res_get_attribute_by_name(resource,
+ attributes->strings[j]);
+
+ if (!attr)
+ continue;
+
+ printf("attr %s has ", attr->name);
+ switch(attr->type) {
+ case mrp_string:
+ printf("type string and value %s\n",
+ attr->string);
+ break;
+ case mrp_int32:
+ printf("type int32 and value %d\n",
+ (int) attr->integer);
+ break;
+ case mrp_uint32:
+ printf("type uint32 and value %u\n",
+ attr->unsignd);
+ break;
+ case mrp_double:
+ printf("type double and value %f\n",
+ attr->floating);
+ break;
+ default:
+ printf("type unknown\n");
+ break;
+ }
+ }
+ mrp_res_free_string_array(attributes);
+ }
+ mrp_res_free_string_array(resource_names);
+ }
+
+ if (system_handles_audio && system_handles_video) {
+ printf("system provides all necessary resources\n");
+ accept_input = TRUE;
+ }
+
+ break;
+
+ case MRP_RES_DISCONNECTED:
+ printf("disconnected from murphy\n");
+ mrp_res_delete_resource_set(app_data->rs);
+ mrp_res_destroy(app_data->cx);
+ exit(1);
+ }
+}
+
+
+static char *state_to_str(mrp_res_resource_state_t st)
+{
+ char *state = "unknown";
+ switch (st) {
+ case MRP_RES_RESOURCE_ACQUIRED:
+ state = "acquired";
+ break;
+ case MRP_RES_RESOURCE_LOST:
+ state = "lost";
+ break;
+ case MRP_RES_RESOURCE_AVAILABLE:
+ state = "available";
+ break;
+ case MRP_RES_RESOURCE_PENDING:
+ state = "pending";
+ break;
+ }
+ return state;
+}
+
+static void resource_callback(mrp_res_context_t *cx,
+ const mrp_res_resource_set_t *rs,
+ void *userdata)
+{
+ my_app_data *my_data = (my_app_data *) userdata;
+ mrp_res_resource_t *res;
+ mrp_res_attribute_t *attr;
+
+ MRP_UNUSED(cx);
+
+ printf("> resource_callback\n");
+
+ if (!mrp_res_equal_resource_set(rs, my_data->rs))
+ return;
+
+ /* here compare the resource set difference */
+
+ res = mrp_res_get_resource_by_name(rs, "audio_playback");
+
+ if (!res) {
+ printf("audio_playback not present in resource set\n");
+ return;
+ }
+
+ printf("resource set state: %s\n", state_to_str(rs->state));
+
+ printf("resource 0 name '%s' -> '%s'\n", res->name, state_to_str(res->state));
+
+ res = mrp_res_get_resource_by_name(rs, "video_playback");
+
+ if (!res) {
+ printf("video_playback not present in resource set\n");
+ return;
+ }
+
+ printf("resource 1 name '%s' -> '%s'\n", res->name, state_to_str(res->state));
+
+ /* let's copy the changed set for ourselves */
+
+ /* Delete must not mean releasing the set! Otherwise this won't work.
+ * It's up to the user to make sure that there's a working reference
+ * to the resource set.
+ */
+ mrp_res_delete_resource_set(my_data->rs);
+
+ /* copying must also have no semantic meaning */
+ my_data->rs = mrp_res_copy_resource_set(rs);
+
+ /* print the current role attribute */
+
+ res = mrp_res_get_resource_by_name(rs, "audio_playback");
+ attr = mrp_res_get_attribute_by_name(res, "role");
+
+ if (res && attr)
+ printf("attribute '%s' has role '%s'\n", res->name, attr->string);
+
+ /* acquiring a copy of an existing release set means:
+ * - acquired state: update, since otherwise no meaning
+ * - pending state: acquire, since previous state not known/meaningless
+ * - lost state: update, since otherwise will fail
+ * - available: update or acquire
+ */
+}
+
+static void handle_input(mrp_io_watch_t *watch, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_io_watch_mainloop(watch);
+ char buf[1024];
+ int size;
+
+ my_app_data *app_data = (my_app_data *) user_data;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (events & MRP_IO_EVENT_IN) {
+ size = read(fd, buf, sizeof(buf) - 1);
+
+ if (size > 0) {
+ buf[size] = '\0';
+ printf("read line %s\n", buf);
+ }
+ }
+
+ if (events & MRP_IO_EVENT_HUP) {
+ mrp_del_io_watch(watch);
+ }
+
+ if (accept_input) {
+ switch (buf[0]) {
+ case 'C':
+ create_resources(app_data);
+ break;
+ case 'A':
+ acquire_resources(app_data);
+ break;
+ case 'D':
+ giveup_resources(app_data);
+ break;
+ case 'Q':
+ if (app_data->rs)
+ mrp_res_delete_resource_set(app_data->rs);
+ if (ml)
+ mrp_mainloop_quit(ml, 0);
+ break;
+ default:
+ printf("'C' to create resource set\n'A' to acquire\n'D' to release\n'Q' to quit\n");
+ break;
+ }
+ }
+ else {
+ printf("not connected to Murphy\n");
+ }
+}
+
+int main(int argc, char **argv)
+{
+ mrp_mainloop_t *ml;
+ int mask;
+ mrp_io_watch_t *watch;
+
+ my_app_data app_data;
+
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ if ((ml = mrp_mainloop_create()) == NULL)
+ exit(1);
+
+ app_data.rs = NULL;
+ app_data.cx = mrp_res_create(ml, state_callback, &app_data);
+
+ mask = MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP | MRP_IO_EVENT_ERR;
+ watch = mrp_add_io_watch(ml, fileno(stdin), (mrp_io_event_t) mask,
+ handle_input, &app_data);
+
+ if (!watch)
+ exit(1);
+
+ /* start looping */
+ mrp_mainloop_run(ml);
+
+ mrp_res_destroy(app_data.cx);
+ mrp_mainloop_destroy(ml);
+
+ app_data.cx = NULL;
+ app_data.rs = NULL;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include "resource-api.h"
+
+#include "attribute.h"
+#include "string_array.h"
+
+void mrp_attribute_array_free(mrp_res_attribute_t *arr,
+ uint32_t dim)
+{
+ uint32_t i;
+ mrp_res_attribute_t *attr;
+
+ if (arr) {
+ for (i = 0; i < dim; i++) {
+ attr = arr + i;
+
+ mrp_free((void *)attr->name);
+
+ if (attr->type == mrp_string)
+ mrp_free((void *)attr->string);
+ }
+ mrp_free(arr);
+ }
+}
+
+
+mrp_res_attribute_t *mrp_attribute_array_dup(uint32_t dim,
+ mrp_res_attribute_t *arr)
+{
+ size_t size;
+ uint32_t i;
+ mrp_res_attribute_t *sattr, *dattr;
+ mrp_res_attribute_t *dup;
+ int err;
+
+ size = (sizeof(mrp_res_attribute_t) * (dim + 1));
+
+ if (!(dup = mrp_allocz(size))) {
+ err = ENOMEM;
+ goto failed;
+ }
+
+ for (i = 0; i < dim; i++) {
+ sattr = arr + i;
+ dattr = dup + i;
+
+ if (!(dattr->name = mrp_strdup(sattr->name))) {
+ err = ENOMEM;
+ goto failed;
+ }
+
+ switch ((dattr->type = sattr->type)) {
+ case mrp_string:
+ if (!(dattr->string = mrp_strdup(sattr->string))) {
+ err = ENOMEM;
+ goto failed;
+ }
+ break;
+ case mrp_int32:
+ dattr->integer = sattr->integer;
+ break;
+ case mrp_uint32:
+ dattr->type = mrp_uint32;
+ dattr->unsignd = sattr->unsignd;
+ break;
+ case mrp_double:
+ dattr->type = mrp_double;
+ dattr->floating = sattr->floating;
+ break;
+ default:
+ err = EINVAL;
+ goto failed;
+ }
+ }
+
+ return dup;
+
+ failed:
+ mrp_attribute_array_free(dup, dim);
+ errno = err;
+ return NULL;
+}
+
+/* public API */
+
+mrp_res_string_array_t * mrp_res_list_attribute_names(
+ const mrp_res_resource_t *res)
+{
+ int i;
+ mrp_res_string_array_t *ret;
+ mrp_res_context_t *cx = NULL;
+
+ if (!res)
+ return NULL;
+
+ cx = res->priv->set->priv->cx;
+
+ if (!cx)
+ return NULL;
+
+ ret = mrp_allocz(sizeof(mrp_res_string_array_t));
+
+ if (!ret)
+ return NULL;
+
+ ret->num_strings = res->priv->num_attributes;
+ ret->strings = mrp_allocz_array(const char *, res->priv->num_attributes);
+
+ if (!ret->strings) {
+ mrp_free(ret);
+ return NULL;
+ }
+
+ for (i = 0; i < res->priv->num_attributes; i++) {
+ ret->strings[i] = mrp_strdup(res->priv->attrs[i].name);
+ if (!ret->strings[i]) {
+ ret->num_strings = i;
+ mrp_res_free_string_array(ret);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+
+mrp_res_attribute_t * mrp_res_get_attribute_by_name(
+ mrp_res_resource_t *res, const char *name)
+{
+ int i;
+
+ if (!res)
+ return NULL;
+
+ for (i = 0; i < res->priv->num_attributes; i++) {
+ if (strcmp(name, res->priv->attrs[i].name) == 0) {
+ return &res->priv->attrs[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+int mrp_res_set_attribute_string(mrp_res_attribute_t *attr,
+ const char *value)
+{
+ char *str;
+
+ if (!attr)
+ return -1;
+
+ /* check the attribute type */
+
+ if (attr->type != mrp_string)
+ return -1;
+
+ str = mrp_strdup(value);
+
+ if (!str)
+ return -1;
+
+ mrp_free((void *) attr->string);
+ attr->string = str;
+
+ return 0;
+}
+
+
+int mrp_res_set_attribute_uint(mrp_res_attribute_t *attr,
+ uint32_t value)
+{
+ if (!attr || attr->type != mrp_uint32)
+ return -1;
+
+ attr->unsignd = value;
+
+ return 0;
+}
+
+
+int mrp_res_set_attribute_int(mrp_res_attribute_t *attr,
+ int32_t value)
+{
+ if (!attr || attr->type != mrp_int32)
+ return -1;
+
+ attr->integer = value;
+
+ return 0;
+}
+
+
+int mrp_res_set_attribute_double(mrp_res_attribute_t *attr,
+ double value)
+{
+ if (!attr || attr->type != mrp_double)
+ return -1;
+
+ attr->floating = value;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_API_ATTRIBUTE_H__
+#define __MURPHY_RESOURCE_API_ATTRIBUTE_H__
+
+#include <errno.h>
+
+#include "resource-private.h"
+
+#define ATTRIBUTE_MAX 32
+
+void mrp_attribute_array_free(mrp_res_attribute_t *arr,
+ uint32_t dim);
+
+mrp_res_attribute_t *mrp_attribute_array_dup(uint32_t dim,
+ mrp_res_attribute_t *arr);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2014, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <murphy/plugins/resource-native/libmurphy-resource/resource-api.h>
+
+mrp_mainloop_t *ml;
+
+void state_cb(mrp_res_context_t *cx, mrp_res_error_t err, void *userdata)
+{
+ MRP_UNUSED(cx);
+ MRP_UNUSED(err);
+ MRP_UNUSED(userdata);
+}
+
+void deferred_cb(mrp_deferred_t *d, void *user_data)
+{
+ uint *iterations = user_data;
+
+ (*iterations)--;
+
+ if (*iterations == 0) {
+ mrp_del_deferred(d);
+ mrp_mainloop_quit(ml, 0);
+ return;
+ }
+
+ mrp_res_context_t *ctx = mrp_res_create(ml, state_cb, NULL);
+ mrp_res_destroy(ctx);
+}
+
+void usage()
+{
+ printf("context-create <iterations>\n");
+}
+
+int main(int argc, char **argv)
+{
+ uint iterations = 0;
+
+ if (argc != 2) {
+ usage();
+ exit(1);
+ }
+
+ if ((ml = mrp_mainloop_create()) == NULL)
+ exit(1);
+
+ iterations = strtoul(argv[1], NULL, 10);
+
+ mrp_add_deferred(ml, deferred_cb, &iterations);
+
+ /* start looping */
+ mrp_mainloop_run(ml);
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include "message.h"
+
+#include "rset.h"
+#include "attribute.h"
+
+bool fetch_resource_set_state(mrp_msg_t *msg, void **pcursor,
+ mrp_resproto_state_t *pstate)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
+ {
+ *pstate = 0;
+ return false;
+ }
+
+ *pstate = value.u16;
+ return true;
+}
+
+
+bool fetch_resource_set_mask(mrp_msg_t *msg, void **pcursor,
+ int mask_type, uint32_t *pmask)
+{
+ uint16_t expected_tag;
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ switch (mask_type) {
+ case 0: expected_tag = RESPROTO_RESOURCE_GRANT; break;
+ case 1: expected_tag = RESPROTO_RESOURCE_ADVICE; break;
+ default: /* don't know what to fetch */ return false;
+ }
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != expected_tag || type != MRP_MSG_FIELD_UINT32)
+ {
+ *pmask = 0;
+ return false;
+ }
+
+ *pmask = value.u32;
+ return true;
+}
+
+
+bool fetch_resource_set_id(mrp_msg_t *msg, void **pcursor,uint32_t *pid)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
+ {
+ *pid = 0;
+ return false;
+ }
+
+ *pid = value.u32;
+ return true;
+}
+
+
+bool fetch_mrp_str_array(mrp_msg_t *msg, void **pcursor,
+ uint16_t expected_tag, mrp_res_string_array_t **parr)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != expected_tag || type != MRP_MSG_FIELD_ARRAY_OF(STRING))
+ {
+ *parr = mrp_str_array_dup(0, NULL);
+ return false;
+ }
+
+ if (!(*parr = mrp_str_array_dup(size, (const char **)value.astr)))
+ return false;
+
+ return true;
+}
+
+
+bool fetch_seqno(mrp_msg_t *msg, void **pcursor, uint32_t *pseqno)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
+ {
+ *pseqno = 0;
+ return false;
+ }
+
+ *pseqno = value.u32;
+ return true;
+}
+
+
+bool fetch_request(mrp_msg_t *msg, void **pcursor, uint16_t *preqtype)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
+ {
+ *preqtype = 0;
+ return false;
+ }
+
+ *preqtype = value.u16;
+ return true;
+}
+
+
+bool fetch_status(mrp_msg_t *msg, void **pcursor, int *pstatus)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
+ {
+ *pstatus = EIO;
+ return false;
+ }
+
+ *pstatus = value.s16;
+ return true;
+}
+
+
+
+bool fetch_attribute_array(mrp_msg_t *msg, void **pcursor,
+ size_t dim, mrp_res_attribute_t *arr,
+ int *n_arr)
+{
+ mrp_res_attribute_t *attr;
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+ size_t i;
+ *n_arr = 0;
+
+ i = 0;
+
+ while (mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size)) {
+ if (tag == RESPROTO_SECTION_END && type == MRP_MSG_FIELD_UINT8)
+ break;
+
+ if (tag != RESPROTO_ATTRIBUTE_NAME ||
+ type != MRP_MSG_FIELD_STRING ||
+ i >= dim - 1) {
+ return false;
+ }
+
+ attr = arr + i++;
+ attr->name = value.str;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_ATTRIBUTE_VALUE) {
+ return false;
+ }
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ attr->type = 's';
+ attr->string = value.str;
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ attr->type = 'i';
+ attr->integer = value.s32;
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ attr->type = 'u';
+ attr->unsignd = value.u32;
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ attr->type = 'f';
+ attr->floating = value.dbl;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ memset(arr + i, 0, sizeof(mrp_res_attribute_t));
+
+ *n_arr = i;
+
+ return TRUE;
+}
+
+
+bool fetch_resource_name(mrp_msg_t *msg, void **pcursor,
+ const char **pname)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_NAME || type != MRP_MSG_FIELD_STRING)
+ {
+ *pname = "<unknown>";
+ return false;
+ }
+
+ *pname = value.str;
+ return true;
+}
+
+
+static int priv_res_to_mrp_res(uint32_t id, resource_def_t *src, mrp_res_resource_t *dst,
+ mrp_res_resource_set_t *set)
+{
+ dst->name = mrp_strdup(src->name);
+ dst->state = MRP_RES_RESOURCE_LOST;
+ dst->priv->mandatory = false;
+ dst->priv->shared = false;
+
+ dst->priv->server_id = id;
+
+ dst->priv->num_attributes = src->num_attrs;
+ dst->priv->attrs = src->attrs;
+ dst->priv->set = set;
+ return 0;
+}
+
+
+mrp_res_resource_set_t *resource_query_response(mrp_res_context_t *cx,
+ mrp_msg_t *msg, void **pcursor)
+{
+ int status;
+ uint32_t dim, i;
+ resource_def_t rdef[RESOURCE_MAX];
+ mrp_res_attribute_t attrs[ATTRIBUTE_MAX + 1];
+ resource_def_t *src;
+ mrp_res_resource_set_t *arr = NULL;
+
+ if (!cx)
+ goto failed;
+
+ if (!fetch_status(msg, pcursor, &status))
+ goto failed;
+
+ if (status != 0)
+ mrp_res_error("Resource query failed (%u): %s", status, strerror(status));
+ else {
+ dim = 0;
+
+ while (fetch_resource_name(msg, pcursor, &rdef[dim].name)) {
+ int n_attrs = 0;
+
+ if (!fetch_attribute_array(msg, pcursor, ATTRIBUTE_MAX+1,
+ attrs, &n_attrs))
+ goto failed;
+
+ if (!(rdef[dim].attrs = mrp_attribute_array_dup(n_attrs, attrs))) {
+ mrp_res_error("failed to duplicate attributes");
+ goto failed;
+ }
+
+ rdef[dim].num_attrs = n_attrs;
+
+ dim++;
+ }
+
+ arr = mrp_allocz(sizeof(mrp_res_resource_set_t));
+
+ if (!arr)
+ goto failed;
+
+ arr->priv = mrp_allocz(sizeof(mrp_res_resource_set_private_t));
+
+ if (!arr->priv)
+ goto failed;
+
+ arr->application_class = NULL;
+ arr->state = MRP_RES_RESOURCE_LOST;
+ arr->priv->cx = cx;
+ arr->priv->num_resources = dim;
+
+ arr->priv->resources = mrp_allocz_array(mrp_res_resource_t *, dim);
+
+ if (!arr->priv->resources)
+ goto failed;
+
+ for (i = 0; i < dim; i++) {
+ src = rdef + i;
+ arr->priv->resources[i] = mrp_allocz(sizeof(mrp_res_resource_t));
+ if (!arr->priv->resources[i]) {
+ arr->priv->num_resources = i;
+ goto failed;
+ }
+ arr->priv->resources[i]->priv =
+ mrp_allocz(sizeof(mrp_res_resource_private_t));
+ if (!arr->priv->resources[i]->priv) {
+ mrp_free(arr->priv->resources[i]);
+ arr->priv->num_resources = i;
+ goto failed;
+ }
+ priv_res_to_mrp_res(i, src, arr->priv->resources[i], arr);
+ }
+ }
+
+ return arr;
+
+ failed:
+ mrp_res_error("malformed reply to resource query");
+ free_resource_set(arr);
+
+ return NULL;
+}
+
+
+mrp_res_string_array_t *class_query_response(mrp_msg_t *msg, void **pcursor)
+{
+ int status;
+ mrp_res_string_array_t *arr = NULL;
+
+ if (!fetch_status(msg, pcursor, &status) || (status == 0 &&
+ !fetch_mrp_str_array(msg, pcursor, RESPROTO_CLASS_NAME, &arr)))
+ {
+ mrp_res_error("ignoring malformed response to class query");
+ return NULL;
+ }
+
+ if (status) {
+ mrp_res_error("class query failed with error code %u", status);
+ mrp_res_free_string_array(arr);
+ return NULL;
+ }
+
+ return arr;
+}
+
+
+bool create_resource_set_response(mrp_msg_t *msg,
+ mrp_res_resource_set_t *rset, void **pcursor)
+{
+ int status;
+ uint32_t rset_id;
+
+ if (!fetch_status(msg, pcursor, &status) || (status == 0 &&
+ !fetch_resource_set_id(msg, pcursor, &rset_id)))
+ {
+ mrp_res_error("ignoring malformed response to resource set creation");
+ goto error;
+ }
+
+ if (status) {
+ mrp_res_error("creation of resource set failed. error code %u",status);
+ goto error;
+ }
+
+ rset->priv->id = rset_id;
+
+ return true;
+error:
+ return false;
+}
+
+
+mrp_res_resource_set_t *acquire_resource_set_response(mrp_msg_t *msg,
+ mrp_res_context_t *cx, void **pcursor)
+{
+ int status;
+ uint32_t rset_id;
+ mrp_res_resource_set_t *rset = NULL;
+
+ if (!fetch_resource_set_id(msg, pcursor, &rset_id) ||
+ !fetch_status(msg, pcursor, &status))
+ {
+ mrp_res_error("ignoring malformed response to resource set");
+ goto error;
+ }
+
+ if (status) {
+ mrp_res_error("acquiring of resource set failed. error code %u",status);
+ goto error;
+ }
+
+ /* we need the previous resource set because the new one doesn't
+ * tell us the resource set class */
+
+ rset = mrp_htbl_lookup(cx->priv->rset_mapping, u_to_p(rset_id));
+
+ if (!rset) {
+ mrp_res_error("no rset found!");
+ goto error;
+ }
+
+ return rset;
+
+error:
+ return NULL;
+}
+
+
+int acquire_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset)
+{
+ mrp_msg_t *msg = NULL;
+
+ if (!cx->priv->connected)
+ return -1;
+
+ msg = mrp_msg_create(
+ RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, cx->priv->next_seqno,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16,
+ RESPROTO_ACQUIRE_RESOURCE_SET,
+ RESPROTO_RESOURCE_SET_ID, MRP_MSG_FIELD_UINT32, rset->priv->id,
+ RESPROTO_MESSAGE_END);
+
+ if (!msg)
+ return -1;
+
+ rset->priv->seqno = cx->priv->next_seqno;
+ cx->priv->next_seqno++;
+
+ if (!mrp_transport_send(cx->priv->transp, msg))
+ goto error;
+
+ mrp_msg_unref(msg);
+ return 0;
+
+error:
+ mrp_msg_unref(msg);
+ return -1;
+}
+
+
+int release_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset)
+{
+ mrp_msg_t *msg = NULL;
+
+ if (!cx->priv->connected)
+ return -1;
+
+ msg = mrp_msg_create(
+ RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, cx->priv->next_seqno,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16,
+ RESPROTO_RELEASE_RESOURCE_SET,
+ RESPROTO_RESOURCE_SET_ID, MRP_MSG_FIELD_UINT32, rset->priv->id,
+ RESPROTO_MESSAGE_END);
+
+ if (!msg)
+ return -1;
+
+ rset->priv->seqno = cx->priv->next_seqno;
+ cx->priv->next_seqno++;
+
+ if (!mrp_transport_send(cx->priv->transp, msg))
+ goto error;
+
+ mrp_msg_unref(msg);
+ return 0;
+
+error:
+ mrp_msg_unref(msg);
+ return -1;
+}
+
+
+int create_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset)
+{
+ mrp_msg_t *msg = NULL;
+ uint32_t i;
+ uint32_t rset_flags = 0;
+
+ if (!cx || !rset)
+ return -1;
+
+ if (!cx->priv->connected)
+ return -1;
+
+ if (rset->priv->autorelease)
+ rset_flags |= RESPROTO_RSETFLAG_AUTORELEASE;
+
+ msg = mrp_msg_create(
+ RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, cx->priv->next_seqno,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16,
+ RESPROTO_CREATE_RESOURCE_SET,
+ RESPROTO_RESOURCE_FLAGS, MRP_MSG_FIELD_UINT32, rset_flags,
+ RESPROTO_RESOURCE_PRIORITY, MRP_MSG_FIELD_UINT32, 0,
+ RESPROTO_CLASS_NAME, MRP_MSG_FIELD_STRING, rset->application_class,
+ RESPROTO_ZONE_NAME, MRP_MSG_FIELD_STRING, cx->zone,
+ RESPROTO_MESSAGE_END);
+
+ if (!msg)
+ return -1;
+
+ rset->priv->seqno = cx->priv->next_seqno;
+ cx->priv->next_seqno++;
+
+ for (i = 0; i < rset->priv->num_resources; i++) {
+ int j;
+ uint32_t res_flags = 0;
+ mrp_res_resource_t *res = rset->priv->resources[i];
+
+ if (!res)
+ goto error;
+
+ if (res->priv->shared)
+ res_flags |= RESPROTO_RESFLAG_SHARED;
+
+ if (res->priv->mandatory)
+ res_flags |= RESPROTO_RESFLAG_MANDATORY;
+
+ if (!mrp_msg_append(msg, RESPROTO_RESOURCE_NAME, MRP_MSG_FIELD_STRING,
+ res->name))
+ goto error;
+
+ if (!mrp_msg_append(msg, RESPROTO_RESOURCE_FLAGS, MRP_MSG_FIELD_UINT32,
+ res_flags))
+ goto error;
+
+ for (j = 0; j < res->priv->num_attributes; j++) {
+ mrp_res_attribute_t *elem = &res->priv->attrs[j];
+ const char *attr_name = elem->name;
+
+ if (!mrp_msg_append(msg, RESPROTO_ATTRIBUTE_NAME, MRP_MSG_FIELD_STRING,
+ attr_name))
+ goto error;
+
+ switch (elem->type) {
+ case 's':
+ if (!mrp_msg_append(msg, RESPROTO_ATTRIBUTE_VALUE,
+ MRP_MSG_FIELD_STRING, elem->string))
+ goto error;
+ break;
+ case 'i':
+ if (!mrp_msg_append(msg, RESPROTO_ATTRIBUTE_VALUE,
+ MRP_MSG_FIELD_SINT32, elem->integer))
+ goto error;
+ break;
+ case 'u':
+ if (!mrp_msg_append(msg, RESPROTO_ATTRIBUTE_VALUE,
+ MRP_MSG_FIELD_UINT32, elem->unsignd))
+ goto error;
+ break;
+ case 'f':
+ if (!mrp_msg_append(msg, RESPROTO_ATTRIBUTE_VALUE,
+ MRP_MSG_FIELD_DOUBLE, elem->floating))
+ goto error;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!mrp_msg_append(msg, RESPROTO_SECTION_END, MRP_MSG_FIELD_UINT8, 0))
+ goto error;
+ }
+
+ if (!mrp_transport_send(cx->priv->transp, msg))
+ goto error;
+
+ mrp_msg_unref(msg);
+ return 0;
+
+error:
+ mrp_msg_unref(msg);
+ return -1;
+}
+
+
+int get_application_classes_request(mrp_res_context_t *cx)
+{
+ mrp_msg_t *msg = NULL;
+
+ if (!cx->priv->connected)
+ goto error;
+
+ msg = mrp_msg_create(RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, 0,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, RESPROTO_QUERY_CLASSES,
+ RESPROTO_MESSAGE_END);
+
+ if (!msg)
+ goto error;
+
+ if (!mrp_transport_send(cx->priv->transp, msg))
+ goto error;
+
+ mrp_msg_unref(msg);
+ return 0;
+
+error:
+ mrp_msg_unref(msg);
+ return -1;
+}
+
+
+int get_available_resources_request(mrp_res_context_t *cx)
+{
+ mrp_msg_t *msg = NULL;
+
+ if (!cx->priv->connected)
+ goto error;
+
+ msg = mrp_msg_create(RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, 0,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16,
+ RESPROTO_QUERY_RESOURCES,
+ RESPROTO_MESSAGE_END);
+
+ if (!msg)
+ goto error;
+
+ if (!mrp_transport_send(cx->priv->transp, msg))
+ goto error;
+
+ mrp_msg_unref(msg);
+ return 0;
+
+error:
+ mrp_msg_unref(msg);
+ return -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_API_MESSAGE_H__
+#define __MURPHY_RESOURCE_API_MESSAGE_H__
+
+#include <murphy/resource/protocol.h>
+
+#include "resource-private.h"
+#include "string_array.h"
+
+/* parsing of the message */
+
+bool fetch_resource_set_state(mrp_msg_t *msg, void **pcursor,
+ mrp_resproto_state_t *pstate);
+
+bool fetch_resource_set_mask(mrp_msg_t *msg, void **pcursor,
+ int mask_type, uint32_t *pmask);
+
+bool fetch_resource_set_id(mrp_msg_t *msg, void **pcursor, uint32_t *pid);
+
+bool fetch_mrp_str_array(mrp_msg_t *msg, void **pcursor,
+ uint16_t expected_tag, mrp_res_string_array_t **parr);
+
+bool fetch_seqno(mrp_msg_t *msg, void **pcursor, uint32_t *pseqno);
+
+bool fetch_request(mrp_msg_t *msg, void **pcursor, uint16_t *preqtype);
+
+bool fetch_status(mrp_msg_t *msg, void **pcursor, int *pstatus);
+
+bool fetch_attribute_array(mrp_msg_t *msg, void **pcursor,
+ size_t dim, mrp_res_attribute_t *arr,
+ int *n_arr);
+
+bool fetch_resource_name(mrp_msg_t *msg, void **pcursor,
+ const char **pname);
+
+/* handling of the message responses */
+
+mrp_res_resource_set_t *resource_query_response(mrp_res_context_t *cx,
+ mrp_msg_t *msg, void **pcursor);
+
+mrp_res_string_array_t *class_query_response(mrp_msg_t *msg, void **pcursor);
+
+bool create_resource_set_response(mrp_msg_t *msg,
+ mrp_res_resource_set_t *rset, void **pcursor);
+
+mrp_res_resource_set_t *acquire_resource_set_response(mrp_msg_t *msg,
+ mrp_res_context_t *cx, void **pcursor);
+
+/* requests to the server */
+
+int acquire_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset);
+
+int release_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset);
+
+int create_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset);
+
+int get_application_classes_request(mrp_res_context_t *cx);
+
+int get_available_resources_request(mrp_res_context_t *cx);
+
+#endif
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-resource
+Description: Murphy policy framework, resource library.
+Requires: murphy-common = @PACKAGE_VERSION@
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-common -lmurphy-resource
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_API_H__
+#define __MURPHY_RESOURCE_API_H__
+
+#include <stdarg.h>
+
+/*
+ * Enable the json-c/JSON-Glib symbol clash hackaround in transport.h.
+ * This is currently the only known place which triggers the symbol
+ * clash. It happens when compiling ico-uxf-homescreen which includes
+ * this indirectly and also uses JSON-Glib internally for manipulating
+ * JSON objects...
+ */
+
+#define __JSON_GLIB_DANGER__
+
+#include <murphy/common.h>
+
+MRP_CDECL_BEGIN
+
+typedef struct mrp_res_context_private_s mrp_res_context_private_t;
+typedef struct mrp_res_resource_private_s mrp_res_resource_private_t;
+typedef struct mrp_res_resource_set_private_s mrp_res_resource_set_private_t;
+
+typedef enum {
+ MRP_RES_CONNECTED,
+ MRP_RES_DISCONNECTED,
+} mrp_res_connection_state_t;
+
+typedef enum {
+ MRP_RES_RESOURCE_LOST,
+ MRP_RES_RESOURCE_PENDING,
+ MRP_RES_RESOURCE_ACQUIRED,
+ MRP_RES_RESOURCE_AVAILABLE,
+} mrp_res_resource_state_t;
+
+typedef enum {
+ MRP_RES_ERROR_NONE,
+ MRP_RES_ERROR_CONNECTION_LOST,
+ MRP_RES_ERROR_INTERNAL,
+ MRP_RES_ERROR_MALFORMED,
+} mrp_res_error_t;
+
+typedef struct {
+ mrp_res_connection_state_t state;
+ const char *zone;
+ mrp_res_context_private_t *priv;
+} mrp_res_context_t;
+
+typedef enum {
+ mrp_int32 = 'i',
+ mrp_uint32 = 'u',
+ mrp_double = 'f',
+ mrp_string = 's',
+ mrp_invalid = '\0'
+} mrp_res_attribute_type_t;
+
+typedef struct {
+ const char *name;
+ mrp_res_attribute_type_t type;
+ union {
+ const char *string;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+ };
+} mrp_res_attribute_t;
+
+typedef struct {
+ const char *name;
+ mrp_res_resource_state_t state;
+ mrp_res_resource_private_t *priv;
+} mrp_res_resource_t;
+
+typedef struct {
+ const char *application_class;
+ mrp_res_resource_state_t state;
+ mrp_res_resource_set_private_t *priv;
+} mrp_res_resource_set_t;
+
+typedef struct {
+ int num_strings;
+ const char **strings;
+} mrp_res_string_array_t;
+
+/**
+ * Prototype for murphy state callback. You have to be in
+ * connected state before you can do any operation with
+ * resources.
+ *
+ * @param cx murphy connection context.
+ * @param err error message.
+ * @param userdata data you gave when starting to connect.
+ */
+typedef void (*mrp_res_state_callback_t) (mrp_res_context_t *cx,
+ mrp_res_error_t err, void *userdata);
+
+/**
+ * Prototype for resource update callback. All changes related to
+ * your acquired resource set is handled through this function.
+ * It is up to you to decide what change in the set is important
+ * for you. This is an update to the set created by you and you
+ * can find the differences by comparison.
+ *
+ * @param cx murphy connection context.
+ * @param set updated resource set for you to handle.
+ * @param userdata data you gave when starting to acquire resources.
+ */
+typedef void (*mrp_res_resource_callback_t) (mrp_res_context_t *cx,
+ const mrp_res_resource_set_t *rs, void *userdata);
+
+/**
+ * Connect to murphy. You have to wait for the callback
+ * to check that state is connected.
+ *
+ * @param ml pointer to murphy mainloop.
+ * @param cb connection state callback from Murphy resource engine.
+ * @param userdata pointer to possible data you want to access in
+ * state callback.
+ *
+ * @return pointer to the newly created resource context.
+ */
+mrp_res_context_t *mrp_res_create(mrp_mainloop_t *ml,
+ mrp_res_state_callback_t cb, void *userdata);
+
+/**
+ * Disconnect from murphy.
+ *
+ * @param cx Murphy connection context to destroy.
+ */
+void mrp_res_destroy(mrp_res_context_t *cx);
+
+/**
+ * List possible application classes that you can assign yourself
+ * when asking for resources. This info is cached to the client
+ * library when creating the connection so it will be synchronous.
+ */
+const mrp_res_string_array_t * mrp_res_list_application_classes(
+ mrp_res_context_t *cx);
+
+/**
+ * List all possible resources that you can try to acquire. This info
+ * is cached to the client library when creating connection so it
+ * will be synchronous. This is a "master" resource set you can't
+ * modify or use as your own resource set. It is only meant for
+ * introspecting the possible resources.
+ */
+const mrp_res_resource_set_t * mrp_res_list_resources(mrp_res_context_t *cx);
+
+/**
+ * Create new empty resource set. This is a resource set allocated
+ * for you so you have to remember to release it.
+ *
+ * @param cx murphy connection context.
+ * @param app_class application class for the resource set.
+ * @param cb resource update callback.
+ * @param userdata data you want to access in resource callback.
+ *
+ * @return pointer to a new empty resource set.
+ */
+mrp_res_resource_set_t * mrp_res_create_resource_set(mrp_res_context_t *cx,
+ const char *app_class, mrp_res_resource_callback_t cb, void *userdata);
+
+/**
+ * Set automatic release mode to the resource set. This means that if an
+ * application loses the resource set, it doesn't automatically get it back
+ * when the resource becomes available again. By default the automatic
+ * release mode is off.
+ *
+ * @param status automatic release status: TRUE means on, FALSE means off
+ * @param rs resource set that is being updated.
+ *
+ * @return true if successful, false otherwise.
+ */
+bool mrp_res_set_autorelease(bool status,
+ mrp_res_resource_set_t *rs);
+
+/**
+ * Delete resource set created with mrp_res_create_resource_set
+ * or mrp_res_copy_resource_set.
+ *
+ * @param set pointer to existing resource set created by the user.
+ */
+void mrp_res_delete_resource_set(mrp_res_resource_set_t *rs);
+
+/**
+ * Make a copy of the resource set. This is a helper function to
+ * be used for example when you receive updated resource set in
+ * resource callback.
+ *
+ * @param original resource set to be copied.
+ *
+ * @return pointer to a copy of the resource set.
+ */
+mrp_res_resource_set_t *mrp_res_copy_resource_set(const mrp_res_resource_set_t *orig);
+
+/**
+ * You might have assigned the same update callback for
+ * several resource sets and you have to identify the
+ * updated set. You can compare your locale copy to
+ * find out if the update concerns that particular set.
+ *
+ * @param a set to be used in comparison
+ * @param b set to be used in comparison
+ *
+ * @return true when matching, false otherwise.
+ */
+bool mrp_res_equal_resource_set(const mrp_res_resource_set_t *a,
+ const mrp_res_resource_set_t *b);
+
+/**
+ * Acquisition and release:
+ *
+ * These two functions serve two purposes. First,
+ * they start the attempt of acquisition or release of
+ * a set of resources. Second, both of them - if
+ * successful - start the delivery of Resource
+ * callbacks to the calling application regarding
+ * the affected resource set.
+ *
+ * What the second point means is that, in case an
+ * application wants to know the state of resources,
+ * but not actually acquire them, it is valid to
+ * "release" a set containing these resources
+ * without acquiring them first.
+ */
+
+/**
+ * Acquire resources. Errors in the return value will
+ * indicate only connection problems or malformed
+ * resource structs. If you will be granted the resources
+ * you asked for you will get an update for your resource
+ * set in the resource callback.
+ *
+ * @param rs resource set you want to acquire.
+ *
+ * @return murphy error code.
+ */
+int mrp_res_acquire_resource_set(const mrp_res_resource_set_t *rs);
+
+/**
+ * Release a resource set. Releasing a set of resources
+ * will not stop delivery of Resource callbacks for that
+ * set, updates for its status will still be delivered.
+ *
+ * This function can be called even with not yet acquired
+ * sets in order to start delivery of Resource callbacks
+ * for them, which can be useful for applications wishing
+ * to survey the state of specific set of resources without
+ * actually affecting it.
+ *
+ * @param rs resource set you want to release.
+ *
+ * @return murphy error code.
+ */
+int mrp_res_release_resource_set(mrp_res_resource_set_t *rs);
+
+
+/**
+ * Get a resource set unique server-side id. The id information is
+ * normally available only after mrp_res_acquire_resource_set or
+ * mrp_res_release_resource_set function callback has been called.
+ *
+ * The id is the resource set internal id, available on the resource
+ * manager side. The client can use this information to associate other
+ * properties with the resource set. The resource manager can then use
+ * this extra information to process system events.
+ *
+ * An example would be to set an audio stream property to contain the
+ * resource set id. The resource manager can use the data associated
+ * with audio streams to find out which streams belong to which resource
+ * set in the audio domain controller.
+ *
+ * @param rs resource set whose id is queried.
+ *
+ * @return resource set id.
+ **/
+int mrp_res_get_resource_set_id(mrp_res_resource_set_t *rs);
+
+
+/**
+ * Create new resource by name and init all other fields.
+ * Created resource will be automatically added to
+ * the resource set provided as argument.
+ *
+ * @param set resource the resource will be added to.
+ * @param name name of the resource you want to create.
+ * @param mandatory is the resource mandatory or not
+ * @param shared can the resource be shared or not
+ *
+ * @return pointer to new resource if succesful null otherwise.
+ */
+mrp_res_resource_t *mrp_res_create_resource(mrp_res_resource_set_t *rs,
+ const char *name, bool mandatory,
+ bool shared);
+
+/**
+ * Get the names of all resources in this resource set.
+ *
+ * @param rs resource set where the resource are.
+ *
+ * @return string array that needs to be freed with mrp_res_free_string_array
+ */
+mrp_res_string_array_t * mrp_res_list_resource_names(
+ const mrp_res_resource_set_t *rs);
+
+/**
+ * Delete resource by name from resource set.
+ *
+ * @param rs resource set where you want to get the resource.
+ * @param name name of the resource you want to get.
+ * @param pointer to resource pointer to be assigned.
+ *
+ * @return 0 if resource found.
+ */
+mrp_res_resource_t * mrp_res_get_resource_by_name(
+ const mrp_res_resource_set_t *rs, const char *name);
+
+/**
+ * Delete a resource from a resource set.
+ *
+ * @param res resource to be deleted.
+ *
+ */
+void mrp_res_delete_resource(mrp_res_resource_t *res);
+
+/**
+ * Delete resource by name from resource set.
+ *
+ * @param rs resource set where you want to remove the resource.
+ * @param name name of the resource you want to remove.
+ *
+ * @return true if resource found and removed.
+ */
+bool mrp_res_delete_resource_by_name(mrp_res_resource_set_t *rs,
+ const char *name);
+
+/**
+ * Get the names of all attributes in this resource.
+ *
+ * @param res resource where the attributes are taken.
+ *
+ * @return string array that needs to be freed with mrp_res_free_string_array
+ */
+mrp_res_string_array_t * mrp_res_list_attribute_names(
+ const mrp_res_resource_t *res);
+
+/**
+ * Get the particular resource attribute by name from the resource.
+ *
+ * @param res resource where the attributes are taken.
+ * @param name of the attribute that is fetched.
+ *
+ * @return attribute pointer to the fetched attribute.
+ */
+mrp_res_attribute_t * mrp_res_get_attribute_by_name(mrp_res_resource_t *res,
+ const char *name);
+
+/**
+ * Set new string attribute value to resource.
+ *
+ * @param attr attrÃbute pointer returned by mrp_res_get_attribute_by_name.
+ * @value value to be set, copied by the library.
+ *
+ * @return murphy error code.
+ */
+int mrp_res_set_attribute_string(mrp_res_attribute_t *attr,
+ const char *value);
+
+
+/**
+ * Set new unsigned integer attribute value to resource.
+ *
+ * @param attr attrÃbute pointer returned by mrp_res_get_attribute_by_name.
+ * @value value to be set.
+ *
+ * @return murphy error code.
+ */
+int mrp_res_set_attribute_uint(mrp_res_attribute_t *attr,
+ uint32_t value);
+
+
+/**
+ * Set new integer attribute value to resource.
+ *
+ * @param attr attrÃbute pointer returned by mrp_res_get_attribute_by_name.
+ * @value value to be set.
+ *
+ * @return murphy error code.
+ */
+int mrp_res_set_attribute_int(mrp_res_attribute_t *attr,
+ int32_t value);
+
+
+/**
+ * Set new unsigned integer attribute value to resource.
+ *
+ * @param attr attrÃbute pointer returned by mrp_res_get_attribute_by_name.
+ * @value value to be set.
+ *
+ * @return murphy error code.
+ */
+int mrp_res_set_attribute_double(mrp_res_attribute_t *attr,
+ double value);
+
+
+/**
+ * Free a string array.
+ *
+ * @param arr string array to be freed.
+ */
+void mrp_res_free_string_array(mrp_res_string_array_t *arr);
+
+
+/**
+ * Prototype for an external logger.
+ *
+ * @param level log level.
+ * @param file source file (__FILE__) he log message originated from.
+ * @param line source line (__LINE__) the log message originated from.
+ * @param func function (__func__) the log message originated from.
+ *
+ * @return none.
+ */
+typedef void (*mrp_res_logger_t) (mrp_log_level_t level, const char *file,
+ int line, const char *func,
+ const char *format, va_list args);
+
+/**
+ * Set an external logger for the resource library. All log messages
+ * produced by the library will be handed to this function. If you
+ * want to suppress all logs by the library, set the logger to NULL.
+ *
+ * @param logger the logger function to use.
+ *
+ * @return pointer to the previously active logger function.
+ */
+
+mrp_res_logger_t mrp_res_set_logger(mrp_res_logger_t logger);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_RESOURCE_API_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <murphy/plugins/resource-native/libmurphy-resource/resource-api.h>
+
+#define DEFAULT_SEED 101
+
+static unsigned int seed;
+
+typedef struct {
+ mrp_res_resource_set_t *rset;
+ bool acquired;
+ mrp_list_hook_t hook;
+} rset_item_t;
+
+typedef struct {
+ /* resource context */
+ mrp_res_context_t *cx;
+
+ /* iteration handling */
+ mrp_mainloop_t *ml;
+ mrp_deferred_t *d;
+ unsigned int iterations_left;
+
+ /* resource set list */
+ mrp_list_hook_t rsets;
+ int n_items;
+} fuzz_data_t;
+
+enum operation_e {
+ OP_CREATE = 0,
+ OP_DELETE,
+ OP_ACQUIRE,
+ OP_RELEASE,
+ OP_MAX
+} operations;
+
+
+static void next_seed() {
+ char tmp_array[9];
+ char buf[7];
+ int offset;
+ int len;
+ int i;
+ unsigned int tmp = seed * seed;
+
+ /* Von Neumann's algorithm */
+
+ snprintf(tmp_array, 8, "%u", tmp);
+
+ len = strlen(tmp_array);
+ offset = 8 - len;
+
+ /* move to the end of the buffer*/
+ memmove(tmp_array+offset, tmp_array, len);
+
+ for (i = 0; i < offset; i++) {
+ tmp_array[i] = ' '; /* space before the num */
+ }
+
+ buf[6] = '\0';
+
+ /* move the last three bytes */
+
+ buf[5] = tmp_array[7];
+ buf[4] = tmp_array[6];
+ buf[3] = tmp_array[5];
+
+ /* the first three bytes */
+
+ buf[2] = tmp_array[2];
+ buf[1] = tmp_array[1];
+ buf[0] = tmp_array[0];
+
+ seed = strtoul(buf, NULL, 10);
+
+ /* printf("new seed: %u\n", seed); */
+}
+
+static int get_index(int max)
+{
+ next_seed();
+ return seed % max;
+}
+
+static void shuffle_strings(char **strings, int n) {
+ char **p; /* non-shuffled strings */
+ char *swap;
+ int i, rem, idx;
+
+ /* in place shuffle */
+
+ for (i = 0, rem = n; i < n; i++, rem--) {
+ p = strings+i;
+
+ idx = get_index(rem);
+
+ /* swap the elements */
+ swap = p[idx];
+ p[idx] = strings[i];
+ strings[i] = swap;
+ }
+}
+
+static void resource_callback(mrp_res_context_t *cx,
+ const mrp_res_resource_set_t *rs,
+ void *userdata)
+{
+ MRP_UNUSED(cx);
+ MRP_UNUSED(rs);
+ MRP_UNUSED(userdata);
+
+ return;
+}
+
+static void acquire_rset(fuzz_data_t *data)
+{
+ int i;
+ mrp_list_hook_t *ip, *in;
+ rset_item_t *item = NULL;
+
+ if (data->n_items == 0)
+ goto error;
+
+ i = get_index(data->n_items);
+
+ mrp_list_foreach(&data->rsets, ip, in) {
+ item = mrp_list_entry(ip, rset_item_t, hook);
+
+ if (i-- == 0) {
+ if (!item->acquired) {
+ mrp_res_acquire_resource_set(item->rset);
+ item->acquired = TRUE;
+ }
+ return;
+ }
+ }
+
+error:
+ return;
+}
+
+static void release_rset(fuzz_data_t *data)
+{
+ int i;
+ mrp_list_hook_t *ip, *in;
+ rset_item_t *item = NULL;
+
+ if (data->n_items == 0)
+ goto error;
+
+ i = get_index(data->n_items);
+
+ mrp_list_foreach(&data->rsets, ip, in) {
+ item = mrp_list_entry(ip, rset_item_t, hook);
+
+ if (i-- == 0) {
+ if (item->acquired) {
+ mrp_res_release_resource_set(item->rset);
+ item->acquired = FALSE;
+ }
+ return;
+ }
+ }
+
+error:
+ return;
+}
+
+static void delete_rset(fuzz_data_t *data)
+{
+ int i;
+ mrp_list_hook_t *ip, *in;
+ rset_item_t *item = NULL;
+
+ if (data->n_items == 0)
+ goto error;
+
+ i = get_index(data->n_items);
+
+ mrp_list_foreach(&data->rsets, ip, in) {
+ item = mrp_list_entry(ip, rset_item_t, hook);
+
+ if (i-- == 0) {
+ mrp_list_delete(ip);
+ mrp_res_delete_resource_set(item->rset);
+ mrp_free(item);
+ data->n_items--;
+ return;
+ }
+ }
+
+error:
+ return;
+}
+
+static void create_resource(mrp_res_resource_set_t *rset, char *resource)
+{
+ mrp_res_resource_t *res;
+ bool attrs[][2] = { {TRUE, TRUE}, {TRUE, FALSE}, {FALSE, TRUE}, {FALSE, FALSE} };
+
+ bool *attr = attrs[get_index(4)];
+
+ res = mrp_res_create_resource(rset, resource, attr[0], attr[1]);
+
+ /* TODO: set some attributes */
+
+ MRP_UNUSED(res);
+
+ return;
+}
+
+static void create_rset(fuzz_data_t *data)
+{
+ mrp_res_resource_set_t *rset;
+ rset_item_t *item;
+
+ char *app_classes[] = { "player", "game", "navigator" };
+ char *app_class = app_classes[get_index(3)];
+
+ char *resources[] = { "audio_playback", "audio_recording" };
+ int n_resources = get_index(1) + 1;
+ int i;
+
+ item = (rset_item_t *) mrp_allocz(sizeof(rset_item_t));
+
+ if (!item)
+ goto error;
+
+ mrp_list_init(&item->hook);
+
+ rset = mrp_res_create_resource_set(data->cx,
+ app_class, resource_callback, data);
+
+ if (!rset)
+ goto error;
+
+ /* create resources */
+
+ shuffle_strings(resources, 2);
+
+ for (i = 0; i < n_resources; i++) {
+ create_resource(rset, resources[i]);
+ }
+
+ /* put the resource set to the rset list */
+
+ item->rset = rset;
+ item->acquired = FALSE;
+
+ mrp_list_append(&data->rsets, &item->hook);
+
+ data->n_items++;
+
+ return;
+
+error:
+ return;
+}
+
+static void fuzz_iteration(mrp_deferred_t *d, void *user_data)
+{
+ enum operation_e op = (enum operation_e) get_index(OP_MAX);
+
+ fuzz_data_t *data = (fuzz_data_t *) user_data;
+
+ data->iterations_left--;
+
+ if (data->iterations_left == 0)
+ mrp_disable_deferred(d);
+
+#if 1
+ printf("iterations left: %d, operation: %d\n", data->iterations_left,
+ op);
+#endif
+
+ switch (op) {
+ case OP_CREATE:
+ create_rset(data);
+ break;
+ case OP_DELETE:
+ delete_rset(data);
+ break;
+ case OP_ACQUIRE:
+ acquire_rset(data);
+ break;
+ case OP_RELEASE:
+ release_rset(data);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void state_callback(mrp_res_context_t *context,
+ mrp_res_error_t err,
+ void *userdata)
+{
+ fuzz_data_t *data = (fuzz_data_t *) userdata;
+
+ if (err != MRP_RES_ERROR_NONE) {
+ printf("error message received from Murphy\n");
+ goto error;
+ }
+
+ switch (context->state) {
+ case MRP_RES_CONNECTED:
+ data->d = mrp_add_deferred(data->ml, fuzz_iteration, data);
+ if (!data->d) {
+ printf("Error creating iteration loop\n");
+ goto error;
+ }
+ break;
+ case MRP_RES_DISCONNECTED:
+ if (data->d)
+ mrp_del_deferred(data->d);
+ goto error;
+ }
+ return;
+
+error:
+ /* TODO (for memory analysis reasons) */
+ /* exit(1); */
+ return;
+}
+
+static void usage()
+{
+ printf("Usage:\n");
+ printf("\tresource-api-fuzz <iterations> [seed]\n");
+}
+
+int main(int argc, char **argv)
+{
+ mrp_mainloop_t *ml;
+ fuzz_data_t data;
+
+ memset(&data, 0, sizeof(fuzz_data_t));
+
+ if (argc < 2) {
+ usage();
+ exit(1);
+ }
+
+ if (argc >= 3)
+ seed = strtoul(argv[2], NULL, 10);
+ else
+ seed = DEFAULT_SEED;
+
+ if ((ml = mrp_mainloop_create()) == NULL)
+ exit(1);
+
+ data.cx = mrp_res_create(ml, state_callback, &data);
+ data.ml = ml;
+ data.iterations_left = strtoul(argv[1], NULL, 10);
+ mrp_list_init(&data.rsets);
+
+ /* start looping */
+ mrp_mainloop_run(ml);
+
+ mrp_res_destroy(data.cx);
+ mrp_mainloop_destroy(ml);
+
+ data.cx = NULL;
+
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013 Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <murphy/common/log.h>
+
+#include "resource-api.h"
+#include "resource-private.h"
+
+static void default_logger(mrp_log_level_t level, const char *file,
+ int line, const char *func,
+ const char *format, va_list args)
+{
+ va_list ap;
+
+ va_copy(ap, args);
+ mrp_log_msgv(level, file, line, func, format, ap);
+ va_end(ap);
+}
+
+
+static mrp_res_logger_t __res_logger = default_logger;
+
+mrp_res_logger_t mrp_res_set_logger(mrp_res_logger_t logger)
+{
+ mrp_res_logger_t old = __res_logger;
+
+ __res_logger = logger;
+
+ return old;
+}
+
+
+void mrp_res_log_msg(mrp_log_level_t level, const char *file,
+ int line, const char *func, const char *format, ...)
+{
+ va_list ap;
+
+ if (__res_logger != NULL) {
+ va_start(ap, format);
+ __res_logger(level, file, line, func, format, ap);
+ va_end(ap);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_API_PRIVATE_H__
+#define __MURPHY_RESOURCE_API_PRIVATE_H__
+
+#include <stdarg.h>
+
+#include <murphy/common/log.h>
+
+#include "resource-api.h"
+
+MRP_CDECL_BEGIN
+
+typedef enum {
+ MRP_RES_PENDING_OPERATION_NONE = 0,
+ MRP_RES_PENDING_OPERATION_ACQUIRE,
+ MRP_RES_PENDING_OPERATION_RELEASE,
+} pending_operation_t;
+
+typedef struct {
+ const char *name;
+ mrp_res_attribute_type_t type; /* s:char *, i:int32_t, u:uint32_t, f:double */
+ union {
+ const char *string;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+ };
+} attribute_t;
+
+typedef struct {
+ uint32_t dim;
+ mrp_res_attribute_t elems[0];
+} attribute_array_t;
+
+typedef struct {
+ const char *name;
+ int num_attrs;
+ mrp_res_attribute_t *attrs;
+} resource_def_t;
+
+typedef struct {
+ uint32_t dim;
+ resource_def_t defs[0];
+} resource_def_array_t;
+
+struct mrp_res_resource_private_s {
+ mrp_res_resource_t *pub; /* composition */
+ mrp_res_resource_set_t *set; /* owning set */
+
+ bool mandatory;
+ bool shared;
+ int num_attributes;
+ mrp_res_attribute_t *attrs;
+ uint32_t server_id;
+};
+
+struct mrp_res_resource_set_private_s {
+ mrp_res_resource_set_t *pub; /* composition */
+ mrp_res_context_t *cx; /* the context of this resource set */
+ uint32_t id; /* id given by the server */
+ uint32_t internal_id; /* id for checking identity */
+ uint32_t internal_ref_count;
+ uint32_t seqno;
+
+ bool autorelease;
+
+ mrp_res_resource_callback_t cb;
+ void *user_data;
+
+ uint32_t num_resources;
+ mrp_res_resource_t **resources;
+
+ pending_operation_t waiting_for;
+
+ mrp_list_hook_t hook;
+};
+
+struct mrp_res_context_private_s {
+ int connection_id;
+
+ /* mapping of server-side resource set numbers to library resource sets */
+ mrp_htbl_t *rset_mapping;
+
+ /* mapping of library resource sets to client resource sets */
+ mrp_htbl_t *internal_rset_mapping;
+
+ mrp_res_state_callback_t cb;
+ void *user_data;
+
+ mrp_mainloop_t *ml;
+ mrp_sockaddr_t saddr;
+ mrp_transport_t *transp;
+ bool connected;
+
+ mrp_res_string_array_t *master_classes;
+ mrp_res_resource_set_t *master_resource_set;
+
+ /* sometimes we need to know which query was answered */
+ uint32_t next_seqno;
+
+ /* running number for identifying resource sets */
+ uint32_t next_internal_id;
+
+ mrp_list_hook_t pending_sets;
+};
+
+uint32_t p_to_u(const void *p);
+void *u_to_p(uint32_t u);
+
+/*
+ * logging macros
+ */
+
+#define __LOCATION__ __FILE__,__LINE__,__FUNCTION__
+
+#define mrp_res_info(format, args...) \
+ mrp_res_log_msg(MRP_LOG_INFO, __LOCATION__, format, ## args)
+
+#define mrp_res_warning(format, args...) \
+ mrp_res_log_msg(MRP_LOG_WARNING, __LOCATION__, format, ## args)
+
+#define mrp_res_error(format, args...) \
+ mrp_res_log_msg(MRP_LOG_ERROR, __LOCATION__, format, ## args)
+
+void mrp_res_log_msg(mrp_log_level_t level, const char *file, int line,
+ const char *func, const char *format, ...);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_RESOURCE_API_PRIVATE_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/resource/protocol.h>
+#include "resource-api.h"
+#include "resource-private.h"
+
+#include "string_array.h"
+#include "message.h"
+#include "rset.h"
+#include "attribute.h"
+
+
+void *u_to_p(uint32_t u)
+{
+#ifdef __SIZEOF_POINTER__
+#if __SIZEOF_POINTER__ == 8
+ uint64_t o = u;
+#else
+ uint32_t o = u;
+#endif
+#else
+ uint32_t o = o;
+#endif
+ return (void *) o;
+}
+
+
+uint32_t p_to_u(const void *p)
+{
+#ifdef __SIZEOF_POINTER__
+#if __SIZEOF_POINTER__ == 8
+ uint32_t o = 0;
+ uint64_t big = (uint64_t) p;
+ o = big & 0xffffffff;
+#else
+ uint32_t o = (uint32_t) p;
+#endif
+#else
+ uint32_t o = p;
+#endif
+ return o;
+}
+
+
+int int_comp(const void *key1, const void *key2)
+{
+ return key1 != key2;
+}
+
+
+uint32_t int_hash(const void *key)
+{
+ return p_to_u(key);
+}
+
+
+static void resource_event(mrp_msg_t *msg,
+ mrp_res_context_t *cx,
+ int32_t seqno,
+ void **pcursor)
+{
+ uint32_t rset_id;
+ uint32_t grant, advice;
+ mrp_resproto_state_t state;
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+ uint32_t resid;
+ const char *resnam;
+ mrp_res_attribute_t attrs[ATTRIBUTE_MAX + 1];
+ int n_attrs;
+ uint32_t mask, all = 0x0, mandatory = 0x0;
+ uint32_t i;
+ mrp_res_resource_set_t *rset;
+
+ mrp_res_info("Resource event (request no %u):", seqno);
+
+ if (!fetch_resource_set_id(msg, pcursor, &rset_id) ||
+ !fetch_resource_set_state(msg, pcursor, &state) ||
+ !fetch_resource_set_mask(msg, pcursor, 0, &grant) ||
+ !fetch_resource_set_mask(msg, pcursor, 1, &advice)) {
+ mrp_res_error("failed to fetch data from message");
+ goto ignore;
+ }
+
+ /* Update our "master copy" of the resource set. */
+
+ rset = mrp_htbl_lookup(cx->priv->rset_mapping, u_to_p(rset_id));
+
+ if (!rset) {
+ mrp_res_info("resource event outside the resource set lifecycle");
+ goto ignore;
+ }
+
+ while (mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size)) {
+
+ mrp_res_resource_t *res = NULL;
+
+ if ((tag != RESPROTO_RESOURCE_ID || type != MRP_MSG_FIELD_UINT32) ||
+ !fetch_resource_name(msg, pcursor, &resnam)) {
+ mrp_res_error("failed to read resource from message");
+ goto ignore;
+ }
+
+ res = get_resource_by_name(rset, resnam);
+
+ if (!res) {
+ mrp_res_error("resource doesn't exist in resource set");
+ goto ignore;
+ }
+
+ resid = value.u32;
+
+ mrp_res_info("data for '%s': %d", res->name, resid);
+
+ if (!fetch_attribute_array(msg, pcursor, ATTRIBUTE_MAX + 1, attrs,
+ &n_attrs)) {
+ mrp_res_error("failed to read attributes from message");
+ goto ignore;
+ }
+
+ /* copy the attributes */
+ for (i = 0; (int) i < n_attrs; i++) {
+ mrp_res_attribute_t *src = &attrs[i];
+ mrp_res_attribute_t *dst = mrp_res_get_attribute_by_name(res, src->name);
+
+ if (!dst) {
+ mrp_log_error("unknown attribute '%s'!", src->name);
+ continue;
+ }
+
+ if (src->type != dst->type) {
+ mrp_log_error("attribute types don't match for '%s'!", src->name);
+ }
+
+ switch (src->type) {
+ case mrp_int32:
+ mrp_res_set_attribute_int(dst, src->integer);
+ break;
+ case mrp_uint32:
+ mrp_res_set_attribute_uint(dst, src->unsignd);
+ break;
+ case mrp_double:
+ mrp_res_set_attribute_double(dst, src->floating);
+ break;
+ case mrp_string:
+ mrp_res_set_attribute_string(dst, src->string);
+ break;
+ default: /* mrp_invalid */
+ break;
+ }
+ }
+ }
+
+ /* go through all resources and see if they have been modified */
+
+ for (i = 0; i < rset->priv->num_resources; i++)
+ {
+ mrp_res_resource_t *res = rset->priv->resources[i];
+
+ mask = (1UL << res->priv->server_id);
+ all |= mask;
+
+ if (res->priv->mandatory)
+ mandatory |= mask;
+
+ if (grant & mask) {
+ res->state = MRP_RES_RESOURCE_ACQUIRED;
+ }
+ else {
+ res->state = MRP_RES_RESOURCE_LOST;
+ }
+ }
+
+ mrp_res_info("advice = 0x%08x, grant = 0x%08x, mandatory = 0x%08x, all = 0x%08x",
+ advice, grant, mandatory, all);
+
+ if (grant) {
+ rset->state = MRP_RES_RESOURCE_ACQUIRED;
+ }
+ else if (advice == mandatory) {
+ rset->state = MRP_RES_RESOURCE_AVAILABLE;
+ }
+ else {
+ rset->state = MRP_RES_RESOURCE_LOST;
+ }
+
+ /* Check the resource set state. If the set is under construction
+ * (we are waiting for "acquire" or "release" message), do not do the
+ * callback before that. Otherwise, if this is a real event, call the
+ * callback right away. */
+
+#if 0
+ print_resource_set(rset);
+#endif
+ if (!rset->priv->seqno) {
+ if (rset->priv->cb) {
+ increase_ref(cx, rset);
+ rset->priv->cb(cx, rset, rset->priv->user_data);
+ decrease_ref(cx, rset);
+ }
+ }
+
+ return;
+
+ ignore:
+ mrp_res_info("ignoring resource event");
+}
+
+
+static void recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data)
+{
+ mrp_res_context_t *cx = user_data;
+ void *cursor = NULL;
+ uint32_t seqno;
+ uint16_t req;
+ mrp_res_error_t err = MRP_RES_ERROR_INTERNAL;
+
+ MRP_UNUSED(transp);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (!fetch_seqno(msg, &cursor, &seqno) ||
+ !fetch_request(msg, &cursor, &req))
+ goto error;
+
+ mrp_res_info("received message %d for %p", req, cx);
+
+ err = MRP_RES_ERROR_MALFORMED;
+
+ switch (req) {
+ case RESPROTO_QUERY_RESOURCES:
+
+ mrp_res_info("received QUERY_RESOURCES response");
+
+ cx->priv->master_resource_set =
+ resource_query_response(cx, msg, &cursor);
+ if (!cx->priv->master_resource_set)
+ goto error;
+ break;
+ case RESPROTO_QUERY_CLASSES:
+
+ mrp_res_info("received QUERY_CLASSES response");
+
+ cx->priv->master_classes = class_query_response(msg, &cursor);
+ if (!cx->priv->master_classes)
+ goto error;
+ break;
+ case RESPROTO_CREATE_RESOURCE_SET:
+ {
+ mrp_res_resource_set_private_t *priv = NULL;
+ mrp_res_resource_set_t *rset = NULL;
+ mrp_list_hook_t *p, *n;
+
+ mrp_res_info("received CREATE_RESOURCE_SET response");
+
+ /* get the correct resource set from the pending_sets list */
+
+ mrp_list_foreach(&cx->priv->pending_sets, p, n) {
+ priv = mrp_list_entry(p, typeof(*priv), hook);
+
+ if (priv->seqno == seqno) {
+ rset = priv->pub;
+ break;
+ }
+ }
+
+ if (!rset) {
+ /* the corresponding set wasn't found */
+ goto error;
+ }
+
+ mrp_list_delete(&rset->priv->hook);
+
+ if (!create_resource_set_response(msg, rset, &cursor))
+ goto error;
+
+ mrp_htbl_insert(cx->priv->rset_mapping,
+ u_to_p(rset->priv->id), rset);
+
+ /* TODO: if the operation was "acquire", do that. Otherwise
+ * release. */
+
+ if (rset->priv->waiting_for == MRP_RES_PENDING_OPERATION_ACQUIRE) {
+ rset->priv->waiting_for = MRP_RES_PENDING_OPERATION_NONE;
+ if (acquire_resource_set_request(cx, rset) < 0) {
+ goto error;
+ }
+ }
+ else if (rset->priv->waiting_for == MRP_RES_PENDING_OPERATION_RELEASE) {
+ rset->priv->waiting_for = MRP_RES_PENDING_OPERATION_NONE;
+ if (release_resource_set_request(cx, rset) < 0) {
+ goto error;
+ }
+ }
+ else {
+ goto error;
+ }
+ break;
+ }
+ case RESPROTO_ACQUIRE_RESOURCE_SET:
+ {
+ mrp_res_resource_set_t *rset;
+
+ mrp_res_info("received ACQUIRE_RESOURCE_SET response");
+
+ rset = acquire_resource_set_response(msg, cx, &cursor);
+
+ if (!rset) {
+ goto error;
+ }
+
+ rset->priv->seqno = 0;
+
+ break;
+ }
+ case RESPROTO_RELEASE_RESOURCE_SET:
+ {
+ mrp_res_resource_set_t *rset;
+ mrp_res_info("received RELEASE_RESOURCE_SET response");
+
+ rset = acquire_resource_set_response(msg, cx, &cursor);
+
+ if (!rset) {
+ goto error;
+ }
+
+ /* TODO: make new releases fail until seqno == 0 */
+ rset->priv->seqno = 0;
+
+ break;
+ }
+ case RESPROTO_RESOURCES_EVENT:
+ mrp_res_info("received RESOURCES_EVENT response");
+
+ resource_event(msg, cx, seqno, &cursor);
+ break;
+ case RESPROTO_DESTROY_RESOURCE_SET:
+ mrp_res_info("received DESTROY_RESOURCE_SET response");
+ /* TODO? */
+ break;
+ default:
+ break;
+ }
+
+ if (cx->state == MRP_RES_DISCONNECTED &&
+ cx->priv->master_classes &&
+ cx->priv->master_resource_set) {
+ cx->state = MRP_RES_CONNECTED;
+ cx->priv->cb(cx, MRP_RES_ERROR_NONE, cx->priv->user_data);
+ }
+
+ return;
+
+error:
+ mrp_res_error("error processing a message from the server");
+ cx->priv->cb(cx, err, cx->priv->user_data);
+}
+
+
+static void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void closed_evt(mrp_transport_t *transp, int error, void *user_data)
+{
+ mrp_res_context_t *cx = user_data;
+ MRP_UNUSED(transp);
+ MRP_UNUSED(error);
+
+ mrp_res_error("connection closed for %p", cx);
+ cx->priv->connected = FALSE;
+
+ if (cx->state == MRP_RES_CONNECTED) {
+ cx->state = MRP_RES_DISCONNECTED;
+ cx->priv->cb(cx, MRP_RES_ERROR_CONNECTION_LOST, cx->priv->user_data);
+ }
+}
+
+
+static void destroy_context(mrp_res_context_t *cx)
+{
+ if (!cx)
+ return;
+
+ if (cx->priv) {
+
+ if (cx->priv->transp)
+ mrp_transport_destroy(cx->priv->transp);
+
+ delete_resource_set(cx->priv->master_resource_set);
+
+ /* FIXME: is this the way we want to free all resources and
+ * resource sets? */
+ if (cx->priv->rset_mapping)
+ mrp_htbl_destroy(cx->priv->rset_mapping, false);
+
+ if (cx->priv->internal_rset_mapping)
+ mrp_htbl_destroy(cx->priv->internal_rset_mapping, true);
+
+ mrp_res_free_string_array(cx->priv->master_classes);
+
+ mrp_free(cx->priv);
+ }
+ mrp_free(cx);
+}
+
+
+static void htbl_free_rset_mapping(void *key, void *object)
+{
+#if 0
+ mrp_res_info("> htbl_free_rset_mapping(%d, %p)", p_to_u(key), object);
+#else
+ MRP_UNUSED(key);
+#endif
+
+ mrp_res_resource_set_t *rset = object;
+ free_resource_set(rset);
+}
+
+/* public API */
+
+mrp_res_context_t *mrp_res_create(mrp_mainloop_t *ml,
+ mrp_res_state_callback_t cb,
+ void *userdata)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = recv_msg },
+ { .recvmsgfrom = recvfrom_msg },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ int alen;
+ const char *type;
+ mrp_htbl_config_t conf;
+ mrp_res_context_t *cx = mrp_allocz(sizeof(mrp_res_context_t));
+
+ if (!cx)
+ goto error;
+
+ cx->priv = mrp_allocz(sizeof(struct mrp_res_context_private_s));
+
+ if (!cx->priv)
+ goto error;
+
+ cx->priv->next_seqno = 1;
+ cx->priv->next_internal_id = 1;
+ cx->priv->ml = ml;
+ cx->priv->connection_id = 0;
+ cx->priv->cb = cb;
+ cx->priv->user_data = userdata;
+
+ conf.comp = int_comp;
+ conf.hash = int_hash;
+ conf.free = htbl_free_rset_mapping;
+ conf.nbucket = 0;
+ conf.nentry = 5;
+
+ /* When the resource set is "created" on the server side, we get
+ * back an id. The id is then mapped to the actual resource set on
+ * the client side, so that the event can be addressed to the
+ * correct resource set. */
+ cx->priv->rset_mapping = mrp_htbl_create(&conf);
+
+ if (!cx->priv->rset_mapping)
+ goto error;
+
+ /* When a resource set is acquired, we are keeping a "master copy" on the
+ * server side. The client can free and copy this resource set as much as
+ * it wants. The internal id is a method for understanding which resource
+ * set maps to which. */
+ cx->priv->internal_rset_mapping = mrp_htbl_create(&conf);
+
+ if (!cx->priv->internal_rset_mapping)
+ goto error;
+
+ /* connect to Murphy */
+
+ alen = mrp_transport_resolve(NULL, mrp_resource_get_default_address(),
+ &cx->priv->saddr, sizeof(cx->priv->saddr), &type);
+
+ cx->priv->transp = mrp_transport_create(cx->priv->ml, type,
+ &evt, cx, 0);
+
+ if (!cx->priv->transp)
+ goto error;
+
+ if (!mrp_transport_connect(cx->priv->transp, &cx->priv->saddr, alen))
+ goto error;
+
+ cx->priv->connected = TRUE;
+ cx->state = MRP_RES_DISCONNECTED;
+
+ if (get_application_classes_request(cx) < 0 || get_available_resources_request(cx) < 0) {
+ goto error;
+ }
+
+ /* TODO: this needs to be gotten from an environment variable */
+ cx->zone = "driver";
+
+ mrp_list_init(&cx->priv->pending_sets);
+
+ return cx;
+
+error:
+
+ mrp_res_error("error connecting to server");
+ destroy_context(cx);
+
+ return NULL;
+}
+
+
+void mrp_res_destroy(mrp_res_context_t *cx)
+{
+ destroy_context(cx);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include "rset.h"
+#include "attribute.h"
+#include "message.h"
+
+
+static char *state_to_str(mrp_res_resource_state_t st)
+{
+ char *state = "unknown";
+ switch (st) {
+ case MRP_RES_RESOURCE_ACQUIRED:
+ state = "acquired";
+ break;
+ case MRP_RES_RESOURCE_LOST:
+ state = "lost";
+ break;
+ case MRP_RES_RESOURCE_AVAILABLE:
+ state = "available";
+ break;
+ case MRP_RES_RESOURCE_PENDING:
+ state = "pending";
+ break;
+ }
+ return state;
+}
+
+
+void print_resource(mrp_res_resource_t *res)
+{
+ mrp_res_info(" resource '%s' -> '%s' : %smandatory, %sshared",
+ res->name, state_to_str(res->state),
+ res->priv->mandatory ? " " : "not ",
+ res->priv->shared ? "" : "not ");
+}
+
+#if 0
+void print_resource_set(mrp_res_resource_set_t *rset)
+{
+ uint32_t i;
+ mrp_res_resource_t *res;
+
+ mrp_res_info("Resource set %i/%i (%s) -> '%s':",
+ rset->priv->id, rset->priv->internal_id,
+ rset->application_class, state_to_str(rset->state));
+
+ for (i = 0; i < rset->priv->num_resources; i++) {
+ res = rset->priv->resources[i];
+ print_resource(res);
+ }
+}
+#endif
+
+void increase_ref(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset)
+{
+ MRP_UNUSED(cx);
+
+ if (!rset)
+ return;
+
+ rset->priv->internal_ref_count++;
+}
+
+
+static int destroy_resource_set_request(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset)
+{
+ mrp_msg_t *msg = NULL;
+
+ if (!cx->priv->connected)
+ goto error;
+
+ rset->priv->seqno = cx->priv->next_seqno;
+
+ msg = mrp_msg_create(
+ RESPROTO_SEQUENCE_NO, MRP_MSG_FIELD_UINT32, cx->priv->next_seqno++,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16,
+ RESPROTO_DESTROY_RESOURCE_SET,
+ RESPROTO_RESOURCE_SET_ID, MRP_MSG_FIELD_UINT32, rset->priv->id,
+ RESPROTO_MESSAGE_END);
+
+ if (!msg)
+ goto error;
+
+ if (!mrp_transport_send(cx->priv->transp, msg))
+ goto error;
+
+ mrp_msg_unref(msg);
+ return 0;
+
+error:
+ mrp_msg_unref(msg);
+ return -1;
+}
+
+
+void decrease_ref(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset)
+{
+ if (!rset)
+ return;
+
+ rset->priv->internal_ref_count--;
+
+ if (rset->priv->internal_ref_count == 0) {
+ mrp_log_info("delete the server resource set now");
+ destroy_resource_set_request(cx, rset);
+
+ /* if a rset is deleted, remove it from the pending sets */
+ mrp_list_delete(&rset->priv->hook);
+
+ mrp_htbl_remove(cx->priv->rset_mapping,
+ u_to_p(rset->priv->id), FALSE);
+ mrp_htbl_remove(cx->priv->internal_rset_mapping,
+ u_to_p(rset->priv->internal_id), TRUE);
+ }
+}
+
+
+mrp_res_resource_t *get_resource_by_name(mrp_res_resource_set_t *rset,
+ const char *name)
+{
+ uint32_t i;
+
+ if (!rset || !name)
+ return NULL;
+
+ for (i = 0; i < rset->priv->num_resources; i++) {
+ mrp_res_resource_t *res = rset->priv->resources[i];
+ if (strcmp(res->name, name) == 0) {
+ return res;
+ }
+ }
+
+ return NULL;
+}
+
+
+static void free_resource(mrp_res_resource_t *res)
+{
+ if (!res)
+ return;
+
+ mrp_free((void *) res->name);
+
+ if (res->priv) {
+ mrp_attribute_array_free(res->priv->attrs,
+ res->priv->num_attributes);
+ }
+
+ mrp_free(res->priv);
+ mrp_free(res);
+}
+
+
+void free_resource_set(mrp_res_resource_set_t *rset)
+{
+ uint32_t i;
+
+ if (!rset)
+ return;
+
+ mrp_free((void *) rset->application_class);
+
+ if (!rset->priv)
+ goto end;
+
+ for (i = 0; i < rset->priv->num_resources; i++) {
+ free_resource(rset->priv->resources[i]);
+ }
+ mrp_free(rset->priv->resources);
+ mrp_free(rset->priv);
+
+end:
+ mrp_free(rset);
+}
+
+
+
+void delete_resource_set(mrp_res_resource_set_t *rs)
+{
+ mrp_res_context_t *cx = NULL;
+
+ if (!rs)
+ return;
+
+ if (rs->priv && rs->priv->cx) {
+ cx = rs->priv->cx;
+
+ /* check if the resource set being deleted is a library resource set */
+ mrp_res_resource_set_t *internal_rset = mrp_htbl_lookup(
+ cx->priv->internal_rset_mapping, u_to_p(rs->priv->internal_id));
+
+ if (internal_rset && internal_rset != rs) {
+ decrease_ref(cx, internal_rset);
+ }
+ }
+
+ free_resource_set(rs);
+}
+
+
+
+static mrp_res_resource_t *resource_copy(const mrp_res_resource_t *original,
+ mrp_res_resource_set_t *new_rset)
+{
+ mrp_res_resource_t *copy;
+
+ copy = mrp_allocz(sizeof(mrp_res_resource_t));
+
+ if (!copy)
+ goto error;
+
+ memcpy(copy, original, sizeof(mrp_res_resource_t));
+
+ copy->name = mrp_strdup(original->name);
+
+ if (!copy->name)
+ goto error;
+
+ copy->priv = mrp_allocz(sizeof(mrp_res_resource_private_t));
+
+ if (!copy->priv)
+ goto error;
+
+ memcpy(copy->priv, original->priv, sizeof(mrp_res_resource_private_t));
+
+ copy->priv->pub = copy;
+ copy->priv->set = new_rset;
+
+ copy->priv->attrs = mrp_attribute_array_dup(original->priv->num_attributes,
+ original->priv->attrs);
+
+ if (!copy->priv->attrs)
+ goto error;
+
+ return copy;
+
+error:
+ mrp_res_error("failed to copy resource");
+
+ if (copy) {
+ mrp_free((void *) copy->name);
+ if (copy->priv) {
+ mrp_attribute_array_free(copy->priv->attrs,
+ original->priv->num_attributes);
+ mrp_free(copy->priv);
+ }
+ mrp_free(copy);
+ }
+
+ return NULL;
+}
+
+
+
+mrp_res_resource_set_t *resource_set_copy(
+ const mrp_res_resource_set_t *original)
+{
+ mrp_res_resource_set_t *copy = NULL;
+ uint32_t i;
+
+ copy = mrp_allocz(sizeof(mrp_res_resource_set_t));
+
+ if (!copy)
+ goto error;
+
+ copy->state = original->state;
+ copy->application_class = mrp_strdup(original->application_class);
+
+ if (!copy->application_class)
+ goto error;
+
+ copy->priv = mrp_allocz(sizeof(mrp_res_resource_set_private_t));
+
+ if (!copy->priv)
+ goto error;
+
+ memcpy(copy->priv, original->priv, sizeof(mrp_res_resource_set_private_t));
+
+ copy->priv->pub = copy;
+ copy->priv->resources = mrp_allocz_array(mrp_res_resource_t *,
+ original->priv->num_resources);
+
+ if (copy->priv->resources == NULL && copy->priv->num_resources)
+ goto error;
+
+ for (i = 0; i < copy->priv->num_resources; i++) {
+ copy->priv->resources[i] = resource_copy(original->priv->resources[i],
+ copy);
+ if (!copy->priv->resources[i]) {
+ copy->priv->num_resources = --i;
+ goto error;
+ }
+ }
+
+ memset(©->priv->hook, 0, sizeof(mrp_list_hook_t));
+ mrp_list_init(©->priv->hook);
+
+ return copy;
+
+error:
+ free_resource_set(copy);
+ return NULL;
+}
+
+
+static mrp_res_resource_set_t *create_resource_set(
+ mrp_res_context_t *cx,
+ const char *klass,
+ mrp_res_resource_callback_t cb,
+ void *userdata)
+{
+ mrp_res_resource_set_t *rs;
+ mrp_res_resource_set_t *internal;
+
+ if (cx->priv->master_resource_set == NULL)
+ return NULL;
+
+ rs = mrp_allocz(sizeof(mrp_res_resource_set_t));
+
+ if (!rs)
+ goto error;
+
+ rs->priv = mrp_allocz(sizeof(mrp_res_resource_set_private_t));
+ if (!rs->priv)
+ goto error;
+
+ rs->application_class = mrp_strdup(klass);
+
+ rs->priv->pub = rs;
+ rs->priv->cx = cx;
+ rs->priv->id = 0;
+ rs->priv->internal_id = cx->priv->next_internal_id++;
+ rs->priv->seqno = 0;
+ rs->priv->cb = cb;
+ rs->priv->user_data = userdata;
+ rs->state = MRP_RES_RESOURCE_PENDING;
+ rs->priv->autorelease = FALSE;
+
+ rs->priv->resources = mrp_allocz_array(mrp_res_resource_t *,
+ cx->priv->master_resource_set->priv->num_resources);
+
+ rs->priv->waiting_for = MRP_RES_PENDING_OPERATION_NONE;
+
+ mrp_list_init(&rs->priv->hook);
+
+ /* ok, create an library-side resource set that we can compare this one to */
+
+ internal = resource_set_copy(rs);
+ if (!internal)
+ goto error;
+
+ increase_ref(cx, internal);
+
+ mrp_htbl_insert(cx->priv->internal_rset_mapping,
+ u_to_p(internal->priv->internal_id), internal);
+
+ return rs;
+
+error:
+ mrp_log_error("error creating resource set");
+ delete_resource_set(rs);
+ return NULL;
+}
+
+
+static int update_library_resource_set(mrp_res_context_t *cx,
+ const mrp_res_resource_set_t *original,
+ mrp_res_resource_set_t *rset)
+{
+ char *application_class = NULL;
+ mrp_res_resource_t **resources = NULL;
+ uint32_t i, num_resources = 0;
+
+ if (!cx || !original)
+ return -1;
+
+ /* Update the rset with the values in the original resource set. There
+ * is only one "library-side" resource set corresponding 1-1 to the server
+ * resource set. The original is the "client-side" resource set, which there
+ * can be many. */
+
+ application_class = mrp_strdup(original->application_class);
+ if (!application_class) {
+ mrp_log_error("error with memory allocation");
+ goto error;
+ }
+
+ resources = mrp_allocz_array(mrp_res_resource_t *,
+ original->priv->num_resources);
+ if (!resources) {
+ mrp_log_error("error allocating %d resources", original->priv->num_resources);
+ goto error;
+ }
+
+ for (i = 0; i < original->priv->num_resources; i++) {
+ resources[i] = resource_copy(original->priv->resources[i], rset);
+ if (!resources[i]) {
+ mrp_log_error("error copying resources to library resource set");
+ goto error;
+ }
+ num_resources++;
+ }
+
+ mrp_free((void *) rset->application_class);
+ for (i = 0; i < rset->priv->num_resources; i++) {
+ free_resource(rset->priv->resources[i]);
+ }
+ mrp_free(rset->priv->resources);
+
+ rset->application_class = application_class;
+ rset->priv->resources = resources;
+ rset->priv->num_resources = num_resources;
+ rset->priv->autorelease = original->priv->autorelease;
+
+ return 0;
+
+error:
+ mrp_log_error("error updating library resource set");
+ mrp_free(application_class);
+ for (i = 0; i < num_resources; i++) {
+ free_resource(resources[i]);
+ }
+ mrp_free(resources);
+
+ return -1;
+}
+
+/* public API */
+
+const mrp_res_string_array_t * mrp_res_list_application_classes(
+ mrp_res_context_t *cx)
+{
+ if (!cx)
+ return NULL;
+
+ return cx->priv->master_classes;
+}
+
+
+mrp_res_resource_t *mrp_res_create_resource(
+ mrp_res_resource_set_t *set,
+ const char *name,
+ bool mandatory,
+ bool shared)
+{
+ mrp_res_resource_t *res = NULL, *proto = NULL;
+ uint32_t i = 0;
+ bool found = false;
+ uint32_t server_id = 0;
+ mrp_res_context_t *cx = NULL;
+
+ if (set == NULL)
+ return NULL;
+
+ cx = set->priv->cx;
+
+ if (cx == NULL || name == NULL)
+ return NULL;
+
+ for (i = 0; i < cx->priv->master_resource_set->priv->num_resources; i++) {
+ proto = cx->priv->master_resource_set->priv->resources[i];
+ if (strcmp(proto->name, name) == 0) {
+ found = true;
+ server_id = proto->priv->server_id;
+ break;
+ }
+ }
+
+ if (!found)
+ goto error;
+
+ res = mrp_allocz(sizeof(mrp_res_resource_t));
+
+ if (!res)
+ goto error;
+
+ res->name = mrp_strdup(name);
+
+ res->state = MRP_RES_RESOURCE_PENDING;
+
+ res->priv = mrp_allocz(sizeof(mrp_res_resource_private_t));
+
+ if (!res->priv)
+ goto error;
+
+ res->priv->server_id = server_id;
+ res->priv->mandatory = mandatory;
+ res->priv->shared = shared;
+ res->priv->pub = res;
+ res->priv->set = set;
+
+ /* copy the attributes with the default values */
+ res->priv->attrs = mrp_attribute_array_dup(proto->priv->num_attributes,
+ proto->priv->attrs);
+
+ res->priv->num_attributes = proto->priv->num_attributes;
+
+ /* add resource to resource set */
+ set->priv->resources[set->priv->num_resources++] = res;
+
+ return res;
+
+error:
+ mrp_res_error("mrp_res_create_resource error");
+ free_resource(res);
+
+ return NULL;
+}
+
+
+mrp_res_resource_set_t *mrp_res_copy_resource_set(
+ const mrp_res_resource_set_t *original)
+{
+ mrp_res_resource_set_t *copy, *internal;
+ mrp_res_context_t *cx = NULL;
+
+ copy = resource_set_copy(original);
+
+ if (!copy)
+ goto error;
+
+ cx = original->priv->cx;
+
+ /* increase the reference count of the library resource set */
+
+ internal = mrp_htbl_lookup(cx->priv->internal_rset_mapping,
+ u_to_p(original->priv->internal_id));
+
+ if (!internal)
+ goto error;
+
+ increase_ref(cx, internal);
+
+ return copy;
+
+error:
+ mrp_log_error("error copying a resource set");
+ free_resource_set(copy);
+ return NULL;
+}
+
+const mrp_res_resource_set_t * mrp_res_list_resources(
+ mrp_res_context_t *cx)
+{
+ if (cx == NULL || cx->priv == NULL)
+ return NULL;
+
+ return cx->priv->master_resource_set;
+}
+
+
+int mrp_res_release_resource_set(mrp_res_resource_set_t *original)
+{
+ mrp_res_resource_set_t *internal_set = NULL;
+ mrp_res_context_t *cx = original->priv->cx;
+
+ if (!cx || !cx->priv->connected)
+ goto error;
+
+ if (!original->priv->internal_id)
+ goto error;
+
+ internal_set = mrp_htbl_lookup(cx->priv->internal_rset_mapping,
+ u_to_p(original->priv->internal_id));
+
+ if (!internal_set)
+ goto error;
+
+ update_library_resource_set(cx, original, internal_set);
+
+ if (internal_set->priv->id) {
+ return release_resource_set_request(cx, internal_set);
+ }
+ else {
+ mrp_list_hook_t *p, *n;
+ mrp_res_resource_set_private_t *pending_rset;
+ bool found = FALSE;
+
+ /* Create the resource set if it doesn't already exist on the
+ * server. The releasing is continued when the set is created.
+ */
+
+ /* only append if not already present in the list */
+
+ mrp_list_foreach(&cx->priv->pending_sets, p, n) {
+ pending_rset = mrp_list_entry(p, mrp_res_resource_set_private_t, hook);
+ if (pending_rset == internal_set->priv) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ mrp_list_append(&cx->priv->pending_sets, &internal_set->priv->hook);
+ }
+
+ internal_set->priv->waiting_for = MRP_RES_PENDING_OPERATION_RELEASE;
+
+ if (create_resource_set_request(cx, internal_set) < 0) {
+ mrp_res_error("creating resource set failed");
+ mrp_list_delete(&internal_set->priv->hook);
+ goto error;
+ }
+
+ return 0;
+ }
+
+error:
+ mrp_res_error("mrp_release_resources error");
+
+ return -1;
+}
+
+
+bool mrp_res_equal_resource_set(const mrp_res_resource_set_t *a,
+ const mrp_res_resource_set_t *b)
+{
+ if (!a || !b)
+ return false;
+
+ /* Compare the internal IDs to figure out if the both sets are result
+ * of the same "create" call. */
+
+ return a->priv->internal_id == b->priv->internal_id;
+}
+
+
+mrp_res_resource_set_t *mrp_res_create_resource_set(mrp_res_context_t *cx,
+ const char *app_class,
+ mrp_res_resource_callback_t cb,
+ void *userdata)
+{
+ if (cx == NULL)
+ return NULL;
+
+ return create_resource_set(cx, app_class, cb, userdata);
+}
+
+
+void mrp_res_delete_resource_set(mrp_res_resource_set_t *set)
+{
+ delete_resource_set(set);
+}
+
+
+void mrp_res_delete_resource(mrp_res_resource_t *res)
+{
+ if (res->priv->set) {
+ if (!mrp_res_delete_resource_by_name(res->priv->set, res->name)) {
+ /* hmm, strange */
+ free_resource(res);
+ }
+ }
+ else
+ free_resource(res);
+}
+
+
+bool mrp_res_delete_resource_by_name(mrp_res_resource_set_t *rs, const char *name)
+{
+ uint32_t i;
+ mrp_res_resource_t *res = NULL;
+
+ /* assumption: only one resource of given name in the resource set */
+ for (i = 0; i < rs->priv->num_resources; i++) {
+ if (strcmp(rs->priv->resources[i]->name, name) == 0) {
+ /* found at i */
+ res = rs->priv->resources[i];
+ break;
+ }
+ }
+
+ if (i == rs->priv->num_resources) {
+ /* not found */
+ return false;
+ }
+
+ memmove(rs->priv->resources+i, rs->priv->resources+i+1,
+ (rs->priv->num_resources-i) * sizeof(mrp_res_resource_t *));
+
+ rs->priv->num_resources--;
+ rs->priv->resources[rs->priv->num_resources] = NULL;
+
+ free_resource(res);
+
+ return true;
+}
+
+
+mrp_res_string_array_t * mrp_res_list_resource_names(
+ const mrp_res_resource_set_t *rs)
+{
+ uint32_t i;
+ mrp_res_string_array_t *ret;
+
+ if (!rs)
+ return NULL;
+
+ ret = mrp_allocz(sizeof(mrp_res_string_array_t));
+
+ if (!ret)
+ return NULL;
+
+ ret->num_strings = rs->priv->num_resources;
+ ret->strings = mrp_allocz_array(const char *, rs->priv->num_resources);
+
+ if (!ret->strings) {
+ mrp_free(ret);
+ return NULL;
+ }
+
+ for (i = 0; i < rs->priv->num_resources; i++) {
+ ret->strings[i] = mrp_strdup(rs->priv->resources[i]->name);
+ if (!ret->strings[i]) {
+ ret->num_strings = i;
+ mrp_res_free_string_array(ret);
+ return NULL;
+ }
+ }
+
+ return ret;
+}
+
+
+mrp_res_resource_t * mrp_res_get_resource_by_name(
+ const mrp_res_resource_set_t *rs,
+ const char *name)
+{
+ uint32_t i;
+
+ if (!rs)
+ return NULL;
+
+ for (i = 0; i < rs->priv->num_resources; i++) {
+ if (strcmp(name, rs->priv->resources[i]->name) == 0) {
+ return rs->priv->resources[i];
+ }
+ }
+
+ return NULL;
+}
+
+bool mrp_res_set_autorelease(bool status,
+ mrp_res_resource_set_t *rs)
+{
+ if (!rs || !rs->priv->cx)
+ return FALSE;
+
+ /* the resource library doesn't allow updating already used sets */
+ if (rs->state != MRP_RES_RESOURCE_PENDING)
+ return FALSE;
+
+ rs->priv->autorelease = status;
+
+ return TRUE;
+}
+
+
+int mrp_res_acquire_resource_set(
+ const mrp_res_resource_set_t *original)
+{
+ mrp_res_resource_set_t *rset;
+ mrp_res_context_t *cx = original->priv->cx;
+
+ if (!cx->priv->connected) {
+ mrp_res_error("not connected to server");
+ goto error;
+ }
+
+ rset = mrp_htbl_lookup(cx->priv->internal_rset_mapping,
+ u_to_p(original->priv->internal_id));
+
+ if (!rset) {
+ mrp_res_error("trying to acquire non-existent resource set");
+ goto error;
+ }
+
+ update_library_resource_set(cx, original, rset);
+
+#if 0
+ print_resource_set(rset);
+#endif
+ if (rset->priv->id) {
+ /* the set has been already created on server */
+
+ if (rset->state == MRP_RES_RESOURCE_ACQUIRED) {
+ /* already requested, updating is not supported yet */
+ mrp_res_error("trying to re-acquire already acquired set");
+
+ /* TODO: when supported by backend
+ * type = RESPROTO_UPDATE_RESOURCE_SET
+ */
+ goto error;
+ }
+ else {
+ /* re-acquire a lost or released set */
+ return acquire_resource_set_request(cx, rset);
+ }
+ }
+ else {
+ mrp_list_hook_t *p, *n;
+ mrp_res_resource_set_private_t *pending_rset;
+ bool found = FALSE;
+
+ /* Create the resource set. The acquisition is continued
+ * when the set is created. */
+
+ /* only append if not already present in the list */
+
+ mrp_list_foreach(&cx->priv->pending_sets, p, n) {
+ pending_rset = mrp_list_entry(p, mrp_res_resource_set_private_t, hook);
+ if (pending_rset == rset->priv) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (!found) {
+ mrp_list_append(&cx->priv->pending_sets, &rset->priv->hook);
+ }
+
+ rset->priv->waiting_for = MRP_RES_PENDING_OPERATION_ACQUIRE;
+
+ if (create_resource_set_request(cx, rset) < 0) {
+ mrp_res_error("creating resource set failed");
+ mrp_list_delete(&rset->priv->hook);
+ goto error;
+ }
+ }
+
+ return 0;
+
+error:
+ mrp_log_error("error acquiring a resource set");
+ return -1;
+}
+
+
+int mrp_res_get_resource_set_id(mrp_res_resource_set_t *rs)
+{
+ mrp_res_resource_set_t *internal_set;
+ mrp_res_context_t *cx = NULL;
+
+ if (!rs || !rs->priv || !rs->priv->cx)
+ return 0;
+
+ cx = rs->priv->cx;
+
+ internal_set = mrp_htbl_lookup(cx->priv->internal_rset_mapping,
+ u_to_p(rs->priv->internal_id));
+
+ if (!internal_set || !internal_set->priv)
+ return 0;
+
+ return internal_set->priv->id;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_API_RSET_H__
+#define __MURPHY_RESOURCE_API_RSET_H__
+
+#include <errno.h>
+
+#include <murphy/resource/protocol.h>
+#include "resource-api.h"
+#include "resource-private.h"
+
+#define RESOURCE_MAX 32
+
+void print_resource(mrp_res_resource_t *res);
+
+void print_resource_set(mrp_res_resource_set_t *rset);
+
+
+void increase_ref(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset);
+
+void decrease_ref(mrp_res_context_t *cx,
+ mrp_res_resource_set_t *rset);
+
+
+void free_resource_set(mrp_res_resource_set_t *rset);
+
+void delete_resource_set(mrp_res_resource_set_t *rs);
+
+mrp_res_resource_set_t *resource_set_copy(
+ const mrp_res_resource_set_t *original);
+
+mrp_res_resource_t *get_resource_by_name(mrp_res_resource_set_t *rset,
+ const char *name);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "string_array.h"
+#include <errno.h>
+
+
+mrp_res_string_array_t *mrp_str_array_dup(uint32_t dim, const char **arr)
+{
+ uint32_t i;
+ mrp_res_string_array_t *dup;
+
+ if (dim >= ARRAY_MAX || !arr)
+ return NULL;
+
+ if (!dim && arr) {
+ for (dim = 0; arr[dim]; dim++)
+ ;
+ }
+
+ if (!(dup = mrp_allocz(sizeof(mrp_res_string_array_t)))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ dup->num_strings = dim;
+ dup->strings = mrp_allocz_array(const char *, dim);
+
+ if (!dup->strings) {
+ mrp_free(dup);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ for (i = 0; i < dim; i++) {
+ if (arr[i]) {
+ if (!(dup->strings[i] = mrp_strdup(arr[i]))) {
+ for (; i > 0; i--) {
+ mrp_free((void *)dup->strings[i-1]);
+ }
+ mrp_free(dup->strings);
+ mrp_free(dup);
+ errno = ENOMEM;
+ return NULL;
+ }
+ }
+ }
+
+ return dup;
+}
+
+/* public API */
+
+void mrp_res_free_string_array(mrp_res_string_array_t *arr)
+{
+ int i;
+
+ if (!arr)
+ return;
+
+ for (i = 0; i < arr->num_strings; i++)
+ mrp_free((void *) arr->strings[i]);
+
+ mrp_free(arr->strings);
+ mrp_free(arr);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_API_STRING_ARRAY_H__
+#define __MURPHY_RESOURCE_API_STRING_ARRAY_H__
+
+#include "resource-api.h"
+#include "resource-private.h"
+
+#define ARRAY_MAX 1024
+
+mrp_res_string_array_t *mrp_str_array_dup(uint32_t dim, const char **arr);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/common/msg.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/debug.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/console.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#include <murphy-db/mql.h>
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/config-api.h>
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/protocol.h>
+
+#include <murphy/resource/resource-set.h>
+
+#define ATTRIBUTE_MAX MRP_ATTRIBUTE_MAX
+
+
+
+enum {
+ RESOURCE_ERROR = -1,
+ ATTRIBUTE_ERROR = -1,
+ RESOURCE_OK = 0,
+ ATTRIBUTE_OK = 0,
+ ATTRIBUTE_LAST,
+ RESOURCE_LAST,
+};
+
+
+enum {
+ ARG_ADDRESS,
+};
+
+
+typedef struct {
+ mrp_plugin_t *plugin;
+ mrp_event_bus_t *plugin_bus;
+ mrp_event_watch_t *w;
+ mrp_sockaddr_t saddr;
+ socklen_t alen;
+ const char *atyp;
+ mrp_transport_t *listen;
+ mrp_list_hook_t clients;
+} resource_data_t;
+
+typedef struct {
+ mrp_list_hook_t list;
+ resource_data_t *data;
+ uint32_t id;
+ mrp_resource_client_t *rscli;
+ mrp_transport_t *transp;
+} client_t;
+
+
+static void print_zones_cb(mrp_console_t *, void *, int, char **argv);
+static void print_classes_cb(mrp_console_t *, void *, int, char **argv);
+static void print_sets_cb(mrp_console_t *, void *, int, char **argv);
+static void print_owners_cb(mrp_console_t *, void *, int, char **argv);
+static void print_resources_cb(mrp_console_t *, void *, int, char **argv);
+
+static void resource_event_handler(uint32_t, mrp_resource_set_t *, void *);
+
+
+MRP_CONSOLE_GROUP(resource_group, "resource", NULL, NULL, {
+ MRP_TOKENIZED_CMD("zones" , print_zones_cb, FALSE,
+ "zones", "prints zones",
+ "prints the available zones. The data sources "
+ "for the printout are the internal data structures "
+ "of the resource library."),
+ MRP_TOKENIZED_CMD("classes" , print_classes_cb, FALSE,
+ "classes", "prints application classes",
+ "prints the available application classes. The "
+ "data sources for the printout are the internal "
+ "data structures of the resource library."),
+ MRP_TOKENIZED_CMD("sets", print_sets_cb, FALSE,
+ "sets", "prints resource sets",
+ "prints the current resource sets for each "
+ "application class. The data sources for the "
+ "printout are the internal data structures of the "
+ "resource library"),
+ MRP_TOKENIZED_CMD("owners" , print_owners_cb , FALSE,
+ "owners", "prints resource owners",
+ "prints for each zone the owner application class "
+ "of each resource. The data sources for the "
+ "printout are the internal data structures of the "
+ "resource library"),
+ MRP_TOKENIZED_CMD("resources" , print_resources_cb , FALSE,
+ "resources", "prints resources",
+ "prints all resource definitions and along with "
+ "all their attributes. The data sources for the "
+ "printout are the internal data structures of the "
+ "resource library"),
+
+});
+
+
+static void print_zones_cb(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ const char **zone_names;
+ int i;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ printf("Zones:\n");
+
+ if ((zone_names = mrp_zone_get_all_names(0, NULL))) {
+
+ for (i = 0; zone_names[i]; i++)
+ printf(" %s\n", zone_names[i]);
+
+
+ mrp_free(zone_names);
+ }
+}
+
+
+static void print_classes_cb(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ char buf[8192];
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ mrp_application_class_print(buf, sizeof(buf), false);
+
+ printf("%s", buf);
+}
+
+
+static void print_sets_cb(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ static int size = 8192;
+ char buf[size];
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ if (mrp_application_class_print(buf, sizeof(buf), true) >= size)
+ size *= 2;
+
+ printf("%s", buf);
+}
+
+
+static void print_owners_cb(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ char buf[2048];
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ mrp_resource_owner_print(buf, sizeof(buf));
+
+ printf("%s", buf);
+}
+
+
+static void print_resources_cb(mrp_console_t *c, void *user_data,
+ int argc, char **argv)
+{
+ const char **names;
+ mrp_attr_t *attrs, *a;
+ mrp_attr_t buf[ATTRIBUTE_MAX];
+ uint32_t resid;
+
+ MRP_UNUSED(c);
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ if (!(names = mrp_resource_definition_get_all_names(0, NULL))) {
+ printf("Failed to read resource definitions.\n");
+ return;
+ }
+
+ printf("Resource definitions:\n");
+ for (resid = 0; names[resid]; resid++) {
+ attrs = mrp_resource_definition_read_all_attributes(resid,
+ ATTRIBUTE_MAX, buf);
+ printf(" Resource '%s'\n", names[resid]);
+ for (a = attrs; a->name; a++) {
+ printf(" attribute %s: ", a->name);
+ switch (a->type) {
+ case mqi_string:
+ printf("'%s'\n", a->value.string);
+ break;
+ case mqi_integer:
+ printf("%d\n", a->value.integer);
+ break;
+ case mqi_unsignd:
+ printf("%u\n", a->value.unsignd);
+ break;
+ case mqi_floating:
+ printf("%f\n", a->value.floating);
+ break;
+ default:
+ printf("<unsupported type>\n");
+ break;
+ }
+ }
+ }
+
+ mrp_free(names);
+}
+
+
+#if 0
+static int set_default_configuration(void)
+{
+ typedef struct {
+ const char *name;
+ bool share;
+ mrp_attr_def_t *attrs;
+ } resdef_t;
+
+ static const char *zones[] = {
+ "driver",
+ "front-passenger",
+ "rear-left-passenger",
+ "rear-right-passenger",
+ NULL
+ };
+
+ static const char *classes[] = {
+ "implicit",
+ "player",
+ "game",
+ "phone",
+ "navigator",
+ NULL
+ };
+
+ static mrp_attr_def_t audio_attrs[] = {
+ { "role", MRP_RESOURCE_RW, mqi_string , .value.string="music" },
+ { NULL , 0 , mqi_unknown, .value.string=NULL }
+ };
+
+ static resdef_t resources[] = {
+ { "audio_playback" , true , audio_attrs },
+ { "audio_recording", true , NULL },
+ { "video_playback" , false, NULL },
+ { "video_recording", false, NULL },
+ { NULL , false, NULL }
+ };
+
+ const char *name;
+ resdef_t *rdef;
+ uint32_t i;
+
+ mrp_zone_definition_create(NULL);
+
+ for (i = 0; (name = zones[i]); i++)
+ mrp_zone_create(name, NULL);
+
+ for (i = 0; (name = classes[i]); i++)
+ mrp_application_class_create(name, i);
+
+ for (i = 0; (rdef = resources + i)->name; i++) {
+ mrp_resource_definition_create(rdef->name, rdef->share, rdef->attrs,
+ NULL, NULL);
+ }
+
+ return 0;
+}
+#endif
+
+static void reply_with_array(client_t *client, mrp_msg_t *msg,
+ uint16_t tag, const char **arr)
+{
+ resource_data_t *data = client->data;
+ mrp_plugin_t *plugin = data->plugin;
+ uint16_t dim;
+ bool s;
+
+ for (dim = 0; arr[dim]; dim++)
+ ;
+
+ s = mrp_msg_append(msg, MRP_MSG_TAG_SINT16(RESPROTO_REQUEST_STATUS, 0));
+ s &= mrp_msg_append(msg, MRP_MSG_TAG_STRING_ARRAY(tag, dim, arr));
+
+ if (!s) {
+ mrp_log_error("%s: failed to build reply", plugin->instance);
+ return;
+ }
+
+ if (!mrp_transport_send(client->transp, msg))
+ mrp_log_error("%s: failed to send reply", plugin->instance);
+}
+
+static void reply_with_status(client_t *client, mrp_msg_t *msg, int16_t err)
+{
+ if (!mrp_msg_append(msg,MRP_MSG_TAG_SINT16(RESPROTO_REQUEST_STATUS,err)) ||
+ !mrp_transport_send(client->transp, msg))
+ {
+ resource_data_t *data = client->data;
+ mrp_plugin_t *plugin = data->plugin;
+
+ mrp_log_error("%s: failed to create or send reply", plugin->instance);
+ }
+}
+
+
+static bool write_attributes(mrp_msg_t *msg, mrp_attr_t *attrs)
+{
+#define PUSH(m, tag, typ, val) \
+ mrp_msg_append(m, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
+
+ mrp_attr_t *a;
+ bool ok;
+
+ if (attrs) {
+ for (a = attrs; a->name; a++) {
+ if (!PUSH(msg, ATTRIBUTE_NAME, STRING, a->name))
+ return false;;
+
+ switch (a->type) {
+ case mqi_string:
+ ok = PUSH(msg, ATTRIBUTE_VALUE, STRING, a->value.string);
+ break;
+ case mqi_integer:
+ ok = PUSH(msg, ATTRIBUTE_VALUE, SINT32, a->value.integer);
+ break;
+ case mqi_unsignd:
+ ok = PUSH(msg, ATTRIBUTE_VALUE, UINT32, a->value.unsignd);
+ break;
+ case mqi_floating:
+ ok = PUSH(msg, ATTRIBUTE_VALUE, DOUBLE, a->value.floating);
+ break;
+ default:
+ ok = false;
+ break;
+ }
+
+ if (!ok)
+ return false;
+ }
+ }
+
+ if (!PUSH(msg, SECTION_END, UINT8, 0))
+ return false;
+
+ return true;
+
+#undef PUSH
+}
+
+
+static void query_resources_request(client_t *client, mrp_msg_t *req)
+{
+#define PUSH(m, tag, typ, val) \
+ mrp_msg_append(m, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
+
+
+ resource_data_t *data = client->data;
+ mrp_plugin_t *plugin = data->plugin;
+ const char **names;
+ mrp_attr_t *attrs;
+ mrp_attr_t buf[ATTRIBUTE_MAX];
+ uint32_t resid;
+
+ if (!(names = mrp_resource_definition_get_all_names(0, NULL)))
+ reply_with_status(client, req, ENOMEM);
+ else {
+ if (!PUSH(req, REQUEST_STATUS, SINT16, 0))
+ goto failed;
+ else {
+ for (resid = 0; names[resid]; resid++) {
+ attrs = mrp_resource_definition_read_all_attributes(
+ resid, ATTRIBUTE_MAX, buf);
+
+ if (!PUSH(req, RESOURCE_NAME, STRING, names[resid]) ||
+ !write_attributes(req, attrs))
+ goto failed;
+ }
+
+ if (!mrp_transport_send(client->transp, req))
+ mrp_log_error("%s: failed to send reply", plugin->instance);
+
+ mrp_free(names);
+ }
+ }
+
+ return;
+
+ failed:
+ mrp_log_error("%s: can't build recource query reply message",
+ plugin->instance);
+ mrp_free(names);
+
+
+#undef PUSH
+}
+
+static void query_classes_request(client_t *client, mrp_msg_t *req)
+{
+ const char **names = mrp_application_class_get_all_names(0, NULL);
+
+ if (!names)
+ reply_with_status(client, req, ENOMEM);
+ else {
+ reply_with_array(client, req, RESPROTO_CLASS_NAME, names);
+ mrp_free(names);
+ }
+}
+
+static void query_zones_request(client_t *client, mrp_msg_t *req)
+{
+ const char **names = mrp_zone_get_all_names(0, NULL);
+
+ if (!names)
+ reply_with_status(client, req, ENOMEM);
+ else {
+ reply_with_array(client, req, RESPROTO_ZONE_NAME, names);
+ mrp_free(names);
+ }
+}
+
+static int read_attribute(mrp_msg_t *req, mrp_attr_t *attr, void **pcurs)
+{
+ uint16_t tag;
+ uint16_t type;
+ size_t size;
+ mrp_msg_value_t value;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size))
+ return ATTRIBUTE_ERROR;
+
+ if (tag == RESPROTO_SECTION_END)
+ return ATTRIBUTE_LAST;
+
+ if (tag != RESPROTO_ATTRIBUTE_NAME || type != MRP_MSG_FIELD_STRING)
+ return ATTRIBUTE_ERROR;
+
+ attr->name = value.str;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_ATTRIBUTE_VALUE)
+ return ATTRIBUTE_ERROR;
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ attr->type = mqi_string;
+ attr->value.string = value.str;
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ attr->type = mqi_integer;
+ attr->value.integer = value.s32;
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ attr->type = mqi_unsignd;
+ attr->value.unsignd = value.u32;
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ attr->type = mqi_floating;
+ attr->value.floating = value.dbl;
+ break;
+ default:
+ return ATTRIBUTE_ERROR;
+ }
+
+ {
+ char str[256];
+
+ switch (attr->type) {
+ case mqi_string:
+ snprintf(str, sizeof(str), "'%s'", attr->value.string);
+ break;
+ case mqi_integer:
+ snprintf(str, sizeof(str), "%d", attr->value.integer);
+ break;
+ case mqi_unsignd:
+ snprintf(str, sizeof(str), "%u", attr->value.unsignd);
+ break;
+ case mqi_floating:
+ snprintf(str, sizeof(str), "%.2lf", attr->value.floating);
+ break;
+ default:
+ snprintf(str, sizeof(str), "< ??? >");
+ break;
+ }
+
+ mrp_log_info(" attribute %s:%s", attr->name, str);
+ }
+
+ return ATTRIBUTE_OK;
+}
+
+
+static int read_resource(mrp_resource_set_t *rset, mrp_msg_t *req,void **pcurs)
+{
+ uint16_t tag;
+ uint16_t type;
+ size_t size;
+ mrp_msg_value_t value;
+ const char *name;
+ bool mand;
+ bool shared;
+ mrp_attr_t attrs[ATTRIBUTE_MAX + 1];
+ uint32_t i;
+ int arst;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size))
+ return RESOURCE_LAST;
+
+ if (tag != RESPROTO_RESOURCE_NAME || type != MRP_MSG_FIELD_STRING)
+ return RESOURCE_ERROR;
+
+ name = value.str;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_FLAGS || type != MRP_MSG_FIELD_UINT32)
+ return RESOURCE_ERROR;
+
+ mand = (value.u32 & RESPROTO_RESFLAG_MANDATORY) ? true : false;
+ shared = (value.u32 & RESPROTO_RESFLAG_SHARED) ? true : false;
+
+ mrp_log_info(" resource: name:'%s' %s %s", name,
+ mand?"mandatory":"optional ", shared?"shared":"exclusive");
+
+ for (i = 0, arst = 0; i < ATTRIBUTE_MAX; i++) {
+ if ((arst = read_attribute(req, attrs + i, pcurs)))
+ break;
+ }
+
+ memset(attrs + i, 0, sizeof(mrp_attr_t));
+
+ if (arst > 0) {
+ if (mrp_resource_set_add_resource(rset, name, shared, attrs, mand) < 0)
+ arst = RESOURCE_ERROR;
+ else
+ arst = 0;
+ }
+
+ return arst;
+}
+
+
+static void create_resource_set_request(client_t *client, mrp_msg_t *req,
+ uint32_t seqno, void **pcurs)
+{
+ static uint16_t reqtyp = RESPROTO_CREATE_RESOURCE_SET;
+
+ resource_data_t *data = client->data;
+ mrp_plugin_t *plugin = data->plugin;
+ mrp_resource_set_t *rset = 0;
+ mrp_msg_t *rpl;
+ uint32_t flags;
+ uint32_t priority;
+ const char *class;
+ const char *zone;
+ uint16_t tag;
+ uint16_t type;
+ size_t size;
+ mrp_msg_value_t value;
+ uint32_t rsid;
+ int arst;
+ int32_t status;
+ bool auto_release;
+ bool auto_acquire;
+ bool dont_wait;
+ mrp_resource_event_cb_t event_cb;
+
+ MRP_ASSERT(client, "invalid argument");
+ MRP_ASSERT(client->rscli, "confused with data structures");
+
+ rsid = MRP_RESOURCE_ID_INVALID;
+ status = EINVAL;
+
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_FLAGS || type != MRP_MSG_FIELD_UINT32)
+ goto reply;
+
+ flags = value.u32;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_PRIORITY || type != MRP_MSG_FIELD_UINT32)
+ goto reply;
+
+ priority = value.u32;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_CLASS_NAME || type != MRP_MSG_FIELD_STRING)
+ goto reply;
+
+ class = value.str;
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_ZONE_NAME || type != MRP_MSG_FIELD_STRING)
+ goto reply;
+
+ zone = value.str;
+
+ mrp_log_info("resource-set flags:%u priority:%u class:'%s' zone:'%s'",
+ flags, priority, class, zone);
+
+ auto_release = (flags & RESPROTO_RSETFLAG_AUTORELEASE);
+ auto_acquire = (flags & RESPROTO_RSETFLAG_AUTOACQUIRE);
+ dont_wait = (flags & RESPROTO_RSETFLAG_DONTWAIT);
+
+ if (flags & RESPROTO_RSETFLAG_NOEVENTS)
+ event_cb = NULL;
+ else
+ event_cb = resource_event_handler;
+
+ rset = mrp_resource_set_create(client->rscli, auto_release, dont_wait,
+ priority, event_cb, client);
+ if (!rset)
+ goto reply;
+
+ rsid = mrp_get_resource_set_id(rset);
+
+ while ((arst = read_resource(rset, req, pcurs)) == 0)
+ ;
+
+ if (arst > 0) {
+ if (auto_acquire)
+ mrp_resource_set_acquire(rset,seqno);
+ if (mrp_application_class_add_resource_set(class,zone,rset,seqno) == 0)
+ status = 0;
+ }
+
+ reply:
+ rpl = mrp_msg_create(MRP_MSG_TAG_UINT32( RESPROTO_SEQUENCE_NO , seqno ),
+ MRP_MSG_TAG_UINT16( RESPROTO_REQUEST_TYPE , reqtyp),
+ MRP_MSG_TAG_SINT16( RESPROTO_REQUEST_STATUS , status),
+ MRP_MSG_TAG_UINT32( RESPROTO_RESOURCE_SET_ID, rsid ),
+ RESPROTO_MESSAGE_END );
+ if (!rpl || !mrp_transport_send(client->transp, rpl)) {
+ mrp_log_error("%s: failed to send reply", plugin->instance);
+ return;
+ }
+
+ mrp_msg_unref(rpl);
+
+ if (status != 0)
+ mrp_resource_set_destroy(rset);
+}
+
+static void destroy_resource_set_request(client_t *client, mrp_msg_t *req,
+ void **pcurs)
+{
+ uint16_t tag;
+ uint16_t type;
+ size_t size;
+ mrp_msg_value_t value;
+ uint32_t rset_id;
+ mrp_resource_set_t *rset;
+
+ MRP_ASSERT(client, "invalid argument");
+ MRP_ASSERT(client->rscli, "confused with data structures");
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
+ {
+ reply_with_status(client, req, EINVAL);
+ return;
+ }
+
+ rset_id = value.u32;
+
+ if (!(rset = mrp_resource_client_find_set(client->rscli, rset_id))) {
+ reply_with_status(client, req, ENOENT);
+ return;
+ }
+
+ reply_with_status(client, req, 0);
+
+ mrp_resource_set_destroy(rset);
+}
+
+
+static void acquire_resource_set_request(client_t *client, mrp_msg_t *req,
+ uint32_t seqno, bool acquire,
+ void **pcurs)
+{
+ uint16_t tag;
+ uint16_t type;
+ size_t size;
+ mrp_msg_value_t value;
+ uint32_t rset_id;
+ mrp_resource_set_t *rset;
+
+ MRP_ASSERT(client, "invalid argument");
+ MRP_ASSERT(client->rscli, "confused with data structures");
+
+ if (!mrp_msg_iterate(req, pcurs, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
+ {
+ reply_with_status(client, req, EINVAL);
+ return;
+ }
+
+ rset_id = value.u32;
+
+ if (!(rset = mrp_resource_client_find_set(client->rscli, rset_id))) {
+ reply_with_status(client, req, ENOENT);
+ return;
+ }
+
+ reply_with_status(client, req, 0);
+
+ if (acquire)
+ mrp_resource_set_acquire(rset, seqno);
+ else
+ mrp_resource_set_release(rset, seqno);
+}
+
+static void connection_evt(mrp_transport_t *listen, void *user_data)
+{
+ static uint32_t id;
+
+ resource_data_t *data = (resource_data_t *)user_data;
+ mrp_plugin_t *plugin = data->plugin;
+ int flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_NONBLOCK;
+ client_t *client = mrp_allocz(sizeof(client_t));
+ char name[256];
+
+ if (!client) {
+ mrp_log_error("%s: Memory alloc error. Can't accept new connection",
+ plugin->instance);
+ return;
+ }
+
+ client->data = data;
+
+ snprintf(name, sizeof(name), "client%u", (client->id = ++id));
+ client->rscli = mrp_resource_client_create(name, client);
+
+ if (!(client->transp = mrp_transport_accept(listen, client, flags))) {
+ mrp_log_error("%s: failed to accept new connection", plugin->instance);
+ mrp_resource_client_destroy(client->rscli);
+ mrp_free(client);
+ return;
+ }
+
+ mrp_list_append(&data->clients, &client->list);
+
+ mrp_log_info("%s: %s connected", plugin->instance, name);
+}
+
+static void closed_evt(mrp_transport_t *transp, int error, void *user_data)
+{
+ client_t *client = (client_t *)user_data;
+ resource_data_t *data = client->data;
+ mrp_plugin_t *plugin = data->plugin;
+
+ MRP_UNUSED(transp);
+
+ if (error)
+ mrp_log_error("%s: connection error %d (%s)",
+ plugin->instance, error, strerror(error));
+ else
+ mrp_log_info("%s: peer closed connection", plugin->instance);
+
+ mrp_resource_client_destroy(client->rscli);
+
+ mrp_list_delete(&client->list);
+ mrp_free(client);
+
+ mrp_transport_disconnect(transp);
+ mrp_transport_destroy(transp);
+}
+
+
+
+static void recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data)
+{
+ client_t *client = (client_t *)user_data;
+ resource_data_t *data = client->data;
+ mrp_plugin_t *plugin = data->plugin;
+ void *cursor = NULL;
+ uint32_t seqno;
+ mrp_resproto_request_t reqtyp;
+ uint16_t tag;
+ uint16_t type;
+ size_t size;
+ mrp_msg_value_t value;
+
+
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ MRP_ASSERT(client->transp == transp, "confused with data structures");
+
+ mrp_log_info("%s: received a message", plugin->instance);
+ mrp_msg_dump(msg, stdout);
+
+
+ if (mrp_msg_iterate(msg, &cursor, &tag, &type, &value, &size) &&
+ tag == RESPROTO_SEQUENCE_NO && type == MRP_MSG_FIELD_UINT32)
+ seqno = value.u32;
+ else {
+ mrp_log_warning("%s: malformed message. Bad or missing "
+ "sequence number", plugin->instance);
+ return;
+ }
+
+ if (mrp_msg_iterate(msg, &cursor, &tag, &type, &value, &size) &&
+ tag == RESPROTO_REQUEST_TYPE && type == MRP_MSG_FIELD_UINT16)
+ reqtyp = value.u16;
+ else {
+ mrp_log_warning("%s: malformed message. Bad or missing "
+ "request type", plugin->instance);
+ return;
+ }
+
+ switch (reqtyp) {
+
+ case RESPROTO_QUERY_RESOURCES:
+ query_resources_request(client, msg);
+ break;
+
+ case RESPROTO_QUERY_CLASSES:
+ query_classes_request(client, msg);
+ break;
+
+ case RESPROTO_QUERY_ZONES:
+ query_zones_request(client, msg);
+ break;
+
+ case RESPROTO_CREATE_RESOURCE_SET:
+ create_resource_set_request(client, msg, seqno, &cursor);
+ break;
+
+ case RESPROTO_DESTROY_RESOURCE_SET:
+ destroy_resource_set_request(client, msg, &cursor);
+ break;
+
+ case RESPROTO_ACQUIRE_RESOURCE_SET:
+ acquire_resource_set_request(client, msg, seqno, true, &cursor);
+ break;
+
+ case RESPROTO_RELEASE_RESOURCE_SET:
+ acquire_resource_set_request(client, msg, seqno, false, &cursor);
+ break;
+
+ default:
+ mrp_log_warning("%s: unsupported request type %d",
+ plugin->instance, reqtyp);
+ break;
+ }
+}
+
+static void recv_msg(mrp_transport_t *transp, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(transp, msg, NULL, 0, user_data);
+}
+
+
+static void resource_event_handler(uint32_t reqid, mrp_resource_set_t *rset,
+ void *userdata)
+{
+#define FIELD(tag, typ, val) \
+ RESPROTO_##tag, MRP_MSG_FIELD_##typ, val
+#define PUSH(m, tag, typ, val) \
+ mrp_msg_append(m, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
+
+ client_t *client = (client_t *)userdata;
+ resource_data_t *data;
+ mrp_plugin_t *plugin;
+ uint16_t reqtyp;
+ uint16_t state;
+ mrp_resource_mask_t grant;
+ mrp_resource_mask_t advice;
+ mrp_resource_mask_t mask;
+ mrp_resource_mask_t all;
+ mrp_msg_t *msg;
+ mrp_resource_t *res;
+ uint32_t id;
+ const char *name;
+ void *curs;
+ mrp_attr_t attrs[ATTRIBUTE_MAX + 1];
+
+ MRP_ASSERT(rset && client, "invalid argument");
+
+ data = client->data;
+ plugin = data->plugin;
+
+ reqtyp = RESPROTO_RESOURCES_EVENT;
+ id = mrp_get_resource_set_id(rset);
+ grant = mrp_get_resource_set_grant(rset);
+ advice = mrp_get_resource_set_advice(rset);
+
+ if (mrp_get_resource_set_state(rset) == mrp_resource_acquire)
+ state = RESPROTO_ACQUIRE;
+ else
+ state = RESPROTO_RELEASE;
+
+ msg = mrp_msg_create(FIELD( SEQUENCE_NO , UINT32, reqid ),
+ FIELD( REQUEST_TYPE , UINT16, reqtyp ),
+ FIELD( RESOURCE_SET_ID, UINT32, id ),
+ FIELD( RESOURCE_STATE , UINT16, state ),
+ FIELD( RESOURCE_GRANT , UINT32, grant ),
+ FIELD( RESOURCE_ADVICE, UINT32, advice ),
+ RESPROTO_MESSAGE_END );
+
+ if (!msg)
+ goto failed;
+
+ all = grant | advice;
+ curs = NULL;
+
+ while ((res = mrp_resource_set_iterate_resources(rset, &curs))) {
+ mask = mrp_resource_get_mask(res);
+
+ if (!(all & mask))
+ continue;
+
+ id = mrp_resource_get_id(res);
+ name = mrp_resource_get_name(res);
+
+ if (!PUSH(msg, RESOURCE_ID , UINT32, id ) ||
+ !PUSH(msg, RESOURCE_NAME, STRING, name) )
+ goto failed;
+
+ if (!mrp_resource_read_all_attributes(res, ATTRIBUTE_MAX + 1, attrs))
+ goto failed;
+
+ if (!write_attributes(msg, attrs))
+ goto failed;
+ }
+
+ if (!mrp_transport_send(client->transp, msg))
+ goto failed;
+
+ mrp_msg_unref(msg);
+
+ return;
+
+ failed:
+ mrp_log_error("%s: failed to build/send message for resource event",
+ plugin->instance);
+ mrp_msg_unref(msg);
+
+#undef PUSH
+#undef FIELD
+}
+
+
+
+static int initiate_transport(mrp_plugin_t *plugin)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = recv_msg },
+ { .recvmsgfrom = recvfrom_msg },
+ .closed = NULL,
+ .connection = NULL
+ };
+
+ mrp_context_t *ctx = plugin->ctx;
+ mrp_plugin_arg_t *args = plugin->args;
+ resource_data_t *data = (resource_data_t *)plugin->data;
+ const char *addr = args[ARG_ADDRESS].str;
+ int flags = MRP_TRANSPORT_REUSEADDR;
+ bool stream;
+
+ if (addr == NULL)
+ addr = mrp_resource_get_default_address();
+
+ data->alen = mrp_transport_resolve(NULL, addr, &data->saddr,
+ sizeof(data->saddr), &data->atyp);
+
+ if (data->alen <= 0) {
+ mrp_log_error("%s: failed to resolve transport arddress '%s'",
+ plugin->instance, addr);
+ return -1;
+ }
+
+
+ if (strncmp(addr, "tcp", 3) && strncmp(addr, "unxs", 4))
+ stream = false;
+ else {
+ stream = true;
+ evt.connection = connection_evt;
+ evt.closed = closed_evt;
+ }
+
+ data->listen = mrp_transport_create(ctx->ml, data->atyp, &evt, data,flags);
+
+ if (!data->listen) {
+ mrp_log_error("%s: can't create listening transport",plugin->instance);
+ return -1;
+ }
+
+ if (!mrp_transport_bind(data->listen, &data->saddr, data->alen)) {
+ mrp_log_error("%s: can't bind to address %s", plugin->instance, addr);
+ return -1;
+ }
+
+ if (stream && !mrp_transport_listen(data->listen, 0)) {
+ mrp_log_error("%s: can't listen for connections", plugin->instance);
+ return -1;
+ }
+
+ mrp_log_info("%s: listening for connections on %s", plugin->instance,addr);
+
+ return 0;
+}
+
+
+static void initiate_lua_configuration(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ mrp_resource_configuration_init();
+}
+
+static void event_cb(mrp_event_watch_t *w, uint32_t id, int format,
+ void *event_data, void *user_data)
+{
+ mrp_plugin_t *plugin = (mrp_plugin_t *)user_data;
+#if 0
+ mrp_plugin_arg_t *args = plugin->args;
+#endif
+ resource_data_t *data = (resource_data_t *)plugin->data;
+ const char *event = mrp_event_name(id);
+ uint16_t tag_inst = MRP_PLUGIN_TAG_INSTANCE;
+ uint16_t tag_name = MRP_PLUGIN_TAG_PLUGIN;
+ const char *inst;
+ const char *name;
+ int success;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(format);
+
+ mrp_log_info("%s: got event 0x%x (%s):", plugin->instance, id, event);
+
+ if (data && event) {
+ if (!strcmp(event, MRP_PLUGIN_EVENT_STARTED)) {
+ success = mrp_msg_get(event_data,
+ MRP_MSG_TAG_STRING(tag_inst, &inst),
+ MRP_MSG_TAG_STRING(tag_name, &name),
+ MRP_MSG_END);
+ if (success) {
+ if (!strcmp(inst, plugin->instance)) {
+#if 0
+ set_default_configuration();
+ mrp_log_info("%s: built-in default configuration "
+ "is in use", plugin->instance);
+#endif
+
+ initiate_lua_configuration(plugin);
+ initiate_transport(plugin);
+ }
+ }
+ } /* if PLUGIN_STARTED */
+ }
+}
+
+
+static int subscribe_events(mrp_plugin_t *plugin)
+{
+ resource_data_t *data = (resource_data_t *)plugin->data;
+ mrp_mainloop_t *ml = plugin->ctx->ml;
+ mrp_event_bus_t *bus = mrp_event_bus_get(ml, MRP_PLUGIN_BUS);
+ mrp_event_mask_t events;
+
+ if (bus == NULL)
+ return FALSE;
+
+ data->plugin_bus = bus;
+
+ mrp_mask_init(&events);
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_LOADED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_STARTED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_FAILED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_STOPPING));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_STOPPED));
+ mrp_mask_set(&events, mrp_event_id(MRP_PLUGIN_EVENT_UNLOADED));
+
+ data->w = mrp_event_add_watch_mask(bus, &events, event_cb, plugin);
+
+ return (data->w != NULL);
+}
+
+
+static void unsubscribe_events(mrp_plugin_t *plugin)
+{
+ resource_data_t *data = (resource_data_t *)plugin->data;
+
+ if (data->w) {
+ mrp_event_del_watch(data->w);
+ data->w = NULL;
+ }
+}
+
+
+static void register_events(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ /* register the events that are sent on the resource state changes */
+
+ mrp_event_register(MURPHY_RESOURCE_EVENT_CREATED);
+ mrp_event_register(MURPHY_RESOURCE_EVENT_ACQUIRE);
+ mrp_event_register(MURPHY_RESOURCE_EVENT_RELEASE);
+ mrp_event_register(MURPHY_RESOURCE_EVENT_DESTROYED);
+}
+
+
+static int resource_init(mrp_plugin_t *plugin)
+{
+#if 0
+ mrp_plugin_arg_t *args = plugin->args;
+#endif
+ resource_data_t *data;
+
+ mrp_log_info("%s() called for resource instance '%s'...", __FUNCTION__,
+ plugin->instance);
+
+ if (!(data = mrp_allocz(sizeof(*data)))) {
+ mrp_log_error("Failed to allocate private data for resource plugin "
+ "instance %s.", plugin->instance);
+ return FALSE;
+ }
+
+ data->plugin = plugin;
+ mrp_list_init(&data->clients);
+
+ plugin->data = data;
+
+ register_events(plugin);
+ subscribe_events(plugin);
+ initiate_lua_configuration(plugin);
+
+ return TRUE;
+}
+
+
+static void resource_exit(mrp_plugin_t *plugin)
+{
+ mrp_log_info("%s() called for test instance '%s'...", __FUNCTION__,
+ plugin->instance);
+
+ unsubscribe_events(plugin);
+}
+
+
+#define RESOURCE_DESCRIPTION "Plugin to implement resource message protocol"
+#define RESOURCE_HELP "Maybe later ..."
+#define RESOURCE_VERSION MRP_VERSION_INT(0, 0, 1)
+#define RESOURCE_AUTHORS "Janos Kovacs <jankovac503@gmail.com>"
+
+#define DEF_CONFIG_FILE "/etc/murphy/resource.conf"
+#define DEF_ADDRESS NULL
+
+static mrp_plugin_arg_t args[] = {
+ MRP_PLUGIN_ARGIDX( ARG_ADDRESS, STRING, "address", DEF_ADDRESS ),
+};
+
+
+MURPHY_REGISTER_PLUGIN("resource",
+ RESOURCE_VERSION,
+ RESOURCE_DESCRIPTION,
+ RESOURCE_AUTHORS,
+ RESOURCE_HELP,
+ MRP_SINGLETON,
+ resource_init,
+ resource_exit,
+ args, MRP_ARRAY_SIZE(args),
+#if 0
+ exports, MRP_ARRAY_SIZE(exports),
+ imports, MRP_ARRAY_SIZE(imports),
+#else
+ NULL, 0,
+ NULL, 0,
+#endif
+ &resource_group);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <signal.h>
+#include <ctype.h>
+#include <libgen.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+#include <murphy/resource/protocol.h>
+
+#define ARRAY_MAX 1024
+#define RESOURCE_MAX 32
+#define ATTRIBUTE_MAX 32
+
+#define INVALID_ID (~(uint32_t)0)
+#define INVALID_INDEX (~(uint32_t)0)
+#define INVALID_SEQNO (~(uint32_t)0)
+#define INVALID_REQUEST (~(uint16_t)0)
+
+#define GRANT 0
+#define ADVICE 1
+
+
+typedef struct {
+ uint32_t dim;
+ const char *elems[0];
+} string_array_t;
+
+typedef struct {
+ const char *name;
+ char type; /* s:char *, i:int32_t, u:uint32_t, f:double */
+ union {
+ const char *string;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+ } v;
+} attribute_t;
+
+typedef struct {
+ uint32_t dim;
+ attribute_t elems[0];
+} attribute_array_t;
+
+typedef struct {
+ const char *name;
+ attribute_array_t *attrs;
+} resource_def_t;
+
+typedef struct {
+ uint32_t dim;
+ resource_def_t defs[0];
+} resource_def_array_t;
+
+typedef struct {
+ const char *name;
+ mrp_mainloop_t *ml;
+ mrp_transport_t *transp;
+ mrp_sockaddr_t saddr;
+ socklen_t alen;
+ const char *atype;
+ uint32_t seqno;
+ bool prompt;
+ bool msgdump;
+ char * class;
+ char * zone;
+ char * rsetd;
+ uint32_t rsetf;
+ uint32_t priority;
+ resource_def_array_t *resources;
+ string_array_t *class_names;
+ string_array_t *zone_names;
+ uint32_t rset_id;
+} client_t;
+
+typedef struct {
+ uint32_t seqno;
+ uint64_t time;
+} reqstamp_t;
+
+#define HASH_BITS 8
+#define HASH_MAX (((uint32_t)1) << HASH_BITS)
+#define HASH_MASK (HASH_MAX - 1)
+#define HASH_FUNC(s) ((uint32_t)(s) & HASH_MASK)
+
+static reqstamp_t reqstamps[HASH_MAX];
+static uint64_t totaltime;
+static uint32_t reqcount;
+
+static void print_prompt(client_t *, bool);
+
+
+static uint64_t reqstamp_current_time(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, NULL) < 0)
+ return 0ULL;
+
+ return ((uint64_t)tv.tv_sec * 1000000ULL) + (uint64_t)tv.tv_usec;
+}
+
+static void reqstamp_start(uint32_t seqno)
+{
+ reqstamp_t *rs = reqstamps + HASH_FUNC(seqno);
+ uint64_t now = reqstamp_current_time();
+
+ if (!rs->seqno && !rs->time && now) {
+ rs->seqno = seqno;
+ rs->time = now;
+ }
+}
+
+static void reqstamp_intermediate(uint32_t seqno)
+{
+ reqstamp_t *rs = reqstamps + HASH_FUNC(seqno);
+ uint64_t now = reqstamp_current_time();
+
+ if (rs->seqno == seqno && rs->time && now > rs->time) {
+ printf("request %u was responded in %.2lf msec\n",
+ seqno, (double)(now - rs->time) / 1000.0);
+ }
+}
+
+static void reqstamp_end(uint32_t seqno)
+{
+ reqstamp_t *rs = reqstamps + HASH_FUNC(seqno);
+ uint64_t now = reqstamp_current_time();
+ uint64_t diff = 0;
+
+ if (rs->seqno == seqno && rs->time) {
+ if (now > rs->time) {
+ diff = now - rs->time;
+ }
+
+ printf("request %u was processed in %.2lf msec\n",
+ seqno, (double)diff / 1000.0);
+
+ rs->seqno = 0;
+ rs->time = 0ULL;
+
+ totaltime += diff;
+ reqcount++;
+ }
+}
+
+
+static void str_array_free(string_array_t *arr)
+{
+ uint32_t i;
+
+ if (arr) {
+ for (i = 0; i < arr->dim; i++)
+ mrp_free((void *)arr->elems[i]);
+
+ mrp_free(arr);
+ }
+}
+
+static string_array_t *str_array_dup(uint32_t dim, const char **arr)
+{
+ size_t size;
+ uint32_t i;
+ string_array_t *dup;
+
+ MRP_ASSERT(dim < ARRAY_MAX && arr, "invalid argument");
+
+ if (!dim && arr) {
+ for (dim = 0; arr[dim]; dim++)
+ ;
+ }
+
+ size = sizeof(string_array_t) + (sizeof(const char *) * (dim + 1));
+
+ if (!(dup = mrp_allocz(size))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ dup->dim = dim;
+
+ for (i = 0; i < dim; i++) {
+ if (arr[i]) {
+ if (!(dup->elems[i] = mrp_strdup(arr[i]))) {
+ errno = ENOMEM;
+ /* probably no use for freing anything */
+ return NULL;
+ }
+ }
+ }
+
+ return dup;
+}
+
+
+static int str_array_print(string_array_t *arr, const char *hdr,
+ const char *sep, const char *trail,
+ char *buf, int len)
+{
+ uint32_t i;
+ int cnt;
+
+ char *p, *e;
+
+ if (!sep)
+ sep = " ";
+
+ e = (p = buf) + len;
+ cnt = 0;
+
+ if (hdr && p < e)
+ p += snprintf(p, e-p, "%s", hdr);
+
+ if (arr) {
+ for (i = 0; i < arr->dim && p < e; i++) {
+ p += snprintf(p, e-p, "%s'%s'", sep, arr->elems[i]);
+ cnt++;
+ }
+ }
+
+ if (!cnt && p < e)
+ p += snprintf(p, e-p, "%s<none>", sep);
+
+ if (trail && hdr && p < e)
+ p += snprintf(p, e-p, "%s", trail);
+
+ return p - buf;
+}
+
+#if 0
+static uint32_t str_array_index(string_array_t *arr, const char *member)
+{
+ uint32_t i;
+
+ if (arr && member) {
+ for (i = 0; i < arr->dim; i++) {
+ if (!strcmp(member, arr->elems[i]))
+ return i;
+ }
+ }
+
+ return INVALID_INDEX;
+}
+#endif
+
+static void attribute_array_free(attribute_array_t *arr)
+{
+ uint32_t i;
+ attribute_t *attr;
+
+ if (arr) {
+ for (i = 0; i < arr->dim; i++) {
+ attr = arr->elems + i;
+
+ mrp_free((void *)attr->name);
+
+ if (attr->type == 's')
+ mrp_free((void *)attr->v.string);
+ }
+ mrp_free(arr);
+ }
+}
+
+static attribute_array_t *attribute_array_dup(uint32_t dim, attribute_t *arr)
+{
+ size_t size;
+ uint32_t i;
+ attribute_t *sattr, *dattr;
+ attribute_array_t *dup;
+ int err = ENOMEM;
+
+ MRP_ASSERT(dim < ARRAY_MAX && arr, "invalid argument");
+
+ if (!dim && arr) {
+ for (dim = 0; arr[dim].name; dim++)
+ ;
+ }
+
+ size = sizeof(attribute_array_t) + (sizeof(attribute_t) * (dim + 1));
+
+ if (!(dup = mrp_allocz(size))) {
+ goto failed;
+ }
+
+ dup->dim = dim;
+
+ for (i = 0; i < dim; i++) {
+ sattr = arr + i;
+ dattr = dup->elems + i;
+
+ if (!(dattr->name = mrp_strdup(sattr->name))) {
+ goto failed;
+ }
+
+ switch ((dattr->type = sattr->type)) {
+ case 's':
+ if (!(dattr->v.string = mrp_strdup(sattr->v.string))) {
+ goto failed;
+ }
+ break;
+ case 'i':
+ dattr->v.integer = sattr->v.integer;
+ break;
+ case 'u':
+ dattr->v.unsignd = sattr->v.unsignd;
+ break;
+ case 'f':
+ dattr->v.floating = sattr->v.floating;
+ break;
+ default:
+ errno = EINVAL;
+ goto failed;
+ }
+ }
+
+ return dup;
+
+ failed:
+ attribute_array_free(dup);
+ errno = err;
+ return NULL;
+}
+
+
+static int attribute_array_print(attribute_array_t *arr, const char *hdr,
+ const char *sep, const char *trail,
+ char *buf, int len)
+{
+ attribute_t *attr;
+ uint32_t i;
+ int cnt;
+
+ char *p, *e;
+
+ if (!sep)
+ sep = " ";
+
+ e = (p = buf) + len;
+ cnt = 0;
+
+ if (hdr && p < e)
+ p += snprintf(p, e-p, "%s", hdr);
+
+ if (arr) {
+ for (i = 0; i < arr->dim && p < e; i++) {
+ attr = arr->elems + i;
+
+ p += snprintf(p, e-p, "%s%s:%c:", sep, attr->name, attr->type);
+
+ if (p < e) {
+ switch (attr->type) {
+ case 's': p += snprintf(p,e-p, "'%s'", attr->v.string); break;
+ case 'i': p += snprintf(p,e-p, "%d", attr->v.integer); break;
+ case 'u': p += snprintf(p,e-p, "%u", attr->v.unsignd); break;
+ case 'f': p += snprintf(p,e-p, "%.2lf",attr->v.floating);break;
+ default: p += snprintf(p,e-p, "<unknown>"); break;
+ }
+ }
+
+ cnt++;
+ }
+ }
+
+ if (!cnt && hdr && p < e)
+ p += snprintf(p, e-p, "%s<none>", sep);
+
+ if (trail && hdr && p < e)
+ p += snprintf(p, e-p, "%s", trail);
+
+ return p - buf;
+}
+
+#if 0
+static uint32_t attribute_array_index(attribute_array_t *arr,
+ const char *member)
+{
+ uint32_t i;
+
+ if (arr && member) {
+ for (i = 0; i < arr->dim; i++) {
+ if (!strcmp(member, arr->elems[i].name))
+ return i;
+ }
+ }
+
+ return INVALID_INDEX;
+}
+#endif
+
+static void resource_def_array_free(resource_def_array_t *arr)
+{
+ uint32_t i;
+ resource_def_t *def;
+
+ if (arr) {
+ for (i = 0; i < arr->dim; i++) {
+ def = arr->defs + i;
+
+ mrp_free((void *)def->name);
+ attribute_array_free(def->attrs);
+ }
+
+ mrp_free(arr);
+ }
+}
+
+
+static int resource_def_array_print(resource_def_array_t *arr,
+ const char *rhdr,
+ const char *rsep,
+ const char *rtrail,
+ const char *ahdr,
+ const char *asep,
+ const char *atrail,
+ char *buf, int len)
+{
+ resource_def_t *def;
+ uint32_t i;
+ int cnt;
+
+ char *p, *e;
+
+ if (!rsep)
+ rsep = " ";
+
+ e = (p = buf) + len;
+ cnt = 0;
+
+ if (rhdr && p < e)
+ p += snprintf(p, e-p, "%s", rhdr);
+
+ if (arr) {
+ for (i = 0; i < arr->dim && p < e; i++) {
+ def = arr->defs + i;
+
+ p += snprintf(p, e-p, "%s%s", rsep, def->name);
+
+ if (p < e)
+ p += attribute_array_print(def->attrs,ahdr,asep,atrail,p,e-p);
+
+ cnt++;
+ }
+ }
+
+ if (!cnt && rhdr && p < e)
+ p += snprintf(p, e-p, "%s<none>", rsep);
+
+ if (rtrail && p < e)
+ p += snprintf(p, e-p, "%s", rtrail);
+
+ return p - buf;
+}
+
+
+
+
+static bool fetch_seqno(mrp_msg_t *msg, void **pcursor, uint32_t *pseqno)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
+ {
+ *pseqno = INVALID_SEQNO;
+ return false;
+ }
+
+ *pseqno = value.u32;
+ return true;
+}
+
+
+static bool fetch_request(mrp_msg_t *msg, void **pcursor, uint16_t *preqtype)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
+ {
+ *preqtype = INVALID_REQUEST;
+ return false;
+ }
+
+ *preqtype = value.u16;
+ return true;
+}
+
+static bool fetch_status(mrp_msg_t *msg, void **pcursor, int *pstatus)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
+ {
+ *pstatus = EIO;
+ return false;
+ }
+
+ *pstatus = value.s16;
+ return true;
+}
+
+static bool fetch_resource_set_id(mrp_msg_t *msg, void **pcursor,uint32_t *pid)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
+ {
+ *pid = INVALID_ID;
+ return false;
+ }
+
+ *pid = value.u32;
+ return true;
+}
+
+static bool fetch_resource_set_state(mrp_msg_t *msg, void **pcursor,
+ mrp_resproto_state_t *pstate)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
+ {
+ *pstate = 0;
+ return false;
+ }
+
+ *pstate = value.u16;
+ return true;
+}
+
+
+static bool fetch_resource_set_mask(mrp_msg_t *msg, void **pcursor,
+ int mask_type, mrp_resproto_state_t *pmask)
+{
+ uint16_t expected_tag;
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ switch (mask_type) {
+ case GRANT: expected_tag = RESPROTO_RESOURCE_GRANT; break;
+ case ADVICE: expected_tag = RESPROTO_RESOURCE_ADVICE; break;
+ default: /* don't know what to fetch */ return false;
+ }
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != expected_tag || type != MRP_MSG_FIELD_UINT32)
+ {
+ *pmask = 0;
+ return false;
+ }
+
+ *pmask = value.u32;
+ return true;
+}
+
+static bool fetch_resource_name(mrp_msg_t *msg, void **pcursor,
+ const char **pname)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_RESOURCE_NAME || type != MRP_MSG_FIELD_STRING)
+ {
+ *pname = "<unknown>";
+ return false;
+ }
+
+ *pname = value.str;
+ return true;
+}
+
+
+static bool fetch_str_array(mrp_msg_t *msg, void **pcursor,
+ uint16_t expected_tag, string_array_t **parr)
+{
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != expected_tag || type != MRP_MSG_FIELD_ARRAY_OF(STRING))
+ {
+ *parr = str_array_dup(0, NULL);
+ return false;
+ }
+
+ if (!(*parr = str_array_dup(size, (const char **)value.astr)))
+ return false;
+
+ return true;
+}
+
+static bool fetch_attribute_array(mrp_msg_t *msg, void **pcursor,
+ size_t dim, attribute_t *arr)
+{
+ attribute_t *attr;
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+ size_t i;
+
+ i = 0;
+
+ while (mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size)) {
+ if (tag == RESPROTO_SECTION_END && type == MRP_MSG_FIELD_UINT8)
+ break;
+
+ if (tag != RESPROTO_ATTRIBUTE_NAME ||
+ type != MRP_MSG_FIELD_STRING ||
+ i >= dim - 1)
+ {
+ return false;
+ }
+
+ attr = arr + i++;
+ attr->name = value.str;
+
+ if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
+ tag != RESPROTO_ATTRIBUTE_VALUE)
+ {
+ return false;
+ }
+
+ switch (type) {
+ case MRP_MSG_FIELD_STRING:
+ attr->type = 's';
+ attr->v.string = value.str;
+ break;
+ case MRP_MSG_FIELD_SINT32:
+ attr->type = 'i';
+ attr->v.integer = value.s32;
+ break;
+ case MRP_MSG_FIELD_UINT32:
+ attr->type = 'u';
+ attr->v.unsignd = value.u32;
+ break;
+ case MRP_MSG_FIELD_DOUBLE:
+ attr->type = 'f';
+ attr->v.floating = value.dbl;
+ break;
+ default:
+ return false;
+ }
+ }
+
+ memset(arr + i, 0, sizeof(attribute_t));
+
+ return true;
+}
+
+
+static void resource_query_response(client_t *client, uint32_t seqno,
+ mrp_msg_t *msg, void **pcursor)
+{
+ int status;
+ uint32_t dim, i;
+ resource_def_t rdef[RESOURCE_MAX];
+ attribute_t attrs[ATTRIBUTE_MAX + 1];
+ resource_def_t *src, *dst;
+ resource_def_array_t *arr;
+ size_t size;
+ char buf[4096];
+
+ MRP_UNUSED(seqno);
+
+ if (!fetch_status(msg, pcursor, &status))
+ goto failed;
+
+ if (status != 0)
+ printf("Resource query failed (%u): %s\n", status, strerror(status));
+ else {
+ dim = 0;
+
+ while (fetch_resource_name(msg, pcursor, &rdef[dim].name)) {
+ if (!fetch_attribute_array(msg, pcursor, ATTRIBUTE_MAX+1, attrs))
+ goto failed;
+
+ if (!(rdef[dim].attrs = attribute_array_dup(0, attrs))) {
+ mrp_log_error("failed to duplicate attributes");
+ return;
+ }
+
+ dim++;
+ }
+
+ size = sizeof(resource_def_array_t) + sizeof(resource_def_t) * (dim+1);
+
+
+ resource_def_array_free(client->resources);
+
+ client->resources = arr = mrp_allocz(size);
+
+ arr->dim = dim;
+
+ for (i = 0; i < dim; i++) {
+ src = rdef + i;
+ dst = arr->defs + i;
+
+ dst->name = mrp_strdup(src->name);
+ dst->attrs = src->attrs;
+ }
+
+ resource_def_array_print(client->resources,
+ "Resource definitions:", "\n ", "\n",
+ NULL, "\n ", NULL,
+ buf, sizeof(buf));
+ printf("\n%s", buf);
+
+ client->prompt = true;
+ print_prompt(client, true);
+ }
+
+ return;
+
+ failed:
+ mrp_log_error("malformed reply to recource query");
+}
+
+static void class_query_response(client_t *client, uint32_t seqno,
+ mrp_msg_t *msg, void **pcursor)
+{
+ int status;
+ string_array_t *arr;
+ char buf[4096];
+
+ MRP_UNUSED(seqno);
+
+ if (!fetch_status(msg, pcursor, &status) || (status == 0 &&
+ !fetch_str_array(msg, pcursor, RESPROTO_CLASS_NAME, &arr)))
+ {
+ mrp_log_error("ignoring malformed response to class query");
+ return;
+ }
+
+ if (status) {
+ mrp_log_error("class query failed with error code %u", status);
+ return;
+ }
+
+ str_array_free(client->class_names);
+ client->class_names = arr;
+
+ str_array_print(arr, "Application class names:", "\n ", "\n",
+ buf, sizeof(buf));
+
+ printf("\n%s", buf);
+
+ client->prompt = true;
+ print_prompt(client, true);
+}
+
+static void zone_query_response(client_t *client, uint32_t seqno,
+ mrp_msg_t *msg, void **pcursor)
+{
+ int status;
+ string_array_t *arr;
+ char buf[4096];
+
+ MRP_UNUSED(seqno);
+
+ if (!fetch_status(msg, pcursor, &status) || (status == 0 &&
+ !fetch_str_array(msg, pcursor, RESPROTO_ZONE_NAME, &arr)))
+ {
+ mrp_log_error("ignoring malformed response to zone query");
+ return;
+ }
+
+ if (status) {
+ mrp_log_error("zone query failed with error code %u", status);
+ return;
+ }
+
+ str_array_free(client->zone_names);
+ client->zone_names = arr;
+
+ str_array_print(arr, "Zone names:", "\n ", "\n",
+ buf, sizeof(buf));
+
+ printf("\n%s", buf);
+
+ client->prompt = true;
+ print_prompt(client, true);
+}
+
+static void create_resource_set_response(client_t *client, uint32_t seqno,
+ mrp_msg_t *msg, void **pcursor)
+{
+ int status;
+ uint32_t rset_id;
+
+ MRP_UNUSED(seqno);
+
+ if (!fetch_status(msg, pcursor, &status) || (status == 0 &&
+ !fetch_resource_set_id(msg, pcursor, &rset_id)))
+ {
+ mrp_log_error("ignoring malformed response to resource set creation");
+ return;
+ }
+
+ if (status) {
+ mrp_log_error("creation of resource set failed. error code %u",status);
+ return;
+ }
+
+ client->rset_id = rset_id;
+
+ printf("\nresource set %u created\n", rset_id);
+
+ client->prompt = true;
+ print_prompt(client, true);
+}
+
+static void acquire_resource_set_response(client_t *client, uint32_t seqno,
+ bool acquire, mrp_msg_t *msg,
+ void **pcursor)
+{
+ const char *op = acquire ? "acquisition" : "release";
+ int status;
+ uint32_t rset_id;
+
+ if (!fetch_resource_set_id(msg, pcursor, &rset_id) ||
+ !fetch_status(msg, pcursor, &status))
+ {
+ mrp_log_error("ignoring malformed response to resource set %s", op);
+ return;
+ }
+
+ if (status) {
+ printf("\n%s of resource set %u failed. request no %u "
+ "error code %u", op, rset_id, seqno, status);
+ }
+ else {
+ printf("\nSuccessful %s of resource set %u. request no %u\n",
+ op, rset_id, seqno);
+ }
+
+ client->prompt = true;
+
+ if (status)
+ print_prompt(client, true);
+}
+
+
+static void resource_event(client_t *client, uint32_t seqno, mrp_msg_t *msg,
+ void **pcursor)
+{
+ uint32_t rset;
+ uint32_t grant, advice;
+ mrp_resproto_state_t state;
+ const char *str_state;
+ uint16_t tag;
+ uint16_t type;
+ mrp_msg_value_t value;
+ size_t size;
+ uint32_t resid;
+ const char *resnam;
+ attribute_t attrs[ATTRIBUTE_MAX + 1];
+ attribute_array_t *list;
+ char buf[4096];
+ uint32_t mask;
+ int cnt;
+
+ printf("\nResource event (request no %u):\n", seqno);
+
+ if (!fetch_resource_set_id(msg, pcursor, &rset) ||
+ !fetch_resource_set_state(msg, pcursor, &state) ||
+ !fetch_resource_set_mask(msg, pcursor, GRANT, &grant) ||
+ !fetch_resource_set_mask(msg, pcursor, ADVICE, &advice))
+ goto malformed;
+
+ switch (state) {
+ case RESPROTO_RELEASE: str_state = "release"; break;
+ case RESPROTO_ACQUIRE: str_state = "acquire"; break;
+ default: str_state = "<unknown>"; break;
+ }
+
+ printf(" resource-set ID : %u\n" , rset);
+ printf(" state : %s\n" , str_state);
+ printf(" grant mask : 0x%x\n", grant);
+ printf(" advice mask : 0x%x\n", advice);
+ printf(" resources :");
+
+ cnt = 0;
+
+ while (mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size)) {
+ if ((tag != RESPROTO_RESOURCE_ID || type != MRP_MSG_FIELD_UINT32) ||
+ !fetch_resource_name(msg, pcursor, &resnam))
+ goto malformed;
+
+ resid = value.u32;
+ mask = (1UL << resid);
+
+ if (!cnt++)
+ printf("\n");
+
+ printf(" %02u name : %s\n", resid, resnam);
+ printf(" mask : 0x%x\n", mask);
+ printf(" grant : %s\n", (grant & mask) ? "yes" : "no");
+ printf(" advice : %savailable\n",
+ (advice & mask) ? "" : "not ");
+
+ if (!fetch_attribute_array(msg, pcursor, ATTRIBUTE_MAX + 1, attrs))
+ goto malformed;
+
+ if (!(list = attribute_array_dup(0, attrs))) {
+ mrp_log_error("failed to duplicate attribute list");
+ exit(ENOMEM);
+ }
+
+ attribute_array_print(list, " attributes :", " ", "\n",
+ buf, sizeof(buf));
+ printf("%s", buf);
+
+ attribute_array_free(list);
+ }
+
+ if (!cnt)
+ printf(" <none>\n");
+
+ print_prompt(client, true);
+
+ return;
+
+ malformed:
+ mrp_log_error("ignoring malformed resource event");
+}
+
+
+static void recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
+ mrp_sockaddr_t *addr, socklen_t addrlen,
+ void *user_data)
+{
+ client_t *client = (client_t *)user_data;
+ void *cursor = NULL;
+ uint32_t seqno;
+ uint16_t request;
+
+ MRP_UNUSED(transp);
+ MRP_UNUSED(addr);
+ MRP_UNUSED(addrlen);
+
+ if (client->msgdump) {
+ mrp_log_info("received a message");
+ mrp_msg_dump(msg, stdout);
+ }
+
+ if (!fetch_seqno (msg, &cursor, &seqno ) ||
+ !fetch_request (msg, &cursor, &request) )
+ {
+ mrp_log_error("ignoring malformed message");
+ return;
+ }
+
+
+ switch (request) {
+ case RESPROTO_QUERY_RESOURCES:
+ reqstamp_end(seqno);
+ resource_query_response(client, seqno, msg, &cursor);
+ break;
+ case RESPROTO_QUERY_CLASSES:
+ reqstamp_end(seqno);
+ class_query_response(client, seqno, msg, &cursor);
+ break;
+ case RESPROTO_QUERY_ZONES:
+ reqstamp_end(seqno);
+ zone_query_response(client, seqno, msg, &cursor);
+ break;
+ case RESPROTO_CREATE_RESOURCE_SET:
+ reqstamp_end(seqno);
+ create_resource_set_response(client, seqno, msg, &cursor);
+ break;
+ case RESPROTO_ACQUIRE_RESOURCE_SET:
+ reqstamp_intermediate(seqno);
+ acquire_resource_set_response(client, seqno, true, msg, &cursor);
+ break;
+ case RESPROTO_RELEASE_RESOURCE_SET:
+ reqstamp_intermediate(seqno);
+ acquire_resource_set_response(client, seqno, false, msg, &cursor);
+ break;
+ case RESPROTO_RESOURCES_EVENT:
+ reqstamp_end(seqno);
+ resource_event(client, seqno, msg, &cursor);
+ break;
+ default:
+ mrp_log_error("ignoring unsupported request type %u", request);
+ break;
+ }
+}
+
+static void recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *user_data)
+{
+ return recvfrom_msg(t, msg, NULL, 0, user_data);
+}
+
+
+void closed_evt(mrp_transport_t *transp, int error, void *user_data)
+{
+ MRP_UNUSED(transp);
+ MRP_UNUSED(user_data);
+
+ if (error) {
+ mrp_log_error("Connection closed with error %d (%s)", error,
+ strerror(error));
+ exit(EIO);
+ }
+ else {
+ mrp_log_info("Peer has closed the connection");
+ exit(0);
+ }
+}
+
+
+static void init_transport(client_t *client, char *addr)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvmsg = recv_msg },
+ { .recvmsgfrom = recvfrom_msg },
+ .closed = closed_evt,
+ .connection = NULL
+ };
+
+ client->alen = mrp_transport_resolve(NULL, addr, &client->saddr,
+ sizeof(client->saddr),&client->atype);
+ if (client->alen <= 0) {
+ mrp_log_error("Can't resolve transport address '%s'", addr);
+ exit(EINVAL);
+ }
+
+ client->transp = mrp_transport_create(client->ml, client->atype,
+ &evt, client, 0);
+
+ if (!client->transp) {
+ mrp_log_error("Failed to create transport");
+ exit(EIO);
+ }
+
+ if (!mrp_transport_connect(client->transp, &client->saddr, client->alen)) {
+ mrp_log_error("Failed to connect to '%s'", addr);
+ exit(EIO);
+ }
+}
+
+
+
+static mrp_msg_t *create_request(uint32_t seqno, mrp_resproto_request_t req)
+{
+ uint16_t type = req;
+ mrp_msg_t *msg;
+
+ msg = mrp_msg_create(RESPROTO_SEQUENCE_NO , MRP_MSG_FIELD_UINT32, seqno,
+ RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, type ,
+ RESPROTO_MESSAGE_END );
+
+ if (!msg) {
+ mrp_log_error("Unable to create new message");
+ exit(ENOMEM);
+ }
+
+ reqstamp_start(seqno);
+
+ return msg;
+}
+
+static void send_message(client_t *client, mrp_msg_t *msg)
+{
+ if (!mrp_transport_send(client->transp, msg)) {
+ mrp_log_error("Failed to send message");
+ exit(EIO);
+ }
+
+ mrp_msg_unref(msg);
+}
+
+static void query_resources(client_t *client)
+{
+ mrp_msg_t *req;
+
+ req = create_request(client->seqno++, RESPROTO_QUERY_RESOURCES);
+
+ send_message(client, req);
+}
+
+static void query_classes(client_t *client)
+{
+ mrp_msg_t *req;
+
+ req = create_request(client->seqno++, RESPROTO_QUERY_CLASSES);
+
+ send_message(client, req);
+}
+
+static void query_zones(client_t *client)
+{
+ mrp_msg_t *req;
+
+ req = create_request(client->seqno++, RESPROTO_QUERY_ZONES);
+
+ send_message(client, req);
+}
+
+static char *parse_attribute(mrp_msg_t *msg, char *str, char *sep)
+{
+#define PUSH_ATTRIBUTE_NAME(m, n) \
+ mrp_msg_append(m, MRP_MSG_TAG_STRING(RESPROTO_ATTRIBUTE_NAME, n))
+
+#define PUSH_ATTRIBUTE_VALUE(m, t, v) \
+ mrp_msg_append(m, MRP_MSG_TAG_##t(RESPROTO_ATTRIBUTE_VALUE, v))
+
+
+ char *p, *e, c;
+ char *name;
+ char type;
+ char *valstr;
+ uint32_t unsignd;
+ int32_t integer;
+ double floating;
+
+
+ *sep = '\0';
+
+ if (!(p = str))
+ return NULL;
+
+ name = p;
+ while ((c = *p++)) {
+ if (c == ':') {
+ *(p-1) = '\0';
+ break;
+ }
+ if (!isalnum(c) && c != '_' && c != '-') {
+ mrp_log_error("invalid attribute name: '%s'", name);
+ return NULL;
+ }
+ }
+
+ if (!c || !(type = *p++) || (*p++ != ':')) {
+ mrp_log_error("invalid or missing resource type");
+ return NULL;
+ }
+
+ if (*p == '\"') {
+ valstr = ++p;
+ while ((c = *p++) != '\"') {
+ if (!c) {
+ mrp_log_error("bad quoted value '%s'", valstr-1);
+ return NULL;
+ }
+ }
+ *(p-1) = '\0';
+ if ((c = *p)) {
+ if (c == '/' || c == ',')
+ p++;
+ else {
+ mrp_log_error("invalid separator '%s'", p);
+ return NULL;
+ }
+ }
+ }
+ else {
+ valstr = p;
+ while ((c = *p++)) {
+ if (c == '/' || c == ',') {
+ *(p-1) = '\0';
+ break;
+ }
+ if (c < 0x20) {
+ mrp_log_error("invalid attribute value '%s'", valstr);
+ return NULL;
+ }
+ }
+ }
+
+ *sep = c;
+
+ if (!PUSH_ATTRIBUTE_NAME(msg, name))
+ goto error;
+
+ if (type == 's') {
+ if (!PUSH_ATTRIBUTE_VALUE(msg, STRING, valstr))
+ goto error;
+ }
+ else if (type == 'i') {
+ integer = strtol(valstr, &e, 10);
+
+ if (*e || e == valstr || !PUSH_ATTRIBUTE_VALUE(msg, SINT32, integer))
+ goto error;
+ }
+ else if (type == 'u') {
+ unsignd = strtoul(valstr, &e, 10);
+
+ if (*e || e == valstr || !PUSH_ATTRIBUTE_VALUE(msg, UINT32, unsignd))
+ goto error;
+ }
+ else if (type == 'f') {
+ floating = strtod(valstr, &e);
+
+ if (*e || e == valstr || !PUSH_ATTRIBUTE_VALUE(msg, DOUBLE, floating))
+ goto error;
+ }
+
+
+ return (p && *p) ? p : NULL;
+
+ error:
+ mrp_log_error("failed to build resource-set creation request");
+ return NULL;
+
+#undef PUSH_ATTRIBUTE_VALUE
+#undef PUSH_ATTRIBUTE_NAME
+}
+
+bool parse_flags(char *str, uint32_t *pflags)
+{
+ typedef struct { char *str; uint32_t flags; } flagdef_t;
+
+ static flagdef_t flagdefs[] = {
+ { "M" , RESPROTO_RESFLAG_MANDATORY | 0 },
+ { "O" , 0 | 0 },
+ { "S" , RESPROTO_RESFLAG_MANDATORY | RESPROTO_RESFLAG_SHARED },
+ { "E" , RESPROTO_RESFLAG_MANDATORY | 0 },
+ { "MS", RESPROTO_RESFLAG_MANDATORY | RESPROTO_RESFLAG_SHARED },
+ { "ME", RESPROTO_RESFLAG_MANDATORY | 0 },
+ { "OS", 0 | RESPROTO_RESFLAG_SHARED },
+ { "OE", 0 | 0 },
+ { "SM", RESPROTO_RESFLAG_MANDATORY | RESPROTO_RESFLAG_SHARED },
+ { "SO", 0 | RESPROTO_RESFLAG_SHARED },
+ { "EM", RESPROTO_RESFLAG_MANDATORY | 0 },
+ { "EO", 0 | 0 },
+ { NULL, 0 | 0 }
+ };
+
+ flagdef_t *fd;
+ bool success;
+
+ *pflags = RESPROTO_RESFLAG_MANDATORY;
+
+ if (!str)
+ success = true;
+ else {
+ for (success = false, fd = flagdefs; fd->str; fd++) {
+ if (!strcasecmp(str, fd->str)) {
+ success = true;
+ *pflags = fd->flags;
+ break;
+ }
+ }
+ }
+
+ return success;
+}
+
+static char *parse_resource(mrp_msg_t *msg, char *str, char *sep)
+{
+#define PUSH(msg, tag, typ, val) \
+ mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
+
+ uint32_t flags;
+ char *name, *flgstr;
+ char *p;
+ char c;
+
+ *sep = '\0';
+
+ if (!(p = str))
+ return NULL;
+
+ name = p;
+ flgstr = NULL;
+
+ while ((c = *p++)) {
+ if (c == ':') {
+ *(p-1) = '\0';
+ flgstr = name;
+ name = p;
+ }
+ else if (c == '/' || c == ',') {
+ *(p-1) = '\0';
+ break;
+ }
+ else if (!isalnum(c) && c != '_' && c != '-') {
+ mrp_log_error("invalid resource name: '%s'", name);
+ return NULL;
+ }
+ }
+
+ if (!parse_flags(flgstr, &flags)) {
+ mrp_log_error("invalid flag string '%s'", flgstr ? flgstr : "");
+ return NULL;
+ }
+
+ if (!PUSH(msg, RESOURCE_NAME , STRING, name ) ||
+ !PUSH(msg, RESOURCE_FLAGS, UINT32, flags) )
+ goto failed;
+
+ if (!c)
+ p--;
+ else {
+ while ((*sep = c) == '/')
+ p = parse_attribute(msg, p, &c);
+ }
+
+ if (!PUSH(msg, SECTION_END, UINT8, 0))
+ goto failed;
+
+ return (p && *p) ? p : NULL;
+
+ failed:
+ mrp_log_error("failed to build resource-set creation request");
+ *sep = '\0';
+ return NULL;
+
+#undef PUSH
+}
+
+static void create_resource_set(client_t *client,
+ const char *class,
+ const char *zone,
+ const char *def,
+ uint32_t flags,
+ uint32_t priority)
+{
+#define PUSH(msg, tag, typ, val) \
+ mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
+
+ char *buf;
+ mrp_msg_t *req;
+ char *p;
+ char c;
+
+ /* 'def' => {m|o}{s|e}:resource_name/attr_name:{s|i|u|f}:["]value["] */
+
+ if (!client || !class || !zone || !def)
+ return;
+
+ req = create_request(client->seqno++, RESPROTO_CREATE_RESOURCE_SET);
+
+ if (!PUSH(req, RESOURCE_FLAGS , UINT32, flags ) ||
+ !PUSH(req, RESOURCE_PRIORITY, UINT32, priority) ||
+ !PUSH(req, CLASS_NAME , STRING, class ) ||
+ !PUSH(req, ZONE_NAME , STRING, zone ) )
+ {
+ mrp_msg_unref(req);
+ }
+ else {
+ p = buf = mrp_strdup(def);
+ c = ',';
+
+ while (c == ',')
+ p = parse_resource(req, p, &c);
+
+ if (client->msgdump)
+ mrp_msg_dump(req, stdout);
+
+ send_message(client, req);
+
+ mrp_free(buf);
+ }
+
+#undef PUSH
+}
+
+static uint32_t acquire_resource_set(client_t *client, bool acquire)
+{
+#define PUSH(msg, tag, typ, val) \
+ mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
+
+ uint16_t tag;
+ uint32_t reqno;
+ mrp_msg_t *req;
+
+ if (!client || client->rset_id == INVALID_ID)
+ return 0;
+
+ if (acquire)
+ tag = RESPROTO_ACQUIRE_RESOURCE_SET;
+ else
+ tag = RESPROTO_RELEASE_RESOURCE_SET;
+
+ req = create_request((reqno = client->seqno++), tag);
+
+ if (!PUSH(req, RESOURCE_SET_ID, UINT32, client->rset_id))
+ mrp_msg_unref(req);
+ else {
+ if (client->msgdump)
+ mrp_msg_dump(req, stdout);
+
+ send_message(client, req);
+ }
+
+ return reqno;
+
+#undef PUSH
+}
+
+static void print_prompt(client_t *client, bool startwith_lf)
+{
+ if (client && client->prompt) {
+ printf("%s%s>", startwith_lf ? "\n":"", client->name);
+ fflush(stdout);
+ }
+}
+
+static void print_command_help(void)
+{
+ printf("\nAvailable commands:\n");
+ printf(" help\t\tprints this help\n");
+ printf(" quit\t\texits\n");
+ printf(" resources\tprints the resource definitions\n");
+ printf(" classes\tprints the application classes\n");
+ printf(" zones\tprints the zones\n");
+ printf(" acquire\tacquires the resource-set specified by command "
+ "line options\n");
+ printf(" release\treleases the resource-set specified by command "
+ "line options\n");
+}
+
+static void parse_line(client_t *client, char *buf, int len)
+{
+ char *p, *e;
+
+ if (len <= 0)
+ print_prompt(client, false);
+ else {
+ for (p = buf; isblank(*p); p++) ;
+ for (e = buf+len; e > buf && isblank(*(e-1)); e--) ;
+
+ *e = '\0';
+
+ if (!strcmp(p, "help")) {
+ print_command_help();
+ print_prompt(client, true);
+ }
+ else if (!strcmp(p, "quit") || !strcmp(p, "exit")) {
+ printf("\n");
+ mrp_mainloop_quit(client->ml, 0);
+ }
+ else if (!strcmp(p, "resources")) {
+ client->prompt = false;
+ printf(" querying resource definitions\n");
+ query_resources(client);
+ }
+ else if (!strcmp(p, "classes")) {
+ client->prompt = false;
+ printf(" querying application classes\n");
+ query_classes(client);
+ }
+ else if (!strcmp(p, "zones")) {
+ client->prompt = false;
+ printf(" querying zones\n");
+ query_zones(client);
+ }
+ else if (!strcmp(p, "acquire")) {
+ if (client->rset_id == INVALID_ID) {
+ printf(" there is no resource set\n");
+ print_prompt(client, true);
+ }
+ else {
+ client->prompt = false;
+ printf(" acquiring resource set %u. request no %u\n",
+ client->rset_id, acquire_resource_set(client, true));
+ }
+ }
+ else if (!strcmp(p, "release")) {
+ if (client->rset_id == INVALID_ID) {
+ printf(" there is no resource set\n");
+ print_prompt(client, true);
+ }
+ else {
+ client->prompt = false;
+ printf(" releasing resource set %u. request no %u\n",
+ client->rset_id, acquire_resource_set(client, false));
+ }
+ }
+ else {
+ printf(" unsupported command\n");
+ print_prompt(client, true);
+ }
+ }
+}
+
+static void console_input(mrp_io_watch_t *w, int fd, mrp_io_event_t events,
+ void *user_data)
+{
+ static char buf[512];
+ static char *bufend = buf + (sizeof(buf) - 1);
+ static char *writep = buf;
+
+ client_t *client = (client_t *)user_data;
+ int len;
+ char *eol;
+
+ MRP_UNUSED(w);
+ MRP_UNUSED(events);
+
+ MRP_ASSERT(client, "invalid argument");
+ MRP_ASSERT(fd == 0, "confused with data structures");
+
+ while ((len = read(fd, writep, bufend-writep)) < 0) {
+ if (errno != EINTR) {
+ mrp_log_error("read error %d: %s", errno, strerror(errno));
+ return;
+ }
+ }
+
+ *(writep += len) = '\0';
+
+ while ((eol = strchr(buf, '\n'))) {
+ *eol++ = '\0';
+
+ parse_line(client, buf, (eol-buf)-1);
+
+ if ((len = writep - eol) <= 0) {
+ writep = buf;
+ break;
+ }
+ else {
+ memmove(buf, eol, len);
+ writep = buf + len;
+ }
+ }
+}
+
+static void sighandler(mrp_sighandler_t *h, int signum, void *user_data)
+{
+ mrp_mainloop_t *ml = mrp_get_sighandler_mainloop(h);
+ client_t *client = (client_t *)user_data;
+
+ MRP_UNUSED(h);
+
+ MRP_ASSERT(client, "invalid argument");
+
+ switch (signum) {
+
+ case SIGHUP:
+ case SIGTERM:
+ case SIGINT:
+ if (ml)
+ mrp_mainloop_quit(ml, 0);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void usage(client_t *client, int exit_code)
+{
+ printf("Usage: "
+ "%s [-h] [-v] [-r] [-a] [-w] [-p pri] [class zone resources]\n"
+ "\nwhere\n"
+ "\t-h\t\tprints this help\n"
+ "\t-v\t\tverbose mode (dumps the transport messages)\n"
+ "\t-a\t\tautoacquire mode\n"
+ "\t-w\t\tdont wait for resources if they were not available\n"
+ "\t-r\t\tautorelease mode\n"
+ "\t-p priority\t\tresource set priority (priority is 0-7)\n"
+ "\tclass\t\tapplication class of the resource set\n"
+ "\tzone\t\tzone wher the resource set lives\n"
+ "\tresources\tcomma separated list of resources. Each resource is\n"
+ "\t\t\tspecified as flags:name[/attribute[/ ... ]]\n"
+ "\t\t\tflags\t\tspecified as {m|o}{s|e} where\n"
+ "\t\t\t\t\t'm' stands for mandatory,\n"
+ "\t\t\t\t\t'o' for optional,\n"
+ "\t\t\t\t\t's' for shared and\n"
+ "\t\t\t\t\t'e' for exclusive.\n"
+ "\t\t\tresource\tis the name of the resource composed of\n"
+ "\t\t\t\t\ta series of letters, digits, '_' and\n"
+ "\t\t\t\t\t'-' characters\n"
+ "\t\t\tattribute\tis defined as attr-name:type:[\"]value[\"]\n"
+ "\t\t\t\t\ttypes can be\n"
+ "\t\t\t\t\t's' - string\n"
+ "\t\t\t\t\t'i' - signed integer\n"
+ "\t\t\t\t\t'u' - unsigned integer\n"
+ "\t\t\t\t\t'f' - floating\n"
+ "\nExample:\n\n%s player driver "
+ "ms:audio_playback/role:s:\"video\",me:video_playback\n"
+ "\n", client->name, client->name);
+
+ exit(exit_code);
+}
+
+static void parse_arguments(client_t *client, int argc, char **argv)
+{
+ unsigned long pri;
+ char *e;
+ int opt;
+
+ while ((opt = getopt(argc, argv, "hvrawp:")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(client, 0);
+ case 'v':
+ client->msgdump = true;
+ break;
+ case 'a':
+ client->rsetf |= RESPROTO_RSETFLAG_AUTOACQUIRE;
+ break;
+ case 'r':
+ client->rsetf |= RESPROTO_RSETFLAG_AUTORELEASE;
+ break;
+ case 'w':
+ client->rsetf |= RESPROTO_RSETFLAG_DONTWAIT;
+ break;
+ case 'p':
+ pri = strtoul(optarg, &e, 10);
+ if (e == optarg || *e || pri > 7)
+ usage(client, EINVAL);
+ else
+ client->priority = pri;
+ break;
+ default:
+ usage(client, EINVAL);
+ }
+ }
+
+ if (optind + 3 == argc) {
+ client->class = argv[optind + 0];
+ client->zone = argv[optind + 1];
+ client->rsetd = argv[optind + 2];
+ }
+ else if (optind < argc) {
+ usage(client, EINVAL);
+ }
+}
+
+
+int main(int argc, char **argv)
+{
+ client_t *client = mrp_allocz(sizeof(client_t));
+ char *addr = RESPROTO_DEFAULT_ADDRESS;
+
+ mrp_log_set_mask(MRP_LOG_UPTO(MRP_LOG_DEBUG));
+ mrp_log_set_target(MRP_LOG_TO_STDOUT);
+
+ client->name = mrp_strdup(basename(argv[0]));
+ client->ml = mrp_mainloop_create();
+ client->seqno = 1;
+ client->prompt = false;
+ client->rset_id = INVALID_ID;
+
+ if (!client->ml || !client->name)
+ exit(1);
+
+ parse_arguments(client, argc, argv);
+
+ mrp_add_sighandler(client->ml, SIGHUP , sighandler, client);
+ mrp_add_sighandler(client->ml, SIGTERM, sighandler, client);
+ mrp_add_sighandler(client->ml, SIGINT , sighandler, client);
+
+ init_transport(client, addr);
+
+
+ if (!client->class || !client->zone || !client->rsetd)
+ print_prompt(client, false);
+ else {
+ create_resource_set(client, client->class, client->zone,
+ client->rsetd, client->rsetf, client->priority);
+ }
+
+ mrp_add_io_watch(client->ml, 0, MRP_IO_EVENT_IN, console_input, client);
+
+ mrp_mainloop_run(client->ml);
+
+ if (reqcount > 0)
+ printf("%u requests, avarage request processing time %.2lfmsec\n",
+ reqcount, (double)(totaltime / (uint64_t)reqcount) / 1000.0);
+
+ printf("exiting now ...\n");
+
+ mrp_transport_destroy(client->transp);
+
+ mrp_mainloop_destroy(client->ml);
+ mrp_free((void *)client->name);
+ resource_def_array_free(client->resources);
+ str_array_free(client->class_names);
+ str_array_free(client->zone_names);
+ mrp_free(client);
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/transport.h>
+#include <murphy/common/wsck-transport.h>
+#include <murphy/common/json.h>
+#include <murphy/core/plugin.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/config-api.h>
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/protocol.h>
+
+#include "resource-wrt.h"
+#include "config.h"
+
+#ifdef MURPHY_DATADIR /* default content dir */
+# define DEFAULT_HTTPDIR MURPHY_DATADIR"/resource-wrt"
+#else
+# define DEFAULT_HTTPDIR "/usr/share/murphy/resource-wrt"
+#endif
+
+#define DEFAULT_ADDRESS "wsck:127.0.0.1:4000/murphy"
+#define ATTRIBUTE_MAX MRP_ATTRIBUTE_MAX
+
+/*
+ * plugin argument indices
+ */
+
+enum {
+ ARG_ADDRESS, /* transport address to use */
+ ARG_HTTPDIR, /* content directory for HTTP */
+ ARG_SSLCERT, /* path to SSL certificate */
+ ARG_SSLPKEY, /* path to SSL private key */
+ ARG_SSLCA /* path to SSL CA */
+};
+
+
+/*
+ * WRT resource context
+ */
+
+typedef struct {
+ mrp_context_t *ctx; /* murphy context */
+ mrp_transport_t *lt; /* transport we listen on */
+ const char *addr; /* address we listen on */
+ mrp_list_hook_t clients; /* connected clients */
+ int id; /* next client id */
+ const char *httpdir; /* WRT-resource agent directory */
+ const char *sslcert; /* path to SSL certificate */
+ const char *sslpkey; /* path to SSL private key */
+ const char *sslca; /* path to SSL CA */
+
+} wrt_data_t;
+
+
+typedef struct {
+ int id; /* client id */
+ int seq; /* last request sequence number */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_transport_t *t; /* client transport */
+ mrp_resource_client_t *rsc; /* resource client */
+ mrp_list_hook_t hook; /* to list of clients */
+ /*
+ * Notes:
+ * The resource infra sends the first event for a resource set
+ * out-of-order, before it has acknowledged the creation of the
+ * set and let the client know the resource set id. We use the
+ * field below to suppress this event and force emitting an event
+ * for the resource set right after we have acked the creation request.
+ */
+ mrp_resource_set_t *rset; /* resource set being created */
+ int force_all; /* flag for */
+} wrt_client_t;
+
+
+typedef struct {
+ const char *name;
+ bool mand;
+ bool share;
+ mrp_attr_t attrs[ATTRIBUTE_MAX + 1];
+ int nattr;
+} resdef_t;
+
+
+typedef struct {
+ const char *name;
+ uint32_t flag;
+} flagdef_t;
+
+
+typedef struct {
+ int err;
+ char msg[256];
+} errbuf_t;
+
+
+static int send_message(wrt_client_t *c, mrp_json_t *msg);
+
+static void ignore_invalid_request(wrt_client_t *c, mrp_json_t *req, ...)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(req);
+
+ mrp_log_error("Ignoring invalid WRT resource request");
+}
+
+
+static void ignore_unknown_request(wrt_client_t *c, mrp_json_t *req,
+ const char *type)
+{
+ MRP_UNUSED(c);
+ MRP_UNUSED(req);
+
+ mrp_log_error("Ignoring unknown WRT resource request '%s'", type);
+}
+
+
+static int error(errbuf_t *e, int code, const char *format, ...)
+{
+ va_list ap;
+
+ errno = e->err = code;
+
+ va_start(ap, format);
+ vsnprintf(e->msg, sizeof(e->msg), format, ap);
+ va_end(ap);
+
+ return code;
+}
+
+
+static mrp_json_t *alloc_reply(const char *type, int seq)
+{
+ mrp_json_t *reply;
+
+ reply = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (reply != NULL) {
+ if (mrp_json_add_string (reply, "type", type) &&
+ mrp_json_add_integer(reply, "seq" , seq))
+ return reply;
+ else
+ mrp_json_unref(reply);
+ }
+
+ mrp_log_error("Failed to allocate WRT resource reply.");
+
+ return NULL;
+}
+
+
+static void error_reply(wrt_client_t *c, const char *type, int seq, int code,
+ const char *fmt, ...)
+{
+ mrp_json_t *reply;
+ char errmsg[256];
+ va_list ap;
+
+ reply = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (reply != NULL) {
+ va_start(ap, fmt);
+ vsnprintf(errmsg, sizeof(errmsg), fmt, ap);
+ errmsg[sizeof(errmsg) - 1] = '\0';
+ va_end(ap);
+
+ if (mrp_json_add_string (reply, "type" , type) &&
+ mrp_json_add_integer(reply, "seq" , seq ) &&
+ mrp_json_add_integer(reply, "error" , code) &&
+ mrp_json_add_string (reply, "message", errmsg))
+ send_message(c, reply);
+
+ mrp_json_unref(reply);
+ }
+}
+
+
+static void query_resources(wrt_client_t *c, mrp_json_t *req)
+{
+ const char *type = RESWRT_QUERY_RESOURCES;
+ mrp_json_t *reply, *rarr, *r, *ao;
+ const char **resources;
+ int seq, cnt;
+ mrp_attr_t *attrs, *a;
+ mrp_attr_t buf[ATTRIBUTE_MAX];
+ uint32_t id;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ rarr = r = ao = NULL;
+ resources = mrp_resource_definition_get_all_names(0, NULL);
+
+ if (resources == NULL) {
+ error_reply(c, type, seq, ENOMEM, "failed to query class names");
+ return;
+ }
+
+ reply = alloc_reply(type, seq);
+
+ if (reply == NULL)
+ return;
+
+ rarr = mrp_json_create(MRP_JSON_ARRAY);
+
+ if (rarr == NULL)
+ goto fail;
+
+ for (id = 0; resources[id]; id++) {
+ r = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (r == NULL)
+ goto fail;
+
+ if (!mrp_json_add_string (r, "name", resources[id]))
+ goto fail;
+
+ attrs = mrp_resource_definition_read_all_attributes(id,
+ ATTRIBUTE_MAX, buf);
+
+ ao = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (ao == NULL)
+ goto fail;
+
+ for (a = attrs, cnt = 0; a->name; a++, cnt++) {
+ switch (a->type) {
+ case mqi_string:
+ if (!mrp_json_add_string(ao, a->name, a->value.string))
+ goto fail;
+
+ break;
+ case mqi_integer:
+ case mqi_unsignd:
+ if (!mrp_json_add_integer(ao, a->name, a->value.integer))
+ goto fail;
+ break;
+ case mqi_floating:
+ if (!mrp_json_add_double(ao, a->name, a->value.floating))
+ goto fail;
+ break;
+ default:
+ mrp_log_error("attribute '%s' of resource '%s' "
+ "has unknown type %d", a->name, resources[id],
+ a->type);
+ break;
+ }
+ }
+
+ if (cnt > 0)
+ mrp_json_add(r, "attributes", ao);
+ else
+ mrp_json_unref(ao);
+
+ ao = NULL;
+
+ if (!mrp_json_array_append(rarr, r))
+ goto fail;
+ else
+ r = NULL;
+ }
+
+ if (mrp_json_add_integer(reply, "status" , 0)) {
+ mrp_json_add (reply, "resources", rarr);
+ send_message(c, reply);
+ }
+
+ mrp_json_unref(reply);
+ mrp_free(resources);
+ return;
+
+ fail:
+ mrp_json_unref(reply);
+ mrp_json_unref(rarr);
+ mrp_json_unref(r);
+ mrp_json_unref(ao);
+ mrp_free(resources);
+}
+
+
+static void query_classes(wrt_client_t *c, mrp_json_t *req)
+{
+ const char *type = RESWRT_QUERY_CLASSES;
+ mrp_json_t *reply;
+ const char **classes;
+ size_t nclass;
+ int seq;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ classes = mrp_application_class_get_all_names(0, NULL);
+
+ if (classes == NULL) {
+ error_reply(c, type, seq, ENOMEM, "failed to query class names");
+ return;
+ }
+
+ reply = alloc_reply(type, seq);
+
+ if (reply == NULL)
+ return;
+
+ nclass = 0;
+ for (nclass = 0; classes[nclass] != NULL; nclass++)
+ ;
+
+ if (mrp_json_add_integer (reply, "status" , 0) &&
+ mrp_json_add_string_array(reply, "classes", classes, nclass))
+ send_message(c, reply);
+
+ mrp_json_unref(reply);
+ mrp_free(classes);
+}
+
+
+static void query_zones(wrt_client_t *c, mrp_json_t *req)
+{
+ const char *type = RESWRT_QUERY_ZONES;
+ mrp_json_t *reply;
+ const char **zones;
+ size_t nzone;
+ int seq;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ zones = mrp_zone_get_all_names(0, NULL);
+
+ if (zones == NULL) {
+ error_reply(c, type, seq, ENOMEM, "failed to query zone names");
+ return;
+ }
+
+ reply = alloc_reply(type, seq);
+
+ if (reply == NULL)
+ mrp_log_error("Failed to allocate WRT resource reply.");
+
+ nzone = 0;
+ for (nzone = 0; zones[nzone] != NULL; nzone++)
+ ;
+
+ if (mrp_json_add_integer (reply, "status", 0) &&
+ mrp_json_add_string_array(reply, "zones" , zones, nzone))
+ send_message(c, reply);
+
+ mrp_json_unref(reply);
+ mrp_free(zones);
+}
+
+
+static int parse_attributes(mrp_json_t *ja, mrp_attr_t *attrs, size_t max,
+ errbuf_t *e)
+{
+ mrp_json_iter_t it;
+ mrp_json_t *v;
+ const char *k;
+ mrp_attr_t *attr;
+ int nattr, cnt;
+
+ nattr = mrp_json_array_length(ja);
+
+ if (nattr >= (int)max)
+ return -error(e, EOVERFLOW, "too many attributes (%d > %d)", nattr,
+ max - 1);
+
+ cnt = 0;
+ attr = attrs;
+ mrp_json_foreach_member(ja, k, v, it) {
+ attr->name = k;
+
+ switch (mrp_json_get_type(v)) {
+ case MRP_JSON_STRING:
+ attr->type = mqi_string;
+ attr->value.string = mrp_json_string_value(v);
+ break;
+ case MRP_JSON_INTEGER:
+ attr->type = mqi_integer;
+ attr->value.integer = mrp_json_integer_value(v);
+ break;
+ case MRP_JSON_DOUBLE:
+ attr->type = mqi_floating;
+ attr->value.floating = mrp_json_double_value(v);
+ break;
+ case MRP_JSON_BOOLEAN:
+ attr->type = mqi_integer;
+ attr->value.integer = mrp_json_boolean_value(v);
+ break;
+ default:
+ return -error(e, EINVAL, "attribute '%s' with invalid type", k);
+ }
+
+ cnt++;
+ attr++;
+ }
+ attr->name = NULL;
+
+ return cnt;
+}
+
+
+static int append_attributes(mrp_json_t *o, mrp_attr_t *attrs, errbuf_t *e)
+{
+ mrp_json_t *a;
+ mrp_attr_t *attr;
+
+ if (attrs->name == NULL)
+ return 0;
+
+ a = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (a == NULL)
+ return error(e, ENOMEM, "failed to create attributes object");
+
+ for (attr = attrs; attr->name != NULL; attr++) {
+ switch (attr->type) {
+ case mqi_string:
+ if (!mrp_json_add_string(a, attr->name, attr->value.string))
+ goto fail;
+ break;
+ case mqi_integer:
+ if (!mrp_json_add_integer(a, attr->name, attr->value.integer))
+ goto fail;
+ break;
+ case mqi_unsignd:
+ if (!mrp_json_add_integer(a, attr->name, attr->value.integer))
+ goto fail;
+ break;
+ case mqi_floating:
+ if (!mrp_json_add_double(a, attr->name, attr->value.floating))
+ goto fail;
+ break;
+ default:
+ goto fail;
+ }
+ }
+
+ mrp_json_add(o, "attributes", a);
+ return 0;
+
+ fail:
+ mrp_json_unref(a);
+
+ return error(e, EINVAL, "failed to append attribtues");
+}
+
+
+static int parse_flags(mrp_json_t *arr, flagdef_t *defs, uint32_t *flagsp,
+ errbuf_t *e)
+{
+ const char *name;
+ flagdef_t *d;
+ int i, cnt;
+ uint32_t flags;
+
+ flags = 0;
+ cnt = mrp_json_array_length(arr);
+
+ for (i = 0; i < cnt; i++) {
+ if (mrp_json_array_get_string(arr, i, &name)) {
+ for (d = defs; d->name != NULL; d++)
+ if (!strcmp(d->name, name))
+ break;
+
+ if (d->name == NULL)
+ return -error(e, EINVAL, "unknown flag '%s'", name);
+
+ flags |= d->flag;
+ }
+ else
+ return -error(e, EINVAL, "flags must be strings");
+ }
+
+ *flagsp = flags;
+ return 0;
+}
+
+
+static int parse_resource_definition(mrp_json_t *jr, resdef_t *d, errbuf_t *e)
+{
+#define OPTIONAL 0x1
+#define SHARED 0x2
+ static flagdef_t res_flags[] = {
+ { "optional", 0x1 },
+ { "shared" , 0x2 },
+ { NULL, 0 }
+ };
+ mrp_json_t *jf, *ja;
+ uint32_t flags;
+
+ if (!mrp_json_get_string(jr, "name", &d->name))
+ return -error(e, EINVAL, "missing resource name");
+
+ if (mrp_json_get_array(jr, "flags", &jf)) {
+ if (parse_flags(jf, res_flags, &flags, e) != 0)
+ return e->err;
+ }
+ else {
+ if (errno != ENOENT)
+ return -error(e, EINVAL, "invalid resource flags");
+ else
+ flags = 0;
+ }
+
+ d->mand = !(flags & OPTIONAL);
+ d->share = flags & SHARED;
+
+ if (mrp_json_get_object(jr, "attributes", &ja)) {
+ d->nattr = parse_attributes(ja, d->attrs, MRP_ARRAY_SIZE(d->attrs), e);
+
+ if (d->nattr < 0)
+ return -e->err;
+ }
+ else {
+ if (errno != ENOENT)
+ return -error(e, EINVAL, "invalid resource attributes");
+ else {
+ d->attrs[0].name = NULL;
+ d->nattr = 0;
+ }
+ }
+
+ return 0;
+#undef OPTIONAL
+#undef SHARED
+}
+
+
+static int block_resource_set_events(wrt_client_t *c, mrp_resource_set_t *rset)
+{
+ if (c->rset == NULL) {
+ c->rset = rset;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int allow_resource_set_events(wrt_client_t *c, mrp_resource_set_t *rset)
+{
+ if (c->rset == rset) {
+ c->rset = NULL;
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static int resource_set_events_blocked(wrt_client_t *c, mrp_resource_set_t *rs)
+{
+ return c->rset == rs;
+}
+
+
+static void emit_resource_set_event(wrt_client_t *c, uint32_t reqid,
+ mrp_resource_set_t *rset, int force_all)
+{
+ const char *type = RESWRT_EVENT;
+ int seq = (int)reqid;
+ mrp_json_t *msg, *rarr, *r;
+ int rsid;
+ const char *state;
+ int grant, advice, all, mask;
+ errbuf_t e;
+ mrp_resource_t *res;
+ void *it;
+ const char *name;
+ mrp_attr_t attrs[ATTRIBUTE_MAX + 1];
+
+ mrp_debug("event for resource set %p of client %p", rset, c);
+
+ if (resource_set_events_blocked(c, rset)) {
+ mrp_debug("suppressing event for unacknowledged resource set");
+ return;
+ }
+
+ if (mrp_get_resource_set_state(rset) == mrp_resource_acquire)
+ state = RESWRT_STATE_GRANTED;
+ else
+ state = RESWRT_STATE_RELEASE;
+
+ rsid = (int)mrp_get_resource_set_id(rset);
+ grant = (int)mrp_get_resource_set_grant(rset);
+ advice = (int)mrp_get_resource_set_advice(rset);
+
+ msg = alloc_reply(type, seq);
+
+ if (msg == NULL)
+ return;
+
+ rarr = r = NULL;
+
+ if (mrp_json_add_integer(msg, "id" , rsid ) &&
+ mrp_json_add_string (msg, "state" , state) &&
+ mrp_json_add_integer(msg, "grant" , grant) &&
+ mrp_json_add_integer(msg, "advice", advice)) {
+
+ all = grant | advice;
+ it = NULL;
+
+ while ((res = mrp_resource_set_iterate_resources(rset, &it)) != NULL) {
+ mask = mrp_resource_get_mask(res);
+
+ if (!(mask & all) && !force_all)
+ continue;
+
+ name = mrp_resource_get_name(res);
+
+ if (!mrp_resource_read_all_attributes(res, ATTRIBUTE_MAX+1, attrs))
+ goto fail;
+
+ if (rarr == NULL) {
+ rarr = mrp_json_create(MRP_JSON_ARRAY);
+ if (rarr == NULL)
+ goto fail;
+ }
+
+ r = mrp_json_create(MRP_JSON_OBJECT);
+
+ if (r == NULL)
+ goto fail;
+
+ if (!mrp_json_add_string (r, "name", name) ||
+ (force_all && !mrp_json_add_integer(r, "mask", mask)))
+ goto fail;
+
+ if (append_attributes(r, attrs, &e) != 0)
+ goto fail;
+
+ if (!mrp_json_array_append(rarr, r))
+ goto fail;
+ else
+ r = NULL;
+ }
+
+ if (rarr != NULL)
+ mrp_json_add(msg, "resources", rarr);
+
+ send_message(c, msg);
+ mrp_json_unref(msg);
+
+ return;
+ }
+
+ fail:
+ mrp_json_unref(msg);
+ mrp_json_unref(rarr);
+ mrp_json_unref(r);
+}
+
+
+static void event_cb(uint32_t reqid, mrp_resource_set_t *rset, void *user_data)
+{
+ emit_resource_set_event((wrt_client_t *)user_data, reqid, rset, FALSE);
+}
+
+
+static void create_set(wrt_client_t *c, mrp_json_t *req)
+{
+ static flagdef_t set_flags[] = {
+ { "autorelease", TRUE },
+ { NULL, 0 }
+ };
+ const char *type = RESWRT_CREATE_SET;
+ int seq;
+ mrp_json_t *reply;
+ errbuf_t e;
+ mrp_json_t *jf, *jra, *jr;
+ uint32_t flags = 0, priority, rsid;
+ bool autorelease;
+ bool dontwait;
+ const char *appclass, *zone;
+ char attr[1024], *p;
+ resdef_t r;
+ int i, j, cnt, n, l;
+ mrp_resource_set_t *rset;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ /* parse resource set flags */
+ if (mrp_json_get_array(req, "flags", &jf)) {
+ if (parse_flags(jf, set_flags, &flags, &e) != 0) {
+ error_reply(c, type, seq, e.err, e.msg);
+ return;
+ }
+ }
+
+ autorelease = (flags != 0);
+ dontwait = false;
+ mrp_debug("autorelease: %s", autorelease ? "true" : "false");
+
+ /* dig out priority, class, and zone */
+ if (mrp_json_get_integer(req, "priority", &priority))
+ mrp_debug("priority: %u", priority);
+ else {
+ error_reply(c, type, seq, EINVAL, "missing or invalid 'priority'");
+ return;
+ }
+
+ if (mrp_json_get_string(req, "class", &appclass))
+ mrp_debug("class: '%s'", appclass);
+ else {
+ error_reply(c, type, seq, EINVAL, "missing or invalid 'class'");
+ return;
+ }
+
+ if (mrp_json_get_string(req, "zone", &zone))
+ mrp_debug("zone: '%s'", zone);
+ else {
+ error_reply(c, type, seq, EINVAL, "missing or invalid 'zone'");
+ return;
+ }
+
+ /* dig out resources */
+ if (!mrp_json_get_array(req, "resources", &jra) ||
+ (cnt = mrp_json_array_length(jra)) <= 0) {
+ error_reply(c, type, seq, EINVAL, "missing or invalid 'resources'");
+ return;
+ }
+
+ /* create a new resource set */
+ rset = mrp_resource_set_create(c->rsc, autorelease, dontwait,
+ priority, event_cb, c);
+
+ if (rset != NULL) {
+ rsid = mrp_get_resource_set_id(rset);
+
+ /* add resources to set */
+ for (i = 0; i < cnt; i++) {
+ if (mrp_json_array_get_object(jra, i, &jr)) {
+ if (parse_resource_definition(jr, &r, &e) != 0) {
+ ignore_invalid_request(c, req, e.msg);
+ goto fail;
+ }
+ else {
+ mrp_debug("resource '%s': %s %s", r.name,
+ r.mand ? "mandatory" : "optional",
+ r.share ? "shared" : "exclusive");
+
+ for (j = 0; j < r.nattr; j++) {
+ p = attr;
+ n = sizeof(attr);
+ l = snprintf(p, n, "'%s' = ", r.attrs[j].name);
+ p += l;
+ n -= l;
+
+ switch (r.attrs[j].type) {
+ case mqi_string:
+ l = snprintf(p, n, "'%s'", r.attrs[j].value.string);
+ p += l;
+ n -= l;
+ break;
+ case mqi_integer:
+ l = snprintf(p, n, "%d", r.attrs[j].value.integer);
+ p += l;
+ n -= l;
+ break;
+ case mqi_floating:
+ l = snprintf(p, n, "%f", r.attrs[j].value.floating);
+ p += l;
+ n -= l;
+ break;
+ default:
+ l = snprintf(p, n, "<unsupported type>");
+ p += l;
+ n -= l;
+ }
+
+ if (n > 0)
+ mrp_debug(" attribute %s", attr);
+ }
+
+ if (mrp_resource_set_add_resource(rset, r.name, r.share,
+ r.attrs, r.mand) < 0) {
+ error_reply(c, type, seq, EINVAL,
+ "failed to add resource %s to set", r.name);
+ goto fail;
+ }
+ }
+ }
+ }
+
+ block_resource_set_events(c, rset);
+ /* suppress events for this resource set (client does not know id) */
+ c->rset = rset;
+
+ /* add resource set to class/zone */
+ if (mrp_application_class_add_resource_set(appclass, zone, rset, seq)) {
+ error_reply(c, type, seq, EINVAL, "failed to add set to class");
+ goto fail;
+ }
+ else {
+ reply = alloc_reply(type, seq);
+
+ if (reply != NULL) {
+ if (mrp_json_add_integer(reply, "status",0) &&
+ mrp_json_add_integer(reply, "id", rsid)) {
+ send_message(c, reply);
+
+ allow_resource_set_events(c, rset);
+ emit_resource_set_event(c, seq, rset, TRUE);
+ }
+ }
+
+ mrp_json_unref(reply);
+
+ return;
+ }
+ }
+
+ fail:
+ mrp_resource_set_destroy(rset);
+}
+
+
+static void destroy_set(wrt_client_t *c, mrp_json_t *req)
+{
+ const char *type = RESWRT_DESTROY_SET;
+ int seq;
+ mrp_json_t *reply;
+ mrp_resource_set_t *rset;
+ uint32_t rsid;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ /* get resource set id */
+ if (!mrp_json_get_integer(req, "id", &rsid)) {
+ error_reply(c, type, seq, EINVAL, "missing id");
+ return;
+ }
+
+ rset = mrp_resource_client_find_set(c->rsc, rsid);
+
+ if (rset != NULL) {
+ reply = alloc_reply(type, seq);
+
+ if (reply != NULL) {
+ if (mrp_json_add_integer(reply, "status", 0))
+ send_message(c, reply);
+ }
+
+ mrp_resource_set_destroy(rset);
+ }
+ else
+ error_reply(c, type, seq, ENOENT, "resource set %d not found", rsid);
+}
+
+
+static void acquire_set(wrt_client_t *c, mrp_json_t *req)
+{
+ const char *type = RESWRT_ACQUIRE_SET;
+ int seq;
+ mrp_json_t *reply;
+ mrp_resource_set_t *rset;
+ uint32_t rsid;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ /* get resource set id */
+ if (!mrp_json_get_integer(req, "id", &rsid)) {
+ error_reply(c, type, seq, EINVAL, "missing id");
+ return;
+ }
+
+ rset = mrp_resource_client_find_set(c->rsc, rsid);
+
+ if (rset != NULL) {
+ reply = alloc_reply(type, seq);
+
+ if (reply != NULL) {
+ if (mrp_json_add_integer(reply, "status", 0))
+ send_message(c, reply);
+ }
+
+ mrp_resource_set_acquire(rset, (uint32_t)seq);
+ }
+ else
+ error_reply(c, type, seq, ENOENT, "resource set %d not found", rsid);
+}
+
+
+static void release_set(wrt_client_t *c, mrp_json_t *req)
+{
+ const char *type = RESWRT_RELEASE_SET;
+ int seq;
+ mrp_json_t *reply;
+ mrp_resource_set_t *rset;
+ uint32_t rsid;
+
+ if (!mrp_json_get_integer(req, "seq", &seq)) {
+ ignore_invalid_request(c, req, "missing 'seq' field");
+ return;
+ }
+
+ /* get resource set id */
+ if (!mrp_json_get_integer(req, "id", &rsid)) {
+ error_reply(c, type, seq, EINVAL, "missing id");
+ return;
+ }
+
+ rset = mrp_resource_client_find_set(c->rsc, rsid);
+
+ if (rset != NULL) {
+ reply = alloc_reply(type, seq);
+
+ if (reply != NULL) {
+ if (mrp_json_add_integer(reply, "status", 0))
+ send_message(c, reply);
+ }
+
+ mrp_resource_set_release(rset, (uint32_t)seq);
+ }
+ else
+ error_reply(c, type, seq, ENOENT, "resource set %d not found", rsid);
+}
+
+
+static wrt_client_t *create_client(wrt_data_t *data, mrp_transport_t *lt)
+{
+ wrt_client_t *c;
+ char name[64];
+
+ c = mrp_allocz(sizeof(*c));
+
+ if (c != NULL) {
+ mrp_list_init(&c->hook);
+
+ c->t = mrp_transport_accept(lt, c, MRP_TRANSPORT_REUSEADDR);
+
+ if (c->t != NULL) {
+ snprintf(name, sizeof(name), "wrt-client%d", data->id++);
+ c->rsc = mrp_resource_client_create(name, c);
+
+ if (c->rsc != NULL) {
+ mrp_list_append(&data->clients, &c->hook);
+
+ return c;
+ }
+
+ mrp_transport_destroy(c->t);
+ }
+
+ mrp_free(c);
+ }
+
+ return NULL;
+}
+
+
+static void destroy_client(wrt_client_t *c)
+{
+ if (c != NULL) {
+ mrp_list_delete(&c->hook);
+
+ mrp_transport_disconnect(c->t);
+ mrp_transport_destroy(c->t);
+ mrp_resource_client_destroy(c->rsc);
+
+ mrp_free(c);
+ }
+}
+
+
+static void connection_evt(mrp_transport_t *lt, void *user_data)
+{
+ wrt_data_t *data = (wrt_data_t *)user_data;
+ wrt_client_t *c;
+
+ c = create_client(data, lt);
+
+ if (c != NULL)
+ mrp_log_info("Accepted WRT resource client connection.");
+ else
+ mrp_log_error("Failed to accept WRT resource client connection.");
+}
+
+
+static void closed_evt(mrp_transport_t *t, int error, void *user_data)
+{
+ wrt_client_t *c = (wrt_client_t *)user_data;
+
+ MRP_UNUSED(t);
+
+ if (error != 0)
+ mrp_log_error("WRT resource connection closed with error %d (%s).",
+ error, strerror(error));
+ else
+ mrp_log_info("WRT resource connection closed.");
+
+ destroy_client(c);
+}
+
+
+static int send_message(wrt_client_t *c, mrp_json_t *msg)
+{
+ const char *s;
+
+ s = mrp_json_object_to_string(msg);
+
+ mrp_log_info("sending WRT resource message:");
+ mrp_log_info(" %s", s);
+
+ return mrp_transport_sendcustom(c->t, msg);
+}
+
+
+static void recv_evt(mrp_transport_t *t, void *data, void *user_data)
+{
+ wrt_client_t *c = (wrt_client_t *)user_data;
+ mrp_json_t *req = (mrp_json_t *)data;
+ const char *type;
+ int seq;
+ const char *s;
+
+ MRP_UNUSED(t);
+
+ s = mrp_json_object_to_string((mrp_json_t *)data);
+
+ mrp_log_info("recived WRT resource message:");
+ mrp_log_info(" %s", s);
+
+ if (!mrp_json_get_string (req, "type", &type) ||
+ !mrp_json_get_integer(req, "seq" , &seq))
+ ignore_invalid_request(c, req);
+ else {
+ if (seq < c->seq) {
+ mrp_log_info("ignoring out-of-date request");
+ return;
+ }
+ else
+ c->seq = seq;
+
+ if (!strcmp(type, RESWRT_QUERY_RESOURCES))
+ query_resources(c, req);
+ else if (!strcmp(type, RESWRT_QUERY_CLASSES))
+ query_classes(c, req);
+ else if (!strcmp(type, RESWRT_QUERY_ZONES))
+ query_zones(c, req);
+ else if (!strcmp(type, RESWRT_CREATE_SET))
+ create_set(c, req);
+ else if (!strcmp(type, RESWRT_DESTROY_SET))
+ destroy_set(c, req);
+ else if (!strcmp(type, RESWRT_ACQUIRE_SET))
+ acquire_set(c, req);
+ else if (!strcmp(type, RESWRT_RELEASE_SET))
+ release_set(c, req);
+ else
+ ignore_unknown_request(c, req, type);
+ }
+}
+
+
+static int transport_create(wrt_data_t *data)
+{
+ static mrp_transport_evt_t evt = {
+ { .recvcustom = recv_evt },
+ { .recvcustomfrom = NULL },
+ .connection = connection_evt,
+ .closed = closed_evt,
+ };
+
+ mrp_mainloop_t *ml = data->ctx->ml;
+ const char *root = data->httpdir;
+ const char *cert = data->sslcert;
+ const char *pkey = data->sslpkey;
+ const char *ca = data->sslca;
+
+ mrp_sockaddr_t addr;
+ socklen_t len;
+ const char *type, *opt, *val;
+ int flags;
+
+ len = mrp_transport_resolve(NULL, data->addr, &addr, sizeof(addr), &type);
+
+ if (len > 0) {
+ flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_MODE_CUSTOM;
+ data->lt = mrp_transport_create(ml, type, &evt, data, flags);
+
+ if (data->lt != NULL) {
+ if (cert || pkey || ca) {
+ mrp_transport_setopt(data->lt, MRP_WSCK_OPT_SSL_CERT, cert);
+ mrp_transport_setopt(data->lt, MRP_WSCK_OPT_SSL_PKEY, pkey);
+ mrp_transport_setopt(data->lt, MRP_WSCK_OPT_SSL_CA , ca);
+ }
+
+ if (mrp_transport_bind(data->lt, &addr, len) &&
+ mrp_transport_listen(data->lt, 0)) {
+ mrp_log_info("Listening on transport '%s'...", data->addr);
+
+ opt = MRP_WSCK_OPT_SENDMODE;
+ val = MRP_WSCK_SENDMODE_TEXT;
+ mrp_transport_setopt(data->lt, opt, val);
+ mrp_transport_setopt(data->lt, MRP_WSCK_OPT_HTTPDIR, root);
+
+ return TRUE;
+ }
+
+ mrp_transport_destroy(data->lt);
+ data->lt = NULL;
+ }
+ }
+ else
+ mrp_log_error("Failed to resolve transport address '%s'.", data->addr);
+
+ return FALSE;
+}
+
+
+static void transport_destroy(wrt_data_t *data)
+{
+ mrp_transport_destroy(data->lt);
+}
+
+
+static int plugin_init(mrp_plugin_t *plugin)
+{
+ wrt_data_t *data;
+
+ data = mrp_allocz(sizeof(*data));
+
+ if (data != NULL) {
+ mrp_list_init(&data->clients);
+
+ data->id = 1;
+ data->ctx = plugin->ctx;
+ data->addr = plugin->args[ARG_ADDRESS].str;
+ data->httpdir = plugin->args[ARG_HTTPDIR].str;
+ data->sslcert = plugin->args[ARG_SSLCERT].str;
+ data->sslpkey = plugin->args[ARG_SSLPKEY].str;
+ data->sslca = plugin->args[ARG_SSLCA].str;
+
+ if (!transport_create(data))
+ goto fail;
+
+ return TRUE;
+ }
+
+
+ fail:
+ if (data != NULL) {
+ transport_destroy(data);
+
+ mrp_free(data);
+ }
+
+ return FALSE;
+}
+
+
+static void plugin_exit(mrp_plugin_t *plugin)
+{
+ wrt_data_t *data = (wrt_data_t *)plugin->data;
+
+ transport_destroy(data);
+
+ mrp_free(data);
+}
+
+
+#define PLUGIN_DESCRIPTION "Murphy resource Web runtime bridge plugin."
+#define PLUGIN_HELP "Murphy resource protocol for web-runtimes."
+#define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
+#define PLUGIN_VERSION MRP_VERSION_INT(0, 0, 1)
+
+static mrp_plugin_arg_t plugin_args[] = {
+ MRP_PLUGIN_ARGIDX(ARG_ADDRESS, STRING, "address", DEFAULT_ADDRESS),
+ MRP_PLUGIN_ARGIDX(ARG_HTTPDIR, STRING, "httpdir", DEFAULT_HTTPDIR),
+ MRP_PLUGIN_ARGIDX(ARG_SSLCERT, STRING, "sslcert", NULL),
+ MRP_PLUGIN_ARGIDX(ARG_SSLPKEY, STRING, "sslpkey", NULL),
+ MRP_PLUGIN_ARGIDX(ARG_SSLCA , STRING, "sslca" , NULL)
+
+};
+
+MURPHY_REGISTER_PLUGIN("resource-wrt",
+ PLUGIN_VERSION, PLUGIN_DESCRIPTION, PLUGIN_AUTHORS,
+ PLUGIN_HELP, MRP_SINGLETON, plugin_init, plugin_exit,
+ plugin_args, MRP_ARRAY_SIZE(plugin_args),
+ NULL, 0,
+ NULL, 0,
+ NULL);
--- /dev/null
+
+
+/*
+ * debugging
+ */
+
+var WRT_MGR = 0; /* resource manager debugging */
+var WRT_MSG = 1; /* message debugging */
+var WRT_SET = 2; /* resource set debugging */
+
+var wrt_debug_names = [ 'MGR', 'MSG', 'SET'];
+var wrt_debug_mask = 0x0;
+
+
+function wrt_debug_index_of(name) {
+ for (var idx in wrt_debug_names) {
+ if (wrt_debug_names[idx] == name)
+ return idx;
+ }
+
+ return -1;
+}
+
+
+function wrt_debug_mask_of(name) {
+ var idx = wrt_debug_index_of(name);
+
+ if (idx >= 0)
+ return (1 << wrt_debug_index_of(name));
+ else
+ return 0;
+}
+
+
+function wrt_debug_set(flags, enable) {
+ var mask = 0;
+ var flag;
+
+ if (typeof flags == typeof "" || typeof flags == typeof 1)
+ flags = [ flags ];
+
+ for (var idx in flags) {
+ flag = flags[idx];
+
+ if (typeof flag == typeof "")
+ mask |= wrt_debug_mask_of(flag);
+ else
+ mask |= (1 << flag);
+ }
+
+ if (enable)
+ wrt_debug_mask |= mask;
+ else
+ wrt_debug_mask &= ~mask;
+}
+
+
+function wrt_debug_enable (flags) {
+ wrt_debug_set(flags, true);
+}
+
+
+function wrt_debug_disable(flags) {
+ wrt_debug_set(flags, false);
+}
+
+
+function wrt_debug() {
+ var flag, msg, i;
+
+ if (arguments.length >= 2) {
+ flag = arguments[0];
+ mask = (1 << flag);
+
+ if (!(mask & wrt_debug_mask))
+ return;
+
+ flag = wrt_debug_names[flag];
+
+ for (i = 1, msg = ""; i < arguments.length; i++) {
+ msg += arguments[i];
+ }
+
+ console.log("D: [" + flag + "] " + msg);
+ }
+ else {
+ if (arguments.length == 1)
+ console.log("D: [ALL] " + arguments[0]);
+ }
+}
+
+
+/*
+ * our custom error type
+ */
+
+function WrtResourceError(message) {
+ this.name = "Resource Error";
+ this.message = message;
+}
+
+
+/*
+ * resource manager
+ */
+
+WrtResourceManager.prototype.reset = function () {
+ this.connected = false; /* no connection */
+ this.server = null; /* no server */
+ this.sck = null; /* no socket */
+ this.reqno = 1; /* next request sequence number */
+ this.reqq = []; /* empty request queue */
+ this.sets = []; /* no resource sets */
+
+ this.onconnect = null; /* clear connection callback */
+ this.ondisconnect = null; /* clear disconnect callback */
+ this.onfailed = null;
+}
+
+
+/** Ensure we have a connection, throw an error if we don't. */
+WrtResourceManager.prototype.check_connection = function () {
+ if (!this.connected)
+ throw new WrtResourceError("not connected");
+}
+
+
+/** Event handler for server socket connection. */
+WrtResourceManager.prototype.sckopen = function () {
+ var mgr = this.manager;
+
+ wrt_debug(WRT_MGR, "connected to server " + mgr.server);
+ wrt_debug(WRT_MGR, "mgr.sck = " + mgr.sck);
+
+ mgr.connected = true;
+
+ if (mgr.onconnect) /* notify listener if any */
+ mgr.onconnect();
+}
+
+
+/** Event handler for server socket disconnection. */
+WrtResourceManager.prototype.sckclose = function () {
+ var mgr = this.manager;
+
+ wrt_debug(WRT_MGR, "disconnected from server");
+
+ mgr.connected = false;
+
+ if (mgr.ondisconnect) /* notify listener if any */
+ mgr.ondisconnect();
+
+ mgr.reset();
+}
+
+
+/** Event handler for server socket connection error. */
+WrtResourceManager.prototype.sckerror = function () {
+ var mgr = this.manager;
+
+ wrt_debug(WRT_MGR, "failed to connect to server " + mgr.server);
+
+ if (mgr.onfailed) { /* notify listener if any */
+ mgr.ondisconnect = null; /* only call error handler */
+ mgr.onfailed();
+ }
+}
+
+
+/** Event handler for incoming message. */
+WrtResourceManager.prototype.sckmessage = function (message) {
+ var mgr = this.manager;
+ var msg = JSON.parse(message.data);
+ var seq = msg.seq;
+ var pending, rset;
+
+ wrt_debug(WRT_MSG, "received ", msg.type, " message (#", seq, ")");
+
+ if (msg.type == 'event') {
+ rset = mgr.sets[msg.id];
+
+ if (rset) {
+ delete mgr.reqq[seq];
+ rset.notify(msg);
+ }
+ }
+ else {
+ pending = mgr.reqq[seq];
+
+ if (pending) {
+ delete mgr.reqq[seq];
+
+ pending.notify(msg);
+ }
+ }
+}
+
+
+/** Resource set constructor. */
+function WrtResourceSet (mgr, reqno) {
+ this.manager = mgr;
+ this.reqno = reqno;
+}
+
+
+/** Deliver resource set event notification. */
+WrtResourceSet.prototype.notify = function (msg) {
+ var type = msg.type;
+
+ if (type == 'event') {
+ wrt_debug(WRT_MSG, "resource set notification...");
+
+ if (!this.resources)
+ this.resources = msg.resources;
+
+ this.state = msg.state;
+ this.grant = msg.grant;
+ this.advice = msg.advice;
+
+ if (this.onstatechanged)
+ this.onstatechanged(this.grant);
+ }
+ else if (type == 'create') {
+ var status = msg.status;
+ var mgr = this.manager;
+
+ if (status == 0) {
+ this.id = msg.id;
+ mgr.sets[this.id] = this;
+
+ if (this.onsuccess)
+ this.onsuccess();
+ }
+ else if (this.onerror) {
+ var error = {
+ error: msg.error ? msg.error : 1,
+ message: msg.message ? msg.message : "<server-side error>"
+ };
+
+ this.onerror(error);
+ }
+ }
+}
+
+
+/** Map resources to names. */
+WrtResourceSet.prototype.ensure_resource_map = function () {
+ var r;
+
+ if (this.resource_by_name)
+ return;
+
+ if (!this.resources)
+ throw new WrtError("resources/masks not known yet");
+
+ this.resource_by_name = {};
+
+ for (var i in this.resources) {
+ r = this.resources[i];
+ this.resource_by_name[r.name] = {
+ mask: r.mask,
+ attributes: r.attributes
+ }
+ }
+}
+
+
+/** Pending request constructor. */
+function WrtPendingRequest (mgr, reqno) {
+ this.manager = mgr;
+ this.reqno = reqno;
+}
+
+
+WrtPendingRequest.prototype.map_resources = function (e) {
+ var i, r, m;
+
+ m = {};
+ for (i in e) {
+ r = e[i];
+
+ if (r.attributes)
+ m[r.name] = r.attributes;
+ else
+ m[r.name] = {};
+ }
+
+ return m;
+}
+
+
+/** Deliver pending request reply notification.*/
+WrtPendingRequest.prototype.notify = function (msg) {
+ var evtmap = {
+ 'query-classes' : { field: 'classes' },
+ 'query-zones' : { field: 'zones' },
+ 'query-resources': { field: 'resources', map: this.map_resources }
+ };
+ var status = msg.status;
+ var event, m;
+
+ if (status == 0) {
+ if (this.onsuccess && (m = evtmap[msg.type])) {
+ event = m.map ? m.map(msg[m.field]) : msg[m.field];
+ this.onsuccess(event);
+ }
+ }
+ else {
+ if (this.onerror) {
+ event = {
+ error: msg.error ? msg.error : 666,
+ message: msg.message ? msg.message : "<server-side error>"
+ };
+ this.onerror(event);
+ }
+ }
+}
+
+
+/** Send a request to the server, return a pending request object for it. */
+WrtResourceManager.prototype.send_request = function (req) {
+ var pending, msg, seq;
+
+ seq = this.reqno++;
+
+ wrt_debug(WRT_MSG, "sending ", req.type, " request (#", seq, ")");
+
+ if (req.type == 'create')
+ pending = new WrtResourceSet(this, seq);
+ else
+ pending = new WrtPendingRequest(this, seq);
+
+ req.seq = seq;
+ pending.req = req;
+ this.reqq[seq] = pending;
+
+ this.sck.send(JSON.stringify(pending.req));
+
+ return pending;
+}
+
+
+/*
+ * Public Resource Manager API
+ */
+
+/** Resource Manager constructor. */
+function WrtResourceManager() {
+ this.reset();
+}
+
+
+/** Initiate connection to the given server. */
+WrtResourceManager.prototype.connect = function (server) {
+ if (this.connected)
+ throw new WrtResourceError("already connected");
+ else {
+ wrt_debug(WRT_MGR, "trying to connect to " + server);
+
+ this.server = server
+
+ if (typeof MozWebSocket != "undefined")
+ this.sck = new MozWebSocket(this.server, "murphy");
+ else
+ this.sck = new WebSocket(this.server, "murphy");
+
+ this.sck.manager = this;
+ this.sck.onopen = this.sckopen;
+ this.sck.onclose = this.sckclose;
+ this.sck.onerror = this.sckerror;
+ this.sck.onmessage = this.sckmessage;
+ }
+}
+
+
+/** Disconnect from the server. */
+WrtResourceManager.prototype.disconnect = function () {
+ this.check_connection();
+
+ wrt_debug(WRT_MGR, "disconnecting from " + this.server);
+
+ this.sck.close();
+ delete this.sck;
+}
+
+
+/** Initiate an application class name query. */
+WrtResourceManager.prototype.queryApplicationClassNames = function () {
+ this.check_connection();
+
+ wrt_debug(WRT_MGR, "initiating application class query");
+
+ return this.send_request({ type: 'query-classes'});
+}
+
+
+/** Initiate zone name query. */
+WrtResourceManager.prototype.queryZoneNames = function () {
+ this.check_connection();
+
+ wrt_debug(WRT_MGR, "initiating zone query");
+
+ return this.send_request({ type: 'query-zones' });
+}
+
+
+/** Initiate resource definition query. */
+WrtResourceManager.prototype.queryResourceDefinitions = function () {
+ this.check_connection();
+
+ wrt_debug(WRT_MGR, "initiating resource query");
+
+ return this.send_request({ type: 'query-resources'});
+}
+
+
+/** Create a new resource set. */
+WrtResourceManager.prototype.createResourceSet = function (resources, options) {
+ var appClass, zone, priority, flags;
+ var req;
+
+ this.check_connection();
+
+ wrt_debug(WRT_MGR, "creating new resource set");
+
+ appClass = options.class ? options.class : "player";
+ zone = options.zone ? options.zone : "driver";
+ priority = options.priority ? options.priority : 0;
+
+ return this.send_request({ type: 'create',
+ class: appClass, zone: zone, priority: priority,
+ resources: resources });
+}
+
+/** Determine a WebSocket URI based on an HTTP URI. */
+WrtResourceManager.prototype.socketUri = function (http_uri) {
+ var proto, colon, rest;
+
+ colon = http_uri.indexOf(':'); /* get first colon */
+ proto = http_uri.substring(0, colon); /* get protocol */
+ rest = http_uri.substring(colon + 3); /* get URI sans protocol:// */
+ addr = rest.split("/")[0]; /* strip URI path from address */
+
+ switch (proto) {
+ case "http": return "ws://" + addr;
+ case "https": return "wss://" + addr;
+ default: return null;
+ }
+}
+
+/** Acquire the resource set. */
+WrtResourceSet.prototype.acquire = function () {
+ this.manager.send_request({ type: 'acquire', id: this.id });
+}
+
+
+/** Release the resource set. */
+WrtResourceSet.prototype.release = function () {
+ this.manager.send_request({ type: 'release', id: this.id });
+}
+
+
+/** Get the mask of granted resources. */
+WrtResourceSet.prototype.getGrantedMask = function () {
+ return this.grant;
+}
+
+
+/** Get the mask of allocable resources. */
+WrtResourceSet.prototype.getAllocableMask = function () {
+ return this.advice;
+}
+
+
+/** Get resource mask by resource name. */
+WrtResourceSet.prototype.getMaskByResourceName = function (name) {
+ var r;
+
+ this.ensure_resource_map();
+
+ if ((r = this.resource_by_name[name]))
+ return r.mask;
+ else
+ return 0;
+}
+
+
+/** Get resource attributes by name. */
+WrtResourceSet.prototype.getAttributesByResourceName = function (name) {
+ var r;
+
+ this.ensure_resource_map();
+
+ if ((r = this.resource_by_name[name]))
+ return r.attributes;
+ else
+ return {};
+}
+
+
+/** Get the names of all resources in the set. */
+WrtResourceSet.prototype.getResourceNames = function () {
+ var names, i;
+
+ names = [];
+ for (var i in this.resources) {
+ names.push(this.resources[i].name);
+ }
+
+ return names;
+}
+
+
+/** Get the names of granted resources in the set. */
+WrtResourceSet.prototype.getGrantedResourceNames = function () {
+ var names, n;
+
+ this.ensure_resource_map();
+
+ names = [];
+
+ for (var n in this.resource_by_name) {
+ r = this.resource_by_name[n];
+ if (this.grant & r.mask)
+ names.push(n);
+ }
+
+ return names;
+}
+
+
+/** Get the names of granted resources in the set. */
+WrtResourceSet.prototype.getAllocableResourceNames = function () {
+ var names, n;
+
+ this.ensure_resource_map();
+
+ names = [];
+
+ for (var n in this.resource_by_name) {
+ r = this.resource_by_name[n];
+ if (this.advice & r.mask)
+ names.push(n);
+ }
+
+ return names;
+}
+
+
+/** Check if the named resource has been granted. */
+WrtResourceSet.prototype.isGranted = function (name) {
+ this.ensure_resource_map();
+
+ r = this.resource_by_name[name];
+
+ if (this.grant & r.mask)
+ return true;
+ else
+ return false;
+}
+
+
+/** Check if the named resource can be allocated. */
+WrtResourceSet.prototype.isAllocable = function (name) {
+ this.ensure_resource_map();
+
+ r = this.resource_by_name[name];
+
+ if (this.advice & r.mask)
+ return true;
+ else
+ return false;
+}
+
+
+/** Check if the set has been released. */
+WrtResourceSet.prototype.isReleased = function () {
+ return (this.state == 'release');
+}
+
+
+/** Check if the set has been pre-empted. */
+WrtResourceSet.prototype.isPreempted = function () {
+ return (this.state == 'acquire' && this.grant == 0);
+}
--- /dev/null
+<html lang="en">
+<head> <meta charset=utf-8 /> <title>Resource-Webruntime Test</title>
+ <script src="resource-api.js"></script>
+ <script>
+
+var ctx;
+
+
+function appClassQueryReplied (classes) {
+ console.log("Application classes: " + classes);
+}
+
+function appClassQueryFailed (error) {
+ console.log("Application class query failed: " +
+ error.error, + " (" + error.message + ")");
+}
+
+
+function zoneNameQueryReplied (zones) {
+ console.log("Zones: " + zones);
+}
+
+function zoneNameQueryFailed (error) {
+ console.log("Zone query failed: " +
+ error.error, + " (" + error.message + ")");
+}
+
+
+function resourceDefinitionQueryReplied (resources) {
+ var name, r, attributes, a;
+
+ console.log("Received resource definitions:: ");
+
+ for (name in resources) {
+ console.log(" resource " + name);
+
+ attributes = resources[name];
+
+ for (a in attributes) {
+ console.log(" attribute " + a + " = " + attributes[a]);
+ }
+ }
+}
+
+function resourceDefinitionQueryFailed (error) {
+ console.log("Resource definition query failed: " +
+ error.error, + " (" + error.message + ")");
+}
+
+
+function resourceSetCreated () {
+ ctx.rset = this;
+
+ console.log("Resource set created");
+ setStatus("resource set created");
+}
+
+function resourceSetFailed (error) {
+ console.log("Resource set creation failed: " +
+ error.error + " (" + error.message + ")");
+ setStatus("resource set failed");
+}
+
+function resourceSetEvent (mask) {
+ var names, mask, attributes, grant, advice, gnames, anames;
+
+ console.log("Resource set event: granted resource mask = " + mask);
+
+ names = this.getResourceNames();
+ anames = this.getAllocableResourceNames();
+ gnames = this.getGrantedResourceNames();
+
+ console.log("All resources: " + names);
+ console.log("Granted resources: " + gnames);
+ console.log("Allocable resources: " + anames);
+
+ for (var i in names) {
+ console.log("mask of " + names[i] + ": " +
+ this.getMaskByResourceName(names[i]));
+
+ if ((attributes = this.getAttributesByResourceName(names[i])))
+ console.log("attributes of " + names[i] + ": " + attributes);
+
+ console.log("isGranted: " + this.isGranted(names[i]));
+ console.log("isAllocable: " + this.isAllocable(names[i]));
+ }
+
+ if (mask == 0) {
+ setStatus(this.isPreempted ? "lost" : "released");
+ }
+ else
+ setStatus("acquired");
+
+ names = this.getResourceNames();
+ grant = this.getGrantedMask();
+ advice = this.getAllocableMask();
+
+ setContent('grant', grant);
+ setContent('advice', advice);
+ setContent('granted', gnames);
+ setContent('allocable', anames);
+
+ if (this.isGranted("audio_playback"))
+ playAudio();
+ else
+ stopAudio();
+}
+
+
+function managerConnected () {
+ var ctx = this.user_data;
+ var mgr = ctx.mgr;
+ var aqry, zqry, rqry, rset;
+
+ console.log("Manager connection up...");
+
+ setStatus("connected")
+
+ disableButton("connect");
+ enableButton("disconnect")
+ enableButton("acquire")
+ enableButton("release")
+
+
+ try {
+ aqry = mgr.queryApplicationClassNames();
+ aqry.onsuccess = appClassQueryReplied;
+ aqry.onerror = appClassQueryFailed;
+
+ zqry = mgr.queryZoneNames();
+ zqry.onsuccess = zoneNameQueryReplied;
+ zqry.onerror = zoneNameQueryFailed;
+
+ rqry = mgr.queryResourceDefinitions();
+ rqry.onsuccess = resourceDefinitionQueryReplied;
+ rqry.onerror = resourceDefinitionQueryFailed;
+
+ var resources = [
+ {
+ name: 'audio_playback', attributes: { role: 'media' }
+ },
+ {
+ name: 'video_playback', flags: ['optional']
+ }
+ ];
+ var options = { priority: 0, 'class': 'player' };
+
+ setStatus("creating resource set");
+ rset = mgr.createResourceSet(resources, options);
+ rset.onsuccess = resourceSetCreated;
+ rset.onerror = resourceSetFailed;
+ rset.onstatechanged = resourceSetEvent;
+ }
+ catch (e) {
+ console.log("Query failed: " +
+ e.error + "(" + e.message + ")");
+ }
+}
+
+
+function managerDisconnected () {
+ console.log("Manager connection down...");
+ setStatus("disconnected");
+
+ enableButton("connect");
+ disableButton("disconnect")
+ disableButton("acquire")
+ disableButton("release")
+
+ setContent('grant', "");
+ setContent('advice', "");
+ setContent('granted', "");
+ setContent('allocable', "");
+}
+
+
+function managerFailed () {
+ console.log("Manager failed to connect...");
+ setStatus("failed to connect");
+}
+
+
+function runtest () {
+ var mgr;
+
+ wrt_debug_enable([WRT_MGR, WRT_MSG]);
+
+ ctx = {};
+ mgr = new WrtResourceManager();
+
+ mgr.onconnect = managerConnected;
+ mgr.ondisconnect = managerDisconnected;
+ mgr.onfailed = managerFailed;
+ mgr.user_data = ctx;
+
+ ctx.mgr = mgr;
+
+ mgr.connect(mgr.resourceUri(document.URL));
+}
+
+
+function setContent(id, text) {
+ var elem = document.getElementById(id);
+
+ if (elem)
+ elem.textContent = text;
+}
+
+
+function setStatus(status) {
+ setContent('status', status);
+}
+
+
+function enableButton(name) {
+ var btn = document.getElementById(name);
+
+ if (btn)
+ btn.disabled = false;
+}
+
+
+function disableButton(name) {
+ var btn = document.getElementById(name);
+
+ if (btn)
+ btn.disabled = true;
+}
+
+
+function initialize() {
+ disableButton("disconnect");
+ disableButton("acquire");
+ disableButton("release");
+ enableButton("connect");
+}
+
+
+function connect() {
+ var mgr;
+
+ setStatus("connecting...");
+
+ wrt_debug_enable([WRT_MGR, WRT_MSG]);
+
+ ctx = {};
+ mgr = new WrtResourceManager();
+
+ mgr.onconnect = managerConnected;
+ mgr.ondisconnect = managerDisconnected;
+ mgr.onfailed = managerFailed;
+ mgr.user_data = ctx;
+
+ ctx.mgr = mgr;
+
+ console.log("server URI: " + mgr.socketUri(document.URL));
+ mgr.connect(mgr.socketUri(document.URL));
+}
+
+
+function disconnect() {
+ var mgr = ctx.mgr;
+
+ setStatus("disconnecting...");
+
+ mgr.disconnect();
+}
+
+
+function acquire() {
+ setStatus("acquiring...");
+
+ ctx.rset.acquire();
+}
+
+
+function release() {
+ setStatus("releasing...");
+
+ ctx.rset.release();
+}
+
+function fileSelected() {
+ var elem = document.getElementById('filepath');
+
+ console.log("File selected: " + elem.value);
+}
+
+
+function audioError(event) {
+ /*console.log('error:' + event.target.error.code);
+ setStatus("audio path: not valid");*/
+}
+
+
+function checkAudio() {
+ var audio = document.getElementById('audio');
+
+ if (audio.networkState == 1)
+ setStatus("audio path: OK");
+ else
+ setStatus("audio path: invalid");
+}
+
+
+function checkPath() {
+ var txt = document.getElementById('filepath');
+ var audio = document.getElementById('audio');
+ var path = "http://127.0.0.1/" + txt.value;
+
+ console.log("trying " + path);
+
+ audio.src = path;
+
+ if (audio.checkTimer)
+ window.clearTimeout(audio.checkTimer);
+
+ audio.checkTimer = window.setTimeout(function() { checkAudio(); }, 250);
+ audio.load();
+}
+
+
+function playAudio() {
+ var audio = document.getElementById('audio');
+
+ if (audio.networkState == 1)
+ audio.play();
+}
+
+
+function stopAudio() {
+ var audio = document.getElementById('audio');
+
+ audio.pause();
+}
+
+
+</script>
+
+</head>
+
+<body onload="/*runtest();*/ initialize();">
+
+
+<table>
+ <tr>
+ <th align=left>Audio File:</th>
+ <td align=left><input id=filepath type="text" accept="audio/*"
+ onchange="fileSelected();"
+ onkeyup="checkPath();"></td>
+ </tr>
+ <tr>
+ <th align=left>Actions:</th>
+ <td align=left>
+ <input type=button id=connect value="Connect"
+ onclick="connect();">
+ <input type=button id=disconnect value="Disconnect"
+ onclick="disconnect();" disabled>
+ <input type=button id=acquire value="Acquire"
+ onclick="acquire();" disabled>
+ <input type=button id=release value="Release"
+ onclick="release();" disabled>
+ </td>
+ </tr>
+ <tr>
+ <th align=left>Status:</th>
+ <td id=statush align=left><div id=status>disconnected</div></td>
+ </tr>
+ <tr>
+ <th align=left>grant mask:</th>
+ <td id=granth align=left><div id=grant></div></td>
+ </tr>
+ <tr>
+ <th align=left>advice mask:</th>
+ <td id=adviceh align=left><div id=advice></div></td>
+ </tr>
+ <tr>
+ <th align=left>granted:</th>
+ <td id=grantedh align=left><div id=granted></div></td>
+ </tr>
+ <tr>
+ <th align=left>allocable:</th>
+ <td id=allocableh align=left><div id=allocable></div></td>
+ </tr>
+
+ <tr>
+ <td colspan=2 width=500 align=center style="background-color: #e0e0e0;"><div id=wslm_drawing> </div></td>
+ </tr>
+ <tr>
+ <td>
+ <audio id=audio preload=metadata onerror="audioError(event);">
+ </audio>
+ <td>
+ </tr>
+</table>
+
+
+</body>
+</html>
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_WRT_H__
+#define __MURPHY_RESOURCE_WRT_H__
+
+#define RESWRT_QUERY_CLASSES "query-classes"
+#define RESWRT_QUERY_ZONES "query-zones"
+#define RESWRT_QUERY_RESOURCES "query-resources"
+
+#define RESWRT_CREATE_SET "create"
+#define RESWRT_DESTROY_SET "destroy"
+#define RESWRT_ACQUIRE_SET "acquire"
+#define RESWRT_RELEASE_SET "release"
+
+#define RESWRT_EVENT "event"
+#define RESWRT_STATE_GRANTED "acquire"
+#define RESWRT_STATE_RELEASE "release"
+
+#endif /* __MURPHY_RESOURCE_WRT_H__ */
--- /dev/null
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
+
+noinst_PROGRAMS =
+
--- /dev/null
+#include <murphy/core/console.h>
+#include <murphy/resolver/resolver.h>
+#include <murphy/resolver/target.h>
+
+static void dump(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ mrp_context_t *ctx = c->ctx;
+
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ if (ctx->r != NULL) {
+ mrp_resolver_dump_facts(ctx->r, c->stdout);
+ mrp_resolver_dump_targets(ctx->r, c->stdout);
+ }
+}
+
+static void dot(mrp_console_t *c, void *user_data, int argc, char **argv)
+{
+ mrp_context_t *ctx = c->ctx;
+
+ MRP_UNUSED(user_data);
+ MRP_UNUSED(argc);
+ MRP_UNUSED(argv);
+
+ if (ctx->r != NULL) {
+ mrp_resolver_dump_dot_graph(ctx->r, c->stdout);
+ }
+}
+
+#define RESOLVER_DESCRIPTION \
+ "Resolver commands provide runtime diagnostics and debugging for\n" \
+ "the Murphy resolver.\n"
+
+#define DUMP_SYNTAX "dump"
+#define DUMP_SUMMARY "dump the resolver facts and targets"
+#define DUMP_DESCRIPTION \
+ "Dump the resolver facts and targets.\n"
+
+#define DOT_SYNTAX "dot"
+#define DOT_SUMMARY "dump the resolver facts and targets in DOT format"
+#define DOT_DESCRIPTION \
+ "Dump the resolver facts and targets in DOT format.\n"
+
+MRP_CORE_CONSOLE_GROUP(resolver_group, "resolver", RESOLVER_DESCRIPTION, NULL, {
+ MRP_TOKENIZED_CMD("dump", dump, FALSE,
+ DUMP_SYNTAX, DUMP_SUMMARY, DUMP_DESCRIPTION),
+ MRP_TOKENIZED_CMD("dot", dot, FALSE,
+ DOT_SYNTAX, DOT_SUMMARY, DOT_DESCRIPTION),
+});
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/mainloop.h>
+
+#include "resolver-types.h"
+#include "resolver.h"
+#include "events.h"
+
+MRP_REGISTER_EVENTS(events,
+ MRP_EVENT(MRP_RESOLVER_EVENT_STARTED, RESOLVER_UPDATE_STARTED),
+ MRP_EVENT(MRP_RESOLVER_EVENT_FAILED , RESOLVER_UPDATE_FAILED ),
+ MRP_EVENT(MRP_RESOLVER_EVENT_DONE , RESOLVER_UPDATE_DONE ));
+
+
+int emit_resolver_event(mrp_resolver_t *r, int event, const char *target,
+ int level)
+{
+ uint16_t ttarget = MRP_RESOLVER_TAG_TARGET;
+ uint16_t tlevel = MRP_RESOLVER_TAG_LEVEL;
+ int flags = MRP_EVENT_SYNCHRONOUS;
+
+ return mrp_event_emit_msg(r->bus, events[event].id, flags,
+ MRP_MSG_TAG_STRING(ttarget, target),
+ MRP_MSG_TAG_UINT32(tlevel , level));
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_EVENTS_H__
+#define __MURPHY_RESOLVER_EVENTS_H__
+
+/*
+ * resolver-related events
+ */
+
+enum {
+ RESOLVER_UPDATE_STARTED = 0,
+ RESOLVER_UPDATE_FAILED,
+ RESOLVER_UPDATE_DONE
+};
+
+
+int emit_resolver_event(mrp_resolver_t *r, int event, const char *target,
+ int level);
+
+
+#endif /* __MURPHY_RESOLVER_EVENTS_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy-db/mqi.h>
+
+#include "resolver-types.h"
+#include "resolver.h"
+#include "target.h"
+#include "fact.h"
+
+static int subscribe_db_events(mrp_resolver_t *r);
+static void unsubscribe_db_events(mrp_resolver_t *r);
+
+int create_fact(mrp_resolver_t *r, char *fact)
+{
+ int i;
+ fact_t *f;
+
+ subscribe_db_events(r);
+
+ for (i = 0; i < r->nfact; i++) {
+ if (!strcmp(r->facts[i].name, fact))
+ return TRUE;
+ }
+
+ if (!mrp_reallocz(r->facts, r->nfact * sizeof(*r->facts),
+ (r->nfact + 1) * sizeof(*r->facts)))
+ return FALSE;
+
+ f = r->facts + r->nfact++;
+ f->name = mrp_strdup(fact);
+ f->table = mqi_get_table_handle(f->name + 1);
+
+ if (f->name != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+void destroy_facts(mrp_resolver_t *r)
+{
+ fact_t *f;
+ int i;
+
+ unsubscribe_db_events(r);
+
+ for (i = 0, f = r->facts; i < r->nfact; i++, f++)
+ mrp_free(f->name);
+
+ mrp_free(r->facts);
+}
+
+
+uint32_t fact_stamp(mrp_resolver_t *r, int id)
+{
+ fact_t *fact = r->facts + id;
+ uint32_t stamp;
+
+ if (fact->table != MQI_HANDLE_INVALID)
+ stamp = mqi_get_table_stamp(fact->table);
+ else
+ stamp = 0; /* MQI_NO_STAMP */
+
+ return stamp;
+}
+
+
+const char *fact_name(mrp_resolver_t *r, int id)
+{
+ fact_t *fact = r->facts + id;
+
+ return fact->name;
+}
+
+
+fact_t *lookup_fact(mrp_resolver_t *r, const char *name)
+{
+ fact_t *f;
+ int i;
+
+ for (i = 0, f = r->facts; i < r->nfact; i++, f++)
+ if (!strcmp(f->name, name))
+ return f;
+
+ return NULL;
+}
+
+
+static void update_fact_table(mrp_resolver_t *r, const char *name,
+ mqi_handle_t tbl)
+{
+ fact_t *f;
+ int i;
+
+ for (i = 0, f = r->facts; i < r->nfact; i++, f++) {
+ if (!strcmp(f->name + 1, name)) {
+ f->table = tbl;
+ return;
+ }
+ }
+}
+
+
+static void check_fact_tables(mrp_resolver_t *r)
+{
+ fact_t *f;
+ int i;
+
+ for (i = 0, f = r->facts; i < r->nfact; i++, f++) {
+ if (f->table != MQI_HANDLE_INVALID)
+ mrp_debug("Fact table '%s' stamp: %u.",
+ f->name, mqi_get_table_stamp(f->table));
+ }
+}
+
+
+static inline int open_db(void)
+{
+ static int opened = FALSE;
+ if (!opened)
+ opened = (mqi_open() == 0);
+
+ return opened;
+}
+
+
+static void table_event(mqi_event_t *e, void *user_data)
+{
+ mrp_resolver_t *r = (mrp_resolver_t *)user_data;
+
+ switch (e->event) {
+ case mqi_table_created:
+ mrp_debug("DB table created (%s, %u).",
+ e->table.table.name, e->table.table.handle);
+ update_fact_table(r, e->table.table.name, e->table.table.handle);
+ break;
+ case mqi_table_dropped:
+ mrp_debug("DB table dropped (%s, %u).",
+ e->table.table.name, e->table.table.handle);
+ update_fact_table(r, e->table.table.name, MQI_HANDLE_INVALID);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void transaction_event(mqi_event_t *e, void *user_data)
+{
+ mrp_resolver_t *r = (mrp_resolver_t *)user_data;
+
+ MRP_UNUSED(r);
+
+ switch (e->event) {
+ case mqi_transaction_end:
+ mrp_debug("DB transaction ended.");
+ check_fact_tables(r);
+ if (mqi_get_transaction_depth() == 1) {
+ mrp_debug("was not nested, scheduling update");
+ schedule_target_autoupdate(r);
+ }
+ else
+ mrp_debug("was nested");
+ break;
+ case mqi_transaction_start:
+ mrp_debug("DB transaction started.");
+ break;
+ default:
+ break;
+ }
+}
+
+
+static int subscribe_db_events(mrp_resolver_t *r)
+{
+ if (open_db()) {
+ if (mqi_create_table_trigger(table_event, r) == 0) {
+ if (mqi_create_transaction_trigger(transaction_event, r) == 0)
+ return TRUE;
+ else
+ mqi_drop_table_trigger(table_event, r);
+ }
+ }
+
+ return FALSE;
+}
+
+
+static void unsubscribe_db_events(mrp_resolver_t *r)
+{
+ mqi_drop_table_trigger(table_event, r);
+ mqi_drop_transaction_trigger(transaction_event, r);
+}
+
+
+mqi_handle_t start_transaction(mrp_resolver_t *r)
+{
+ MRP_UNUSED(r);
+
+ return mqi_begin_transaction();
+}
+
+
+int commit_transaction(mrp_resolver_t *r, mqi_handle_t tx)
+{
+ MRP_UNUSED(r);
+
+ return mqi_commit_transaction(tx) != -1;
+}
+
+
+int rollback_transaction(mrp_resolver_t *r, mqi_handle_t tx)
+{
+ MRP_UNUSED(r);
+
+ return mqi_rollback_transaction(tx) != -1;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_FACT_H__
+#define __MURPHY_RESOLVER_FACT_H__
+
+#include <murphy-db/mqi.h>
+#include "resolver.h"
+
+int create_fact(mrp_resolver_t *r, char *name);
+void destroy_facts(mrp_resolver_t *r);
+
+int fact_changed(mrp_resolver_t *r, int id);
+uint32_t fact_stamp(mrp_resolver_t *r, int id);
+const char *fact_name(mrp_resolver_t *r, int id);
+
+fact_t *lookup_fact(mrp_resolver_t *r, const char *name);
+
+
+mqi_handle_t start_transaction(mrp_resolver_t *r);
+int commit_transaction(mrp_resolver_t *r, mqi_handle_t tx);
+int rollback_transaction(mrp_resolver_t *r, mqi_handle_t tx);
+
+#endif /* __MURPHY_RESOLVER_FACT_H__ */
--- /dev/null
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: murphy-resolver
+Description: Murphy policy framework, resolver library.
+Requires: murphy-core murphy-common
+Version: @PACKAGE_VERSION@
+Libs: -L${libdir} -lmurphy-resolver -lmurphy-core -lmurphy-common
+Cflags: -I${includedir}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_PARSER_TYPES_H__
+#define __MURPHY_RESOLVER_PARSER_TYPES_H__
+
+#include <stdio.h>
+
+#include <murphy/common/list.h>
+
+#define YY_RES_RINGBUF_SIZE (8 * 1024) /* token buffer size */
+
+/*
+ * a parsed target definition
+ */
+
+typedef struct {
+ char *type; /* script type */
+ char *source; /* script source */
+} yy_res_script_t;
+
+typedef struct {
+ mrp_list_hook_t hook; /* to list of targets */
+ char *name; /* target name */
+ char **depends; /* target dependencies */
+ int ndepend; /* number of dependencies */
+ char *script_type; /* update script type */
+ char *script_source; /* update script source */
+} yy_res_target_t;
+
+
+typedef struct yy_res_input_s yy_res_input_t;
+
+struct yy_res_input_s {
+ yy_res_input_t *prev; /* previous input */
+ void *yybuf; /* scanner buffer */
+ char *name; /* name of this input */
+ int line; /* line number in input */
+ FILE *fp; /* input stream */
+};
+
+
+typedef struct {
+ mrp_list_hook_t targets; /* list of targets */
+ char *auto_update; /* auto-update target */
+ char ringbuf[YY_RES_RINGBUF_SIZE]; /* token ringbuffer */
+ int offs; /* buffer insert offset */
+ yy_res_input_t *in; /* current input */
+ yy_res_input_t *done; /* processed inputs */
+} yy_res_parser_t;
+
+
+int parser_setup(yy_res_parser_t *parser, const char *path);
+void parser_cleanup(yy_res_parser_t *parser);
+int parser_parse_file(yy_res_parser_t *parser, const char *path);
+
+#endif /* __MURPHY_RESOLVER_PARSER_TYPES_H__ */
--- /dev/null
+%{ /* -*- c -*- */
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+
+#include "murphy/resolver/resolver.h"
+#include "murphy/resolver/parser-api.h"
+#include "murphy/resolver/token.h"
+#include "murphy/resolver/scanner.h"
+
+void yy_res_error(yy_res_parser_t *parser, const char *msg);
+
+static tkn_strarr_t *strarr_append(tkn_strarr_t *arr, char *str);
+
+%}
+
+%union {
+ tkn_any_t any;
+ tkn_string_t string;
+ tkn_s16_t s16;
+ tkn_u16_t u16;
+ tkn_s32_t s32;
+ tkn_u32_t u32;
+ yy_res_target_t *target;
+ yy_res_script_t script;
+ tkn_strarr_t *strarr;
+}
+
+%defines
+%parse-param { yy_res_parser_t *parser }
+%lex-param { yy_res_parser_t *parser }
+
+%token KEY_TARGET
+%token KEY_DEPENDS_ON
+%token <string> KEY_UPDATE_SCRIPT
+%token KEY_END_SCRIPT
+%token KEY_AUTOUPDATE
+%token <string> TKN_IDENT
+%token <string> TKN_FACT
+%token <string> TKN_SCRIPT_LINE
+%token <string> TKN_LEX_ERROR
+
+%type <target> targets
+%type <target> target
+%type <strarr> optional_dependencies
+%type <strarr> dependencies
+%type <script> optional_script
+%type <string> script_source
+
+%%
+
+input: optional_autoupdate targets
+ ;
+
+optional_autoupdate:
+/* no autoupdate target */ { parser->auto_update = NULL; }
+| KEY_AUTOUPDATE TKN_IDENT { parser->auto_update = mrp_strdup($2.value); }
+;
+
+targets:
+ target { mrp_list_append(&parser->targets, &$1->hook); }
+| targets target { mrp_list_append(&parser->targets, &$2->hook); }
+| targets error { YYABORT; }
+;
+
+target: KEY_TARGET TKN_IDENT optional_dependencies optional_script {
+ yy_res_target_t *t;
+ int i;
+
+ t = mrp_allocz(sizeof(*t));
+
+ if (t != NULL) {
+ mrp_list_init(&t->hook);
+
+ t->name = mrp_strdup($2.value);
+ if ($3 != NULL) {
+ t->depends = $3->strs;
+ t->ndepend = $3->nstr;
+ }
+
+ if ($4.source != NULL) {
+ t->script_type = mrp_strdup($4.type);
+ t->script_source = mrp_strdup($4.source);
+ }
+
+ if (t->name != NULL) {
+ mrp_list_append(&parser->targets, &t->hook);
+ $$ = t;
+ }
+ else
+ YYABORT;
+ }
+
+ mrp_log_info("target '%s':", $2.value);
+
+ if ($3 != NULL) {
+ for (i = 0; i < $3->nstr; i++)
+ mrp_log_info(" depends on '%s'", $3->strs[i]);
+ }
+ else
+ mrp_log_info(" no dependencies");
+
+ if ($4.source != NULL)
+ mrp_log_info(" update script (%s): %s", $4.type, $4.source);
+ else
+ mrp_log_info(" no update script");
+ }
+;
+
+optional_dependencies:
+ /* no dependencies */ { $$ = NULL; }
+| KEY_DEPENDS_ON dependencies { $$ = $2; }
+;
+
+dependencies:
+ TKN_IDENT { $$ = strarr_append(NULL, $1.value); }
+| TKN_FACT { $$ = strarr_append(NULL, $1.value); }
+| dependencies TKN_IDENT {
+ if (!strarr_append($1, $2.value))
+ YYABORT;
+ else
+ $$ = $1;
+ }
+| dependencies TKN_FACT {
+ if (!strarr_append($1, $2.value))
+ YYABORT;
+ else
+ $$ = $1;
+ }
+;
+
+optional_script:
+ /* no script */ {
+ $$.type = NULL;
+ $$.source = NULL;
+ }
+| KEY_UPDATE_SCRIPT script_source KEY_END_SCRIPT {
+ $$.type = $1.value;
+ $$.source = $2.value;
+ }
+;
+
+script_source:
+ TKN_SCRIPT_LINE {
+ int n;
+
+ n = strlen($1.value) + 2;
+ $$.value = mrp_allocz(n);
+
+ if ($$.value != NULL) {
+ strcpy($$.value, $1.value);
+ $$.value[n - 2] = '\n';
+ $$.value[n - 1] = '\0';
+ }
+ else
+ YYABORT;
+ }
+| script_source TKN_SCRIPT_LINE {
+ int o, n;
+
+ o = strlen($1.value);
+ n = o + strlen($2.value) + 2;
+ $$.value = mrp_reallocz($1.value, o, n);
+
+ if ($$.value == NULL)
+ YYABORT;
+
+ strcat($$.value, $2.value);
+ $$.value[n - 2] = '\n';
+ $$.value[n - 1] = '\0';
+ }
+;
+
+%%
+
+void yy_res_error(yy_res_parser_t *parser, const char *msg)
+{
+ MRP_UNUSED(parser);
+
+ mrp_log_error("parse error at %s:%d near token '%s': %s",
+ yy_res_lval.any.source, yy_res_lval.any.line,
+ yy_res_lval.any.token, msg);
+}
+
+
+static tkn_strarr_t *strarr_append(tkn_strarr_t *arr, char *str)
+{
+ int n;
+
+ if (arr == NULL) {
+ arr = mrp_allocz(sizeof(*arr));
+ if (arr == NULL)
+ return NULL;
+ }
+
+ if (!mrp_reallocz(arr->strs, arr->nstr, arr->nstr + 1))
+ return NULL;
+
+ n = arr->nstr++;
+ arr->strs[n] = mrp_strdup(str);
+
+ if (arr->strs[n] != NULL)
+ return arr;
+ else {
+ if (n == 0) {
+ mrp_free(arr->strs);
+ mrp_free(arr);
+ }
+
+ return NULL;
+ }
+}
+
+
+int parser_setup(yy_res_parser_t *parser, const char *path)
+{
+ mrp_clear(parser);
+ mrp_list_init(&parser->targets);
+
+ if (path != NULL)
+ return scanner_push_file(parser, path);
+ else
+ return TRUE;
+}
+
+
+void parser_cleanup(yy_res_parser_t *parser)
+{
+ mrp_list_hook_t *tp, *tn;
+ yy_res_target_t *t;
+ yy_res_input_t *ip, *in;
+ char **dep;
+ int i;
+
+ mrp_list_foreach(&parser->targets, tp, tn) {
+ t = mrp_list_entry(tp, typeof(*t), hook);
+
+ mrp_free(t->name);
+ mrp_free(t->script_type);
+ mrp_free(t->script_source);
+
+ if (t->depends != NULL) {
+ for (i = 0, dep = t->depends; i < t->ndepend; i++, dep++)
+ mrp_free(*dep);
+
+ mrp_free(t->depends);
+ }
+
+ mrp_free(t);
+ }
+
+ ip = parser->in;
+ while (ip != NULL) {
+ in = ip->prev;
+ scanner_free_input(ip);
+ ip = in;
+ }
+
+ ip = parser->done;
+ while (ip != NULL) {
+ in = ip->prev;
+ scanner_free_input(ip);
+ ip = in;
+ }
+
+ mrp_free(parser->auto_update);
+}
+
+
+int parser_parse_file(yy_res_parser_t *parser, const char *path)
+{
+ if (parser_setup(parser, path))
+ return yy_res_parse(parser) == 0;
+ else
+ return FALSE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_TYPES_H__
+#define __MURPHY_RESOLVER_TYPES_H__
+
+#include <stdint.h>
+
+#include <murphy/common/mainloop.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/core/context.h>
+#include <murphy/core/scripting.h>
+
+#include <murphy-db/mqi.h>
+
+typedef struct target_s target_t; /* opaque type for resolver targets */
+typedef struct fact_s fact_t; /* opaque type for tracked facts */
+
+/*
+ * a resolver target
+ */
+struct target_s {
+ char *name; /* target name */
+ uint32_t stamp; /* touch-stamp */
+ char **depends; /* dependencies stated in the input */
+ int ndepend; /* number of dependencies */
+ int *update_facts; /* facts to check when updating */
+ int *update_targets; /* targets to check when updating */
+ int *directs; /* direct dependencies */
+ int ndirect; /* number of direct dependencies */
+ uint32_t *fact_stamps; /* stamps of facts at last update */
+ mrp_scriptlet_t *script; /* update script if any, or NULL */
+ int prepared : 1; /* ready for resolution */
+ int precompiled : 1;
+};
+
+
+/*
+ * a tracked fact
+ */
+struct fact_s {
+ char *name; /* fact name */
+ mqi_handle_t table; /* associated DB table */
+ uint32_t stamp; /* touch-stamp */
+};
+
+
+struct mrp_resolver_s {
+ mrp_context_t *ctx; /* murphy context we're running in */
+ mrp_event_bus_t *bus; /* bus for resolver events */
+ target_t *targets; /* targets defined in the ruleset */
+ int ntarget; /* number of targets */
+ fact_t *facts; /* facts tracked as dependencies */
+ int nfact; /* number of tracked facts */
+ target_t *auto_update; /* target to resolve on fact changes */
+ mrp_deferred_t *auto_scheduled; /* scheduled auto_update */
+ uint32_t stamp; /* update stamp */
+ mrp_context_tbl_t *ctbl; /* context variable table */
+ int level; /* target update nesting level */
+};
+
+
+#endif /* __MURPHY_RESOLVER_TYPES_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+
+#include "scanner.h"
+#include "resolver-types.h"
+#include "target.h"
+#include "target-sorter.h"
+#include "fact.h"
+#include "resolver.h"
+
+
+mrp_resolver_t *mrp_resolver_create(mrp_context_t *ctx)
+{
+ mrp_resolver_t *r;
+
+ r = mrp_allocz(sizeof(mrp_resolver_t));
+
+ if (r != NULL) {
+ r->ctx = ctx;
+ r->ctbl = mrp_create_context_table();
+ r->bus = mrp_event_bus_get(ctx->ml, MRP_RESOLVER_BUS);
+
+ if (r->ctbl != NULL && r->bus != NULL)
+ return r;
+
+ mrp_free(r);
+ }
+
+ return NULL;
+}
+
+
+mrp_resolver_t *mrp_resolver_parse(mrp_resolver_t *r, mrp_context_t *ctx,
+ const char *path)
+{
+ yy_res_parser_t parser;
+
+ mrp_clear(&parser);
+
+ if (r == NULL) {
+ r = mrp_resolver_create(ctx);
+
+ if (r == NULL)
+ return NULL;
+ }
+
+ if (parser_parse_file(&parser, path)) {
+ if (create_targets(r, &parser) == 0 &&
+ sort_targets(r) == 0 &&
+ compile_target_scripts(r) == 0) {
+ parser_cleanup(&parser);
+ return r;
+ }
+ }
+ else
+ mrp_log_error("Failed to parse resolver input.");
+
+ mrp_resolver_destroy(r);
+ parser_cleanup(&parser);
+
+ return NULL;
+}
+
+
+int mrp_resolver_prepare(mrp_resolver_t *r)
+{
+ return (prepare_target_scripts(r) == 0);
+}
+
+
+void mrp_resolver_destroy(mrp_resolver_t *r)
+{
+ if (r != NULL) {
+ mrp_destroy_context_table(r->ctbl);
+ destroy_targets(r);
+ destroy_facts(r);
+
+ mrp_free(r);
+ }
+}
+
+
+int mrp_resolver_add_target(mrp_resolver_t *r, const char *target,
+ const char **depend, int ndepend,
+ const char *script_type,
+ const char *script_source)
+{
+ return (create_target(r, target, depend, ndepend,
+ script_type, script_source) != NULL);
+}
+
+
+int mrp_resolver_add_alias(mrp_resolver_t *r, const char *target,
+ const char *alias)
+{
+ const char *depend[1] = { target };
+
+ return (create_target(r, alias, depend, 1, NULL, NULL) != NULL);
+}
+
+
+int mrp_resolver_add_prepared_target(mrp_resolver_t *r, const char *target,
+ const char **depend, int ndepend,
+ mrp_interpreter_t *interpreter,
+ void *compiled_data, void *target_data)
+{
+ mrp_scriptlet_t *script;
+ target_t *t;
+
+ t = create_target(r, target, depend, ndepend, NULL, NULL);
+
+ if (t != NULL) {
+ if (interpreter != NULL) {
+ script = mrp_allocz(sizeof(*script));
+
+ if (script != NULL) {
+ script->interpreter = interpreter;
+ script->data = target_data;
+ script->compiled = compiled_data;
+
+ t->script = script;
+ }
+ else
+ return FALSE;
+ }
+
+ t->precompiled = TRUE;
+ t->prepared = TRUE;
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+int mrp_resolver_enable_autoupdate(mrp_resolver_t *r, const char *name)
+{
+ return generate_autoupdate_target(r, name);
+}
+
+
+int mrp_resolver_update_targetl(mrp_resolver_t *r, const char *target, ...)
+{
+ const char *name;
+ mrp_script_value_t value;
+ va_list ap;
+ int id, status;
+
+ if (mrp_push_context_frame(r->ctbl) == 0) {
+ va_start(ap, target);
+ while ((name = va_arg(ap, char *)) != NULL) {
+ id = mrp_get_context_id(r->ctbl, name);
+
+ if (id > 0) {
+ value.type = va_arg(ap, int);
+
+#define HANDLE_TYPE(_type, _member, _va_type) \
+ case MRP_SCRIPT_TYPE_##_type: \
+ value._member = \
+ (typeof(value._member))va_arg(ap, _va_type); \
+ break
+
+ switch (value.type) {
+ HANDLE_TYPE(STRING, str, char * );
+ HANDLE_TYPE(BOOL , bln, int );
+ HANDLE_TYPE(UINT8 , u8, uint32_t);
+ HANDLE_TYPE(SINT8 , s8, int32_t );
+ HANDLE_TYPE(UINT16, u16, uint32_t);
+ HANDLE_TYPE(SINT16, s16, int32_t );
+ HANDLE_TYPE(UINT32, u32, uint32_t);
+ HANDLE_TYPE(SINT32, s32, int32_t );
+ HANDLE_TYPE(UINT64, u64, uint64_t);
+ HANDLE_TYPE(SINT64, u64, uint64_t);
+ HANDLE_TYPE(DOUBLE, dbl, double );
+ default:
+ errno = EINVAL;
+ status = -1;
+ goto pop_frame;
+ }
+#undef HANDLE_TYPE
+
+ if (mrp_set_context_value(r->ctbl, id, &value) < 0) {
+ status = -1;
+ goto pop_frame;
+ }
+ }
+ else {
+ errno = ESRCH;
+ status = -1;
+ goto pop_frame;
+ }
+ }
+
+ status = update_target_by_name(r, target);
+
+ pop_frame:
+ mrp_pop_context_frame(r->ctbl);
+ va_end(ap);
+ }
+ else
+ status = -1;
+
+ return status;
+}
+
+
+int mrp_resolver_update_targetv(mrp_resolver_t *r, const char *target,
+ const char **variables,
+ mrp_script_value_t *values,
+ int nvariable)
+{
+ const char *name;
+ mrp_script_value_t *value;
+ int id, i, status;
+
+ if (mrp_push_context_frame(r->ctbl) == 0) {
+ for (i = 0; i < nvariable; i++) {
+ name = variables[i];
+ value = values + i;
+ id = mrp_get_context_id(r->ctbl, name);
+
+ if (id > 0) {
+ if (mrp_set_context_value(r->ctbl, id, value) < 0) {
+ status = -1;
+ goto pop_frame;
+ }
+ }
+ else {
+ errno = ESRCH;
+ status = -1;
+ goto pop_frame;
+ }
+ }
+
+ status = update_target_by_name(r, target);
+
+ pop_frame:
+ mrp_pop_context_frame(r->ctbl);
+ }
+ else
+ status = -1;
+
+ return status;
+}
+
+
+void mrp_resolver_dump_targets(mrp_resolver_t *r, FILE *fp)
+{
+ fprintf(fp, "%d target%s\n", r->ntarget, r->ntarget != 1 ? "s" : "");
+ dump_targets(r, fp);
+}
+
+
+void mrp_resolver_dump_facts(mrp_resolver_t *r, FILE *fp)
+{
+ int i;
+ fact_t *f;
+
+ fprintf(fp, "%d fact%s\n", r->nfact, r->nfact != 1 ? "s" : "");
+ for (i = 0; i < r->nfact; i++) {
+ f = r->facts + i;
+ fprintf(fp, " #%d: %s (@%u)\n", i, f->name, fact_stamp(r, i));
+ }
+}
+
+
+int mrp_resolver_register_interpreter(mrp_interpreter_t *i)
+{
+ return mrp_register_interpreter(i);
+}
+
+
+int mrp_resolver_unregister_interpreter(const char *name)
+{
+ return mrp_unregister_interpreter(name);
+}
+
+
+int mrp_resolver_declare_variable(mrp_resolver_t *r, const char *name,
+ mrp_script_type_t type)
+{
+ return mrp_declare_context_variable(r->ctbl, name, type);
+}
+
+
+int mrp_resolver_get_value(mrp_resolver_t *r, int id, mrp_script_value_t *v)
+{
+ return mrp_get_context_value(r->ctbl, id, v);
+}
+
+
+int mrp_resolver_get_value_by_name(mrp_resolver_t *r, const char *name,
+ mrp_script_value_t *v)
+{
+ return mrp_get_context_value(r->ctbl, mrp_get_context_id(r->ctbl, name), v);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_H__
+#define __MURPHY_RESOLVER_H__
+
+#include <stdio.h>
+#include <stdbool.h>
+
+typedef struct mrp_resolver_s mrp_resolver_t;
+
+#include <murphy/common/macros.h>
+#include <murphy/core/context.h>
+#include <murphy/core/scripting.h>
+
+MRP_CDECL_BEGIN
+
+
+/*
+ * tags and names of resolver-related events we emit
+ */
+
+#define MRP_RESOLVER_BUS "resolver-bus"
+#define MRP_RESOLVER_EVENT_STARTED "resolver-update-start"
+#define MRP_RESOLVER_EVENT_FAILED "resolver-update-failed"
+#define MRP_RESOLVER_EVENT_DONE "resolver-update-done"
+
+#define MRP_RESOLVER_TAG_TARGET ((uint16_t)1)
+#define MRP_RESOLVER_TAG_LEVEL ((uint16_t)2)
+
+/** Just create a resolver context without parsing any input. */
+mrp_resolver_t *mrp_resolver_create(mrp_context_t *ctx);
+
+/** Parse the given resolver input file into a resolver context. */
+mrp_resolver_t *mrp_resolver_parse(mrp_resolver_t *r, mrp_context_t *ctx,
+ const char *path);
+
+/** Add a new target with the given parameters to the resolver context. */
+int mrp_resolver_add_target(mrp_resolver_t *r, const char *target,
+ const char **depend, int ndepend,
+ const char *script_type,
+ const char *script_source);
+
+/** Add a precompiled target to the resolver context. */
+int mrp_resolver_add_prepared_target(mrp_resolver_t *r, const char *target,
+ const char **depend, int ndepend,
+ mrp_interpreter_t *interpreter,
+ void *compiled_data, void *target_data);
+
+/** Add an alias for the given target. */
+int mrp_resolver_add_alias(mrp_resolver_t *r, const char *target,
+ const char *alias);
+
+/** Enable autoupdate, generate autoupdate target if needed. */
+int mrp_resolver_enable_autoupdate(mrp_resolver_t *r, const char *name);
+
+/** Destroy the given resolver context, freeing all associated resources. */
+void mrp_resolver_destroy(mrp_resolver_t *r);
+
+/** Prepare the targets for resolution (link scriptlets, etc.). */
+int mrp_resolver_prepare(mrp_resolver_t *r);
+
+/** Update the given target. The NULL-terminated variable argument list
+ after the target name sepcifies the resolver context variables to
+ set during the update. Use a single NULL to omit variables. */
+int mrp_resolver_update_targetl(mrp_resolver_t *r,
+ const char *target, ...) MRP_NULLTERM;
+
+#define mrp_resolver_update_target mrp_resolver_update_targetl
+
+/** Update the given target. The variable name and type/value arrays
+ specify the resolver context variables to set during the update. */
+int mrp_resolver_update_targetv(mrp_resolver_t *r, const char *target,
+ const char **variables,
+ mrp_script_value_t *values,
+ int nvariable);
+
+/** Declare a context variable with a given type. */
+int mrp_resolver_declare_variable(mrp_resolver_t *r, const char *name,
+ mrp_script_type_t type);
+
+
+/** Get the value of a context variable by id. */
+int mrp_resolver_get_value(mrp_resolver_t *r, int id, mrp_script_value_t *v);
+#define mrp_resolver_get_value_by_id mrp_resolver_get_value
+
+/** Get the value of a context variable by name. */
+int mrp_resolver_get_value_by_name(mrp_resolver_t *r, const char *name,
+ mrp_script_value_t *v);
+
+/** Print the given value to the given buffer. */
+char *mrp_print_value(char *buf, size_t size, mrp_script_value_t *value);
+
+/** Produce a debug dump of all targets. */
+void mrp_resolver_dump_targets(mrp_resolver_t *r, FILE *fp);
+
+/** Produce a debug dump of all tracked facts. */
+void mrp_resolver_dump_facts(mrp_resolver_t *r, FILE *fp);
+
+/** Register a script interpreter. */
+int mrp_resolver_register_interpreter(mrp_interpreter_t *i);
+
+/** Unregister a script interpreter. */
+int mrp_resolver_unregister_interpreter(const char *name);
+
+MRP_CDECL_END
+
+#endif /* __MURPHY_RESOLVER_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_SCANNER_H__
+#define __MURPHY_RESOLVER_SCANNER_H__
+
+#include "murphy/resolver/parser-api.h"
+
+int scanner_push_file(yy_res_parser_t *parser, const char *path);
+void scanner_free_input(yy_res_input_t *in);
+
+int yy_res_lex(yy_res_parser_t *parser);
+
+#endif /* __MURPHY_RESOLVER_SCANNER_H__ */
--- /dev/null
+%{ /* -*- c -*- */
+
+#define YY_DECL int yy_res_lex(yy_res_parser_t *parser)
+
+#include <stdio.h>
+#include <limits.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include "murphy/resolver/resolver.h"
+#include "murphy/resolver/scanner.h"
+#include "murphy/resolver/token.h"
+#include "murphy/resolver/parser-api.h"
+#include "murphy/resolver/parser.h"
+
+#define YY_NO_INPUT
+
+#define yy_res_create_buffer yy_res__create_buffer
+#define yy_res_delete_buffer yy_res__delete_buffer
+#define yy_res_switch_to_buffer yy_res__switch_to_buffer
+#define yy_res_scan_buffer yy_res__scan_buffer
+
+
+/*
+ * lexical analyser input sources
+ *
+ * We support an include mechanism similar to the #include directive
+ * of the C-preprocessor. When one input file includes another, this
+ * is treated as if the latter file was verbatim copied in place of
+ * the include directive in the former file.
+ *
+ * The include mechanism is (almost) entirely implemented in the lexical
+ * analyser and is transparet to/hidden from the parser. The functions
+ * below and the macro CHECK_EOF take care of inclusion.
+ *
+ * Note that currently there is no any attempt to check for and prevent
+ * circular inclusion loops...
+ */
+
+int scanner_push_file(yy_res_parser_t *parser, const char *file)
+{
+ yy_res_input_t *in;
+ char buf[PATH_MAX], *base;
+ const char *path;
+ int len;
+ FILE *fp;
+
+ if (*file != '/' && parser->in != NULL) {
+ base = strrchr(parser->in->name, '/');
+ if (base != NULL) {
+ len = base - parser->in->name;
+ snprintf(buf, sizeof(buf), "%*.*s/%s",
+ len, len, parser->in->name, file);
+ path = buf;
+ }
+ else
+ path = file;
+ }
+ else
+ path = file;
+
+ fp = fopen(path, "r");
+
+ if (fp != NULL) {
+ in = mrp_allocz(sizeof(*in));
+
+ if (in != NULL) {
+ in->fp = fp;
+ in->name = mrp_strdup(path);
+ in->line = 1;
+ in->yybuf = yy_res_create_buffer(in->fp, YY_BUF_SIZE);
+ in->prev = parser->in;
+
+ yy_res_switch_to_buffer(in->yybuf);
+
+ parser->in = in;
+
+ return TRUE;
+ }
+ else
+ fclose(fp);
+ }
+ else
+ mrp_log_error("Failed to open input file '%s' ('%s', error: %s).",
+ file, path, strerror(errno));
+
+ return FALSE;
+}
+
+
+void scanner_free_input(yy_res_input_t *in)
+{
+ if (in != NULL) {
+ if (in->fp != NULL)
+ fclose(in->fp);
+ mrp_free(in->name);
+ yy_res_delete_buffer(in->yybuf);
+
+ mrp_free(in);
+ }
+}
+
+
+int scanner_pop_input(yy_res_parser_t *parser)
+{
+ yy_res_input_t *in, *prev;
+
+ if (parser->in != NULL) {
+ in = parser->in;
+ prev = in->prev;
+
+ in->prev = parser->done;
+ parser->done = in;
+
+ parser->in = prev;
+ if (prev != NULL) {
+ yy_res_switch_to_buffer(prev->yybuf);
+
+ return TRUE; /* more input to process */
+ }
+ }
+
+ return FALSE; /* no more input */
+}
+
+
+/*
+ * ringbuffer of tokens
+ *
+ * To simplify the lifecycle management of tokens passed between the
+ * lexical analyser and the parser we collect them into a ring buffer
+ * instead of dynamic allocation. This simplifies both the lexical
+ * analyser and the parser and allows us to have sane owner allocates /
+ * owner frees allocation semantics. The price we pay for this is that
+ * the ring buffer must be big enough to accomodate all the unprocessed
+ * tokens between bison rule reductions.
+ */
+
+static char *save_token(yy_res_parser_t *parser, char *str, size_t size)
+{
+ char *token;
+
+ if (!size)
+ size = strlen(str);
+
+ if (parser->offs + size + 1 >= YY_RES_RINGBUF_SIZE)
+ parser->offs = 0;
+
+ token = parser->ringbuf + parser->offs;
+ parser->offs += size + 1;
+
+#ifdef __MURPHY_RESOLVER_CHECK_RINGBUF__
+ if (*token != '\0') {
+ mrp_log_error("Token ring buffer overflow in resolver lexical "
+ "analyser.");
+ exit(1);
+ }
+#endif
+
+ strncpy(token, str, size);
+ token[size] = '\0';
+
+ yy_res_lval.any.token = token;
+ yy_res_lval.any.source = parser->in->name;
+ yy_res_lval.any.line = parser->in->line;
+ yy_res_lval.any.size = size;
+
+ return token;
+}
+
+
+/*
+ * string token types (must include all token types passed via STRING_TOKEN)
+ */
+
+typedef enum {
+ STRING_TYPE_IDENT,
+ STRING_TYPE_FACT,
+ STRING_TYPE_SCRIPT_LINE,
+} string_type_t;
+
+
+#define KEYWORD_TOKEN(tkn) do { \
+ save_token(parser, yy_res_text, yy_res_leng); \
+ \
+ mrp_debug("KEY_%s", #tkn); \
+ \
+ return KEY_##tkn; \
+ } while (0)
+
+#define KEYWORD_TOKENV(tkn, val, size) do { \
+ size_t _size; \
+ char *_t; \
+ \
+ _size = size ? size : strlen(val); \
+ _t = save_token(parser, val, _size); \
+ yy_res_lval.string.value = _t; \
+ \
+ mrp_debug("KEY_%s ('%s')", #tkn, val); \
+ \
+ return KEY_##tkn; \
+ } while (0)
+
+
+#define STRING_TOKEN(tkn) do { \
+ char *_t, *_v; \
+ int _l; \
+ \
+ switch (STRING_TYPE_##tkn) { \
+ case STRING_TYPE_FACT: \
+ _v = yy_res_text; \
+ _l = yy_res_leng; \
+ break; \
+ default: \
+ _v = yy_res_text; \
+ _l = yy_res_leng; \
+ } \
+ \
+ _t = save_token(parser, _v, _l); \
+ yy_res_lval.string.value = _t; \
+ \
+ mrp_debug("TKN_%s ('%s')", #tkn, _t); \
+ \
+ return TKN_##tkn; \
+ } while (0)
+
+
+#define IGNORE_TOKEN(tkn) do { \
+ mrp_debug("ignore %s ('%s')", #tkn, \
+ yy_res_text); \
+ } while (0)
+
+
+#define INCLUDE_FILE() do { \
+ char *_p; \
+ int _l; \
+ \
+ _p = strchr(yy_res_text, '"'); \
+ if (_p != NULL) { \
+ _p++; \
+ _l = yy_res_leng - (_p - yy_res_text); \
+ _p = save_token(parser, _p, _l - 1); \
+ \
+ mrp_debug("including file '%s'...", _p); \
+ \
+ if (!scanner_push_file(parser, _p)) \
+ return TKN_LEX_ERROR; \
+ } \
+ } while (0)
+
+#define CHECK_EOF() do { \
+ if (!scanner_pop_input(parser)) \
+ yyterminate(); \
+ } while (0)
+
+%}
+
+%option warn
+%option batch
+%option noyywrap
+%option nounput
+
+WS [ \t]+
+OWS [ \t]*
+ESCAPED_EOL \\\n
+EOL \n
+COMMENT #.*
+
+INCLUDE ^include{WS}\"[^\"]*\"
+
+TARGET ^target
+AUTOUPDATE ^auto-update-target
+DEPENDS_ON depends\ on
+IDENT [a-zA-Z_][a-zA-Z0-9_]+
+FACT \${IDENT}(\.{IDENT})*
+UPDATE_SCRIPT ^{WS}(update\ script|update\ script{OWS}\({OWS}{IDENT}{OWS}\))
+END_SCRIPT ^{WS}end\ script
+PAREN_OPEN \(
+PAREN_CLOSE \)
+SCRIPT_LINE ^{WS}.*
+
+%x SCRIPT
+
+%%
+
+{TARGET} { KEYWORD_TOKEN(TARGET); }
+{AUTOUPDATE} { KEYWORD_TOKEN(AUTOUPDATE); }
+{DEPENDS_ON} { KEYWORD_TOKEN(DEPENDS_ON); }
+{IDENT} { STRING_TOKEN(IDENT); }
+{FACT} { STRING_TOKEN(FACT); }
+
+{UPDATE_SCRIPT} {
+ char *type, *end, *p;
+ size_t len;
+
+ BEGIN(SCRIPT);
+
+ type = strchr(yy_res_text, '(');
+
+ if (type != NULL) {
+ end = strchr(type, ')');
+ len = end - type - 1;
+ p = type + 1;
+ while (*p == ' ' || *p == '\t')
+ p++, len--;
+ while (len > 0 &&
+ (p[len-1] == ' ' || p[len-1] == '\t'))
+ len--;
+
+ KEYWORD_TOKENV(UPDATE_SCRIPT, p, len);
+ }
+ else
+ KEYWORD_TOKENV(UPDATE_SCRIPT, "simple", 0);
+ }
+<SCRIPT>{END_SCRIPT} { BEGIN(INITIAL); KEYWORD_TOKEN(END_SCRIPT); }
+<SCRIPT>{SCRIPT_LINE} { STRING_TOKEN(SCRIPT_LINE); }
+
+<*>{WS} { /*IGNORE_TOKEN(WS);*/ }
+<*>{EOL} { parser->in->line++; /*IGNORE_TOKEN(EOL);*/ }
+{ESCAPED_EOL} { parser->in->line++; /*IGNORE_TOKEN(EOL);*/ }
+<INITIAL>{COMMENT} { parser->in->line++; /*IGNORE_TOKEN(COMMENT);*/ }
+{INCLUDE} { INCLUDE_FILE(); }
+<<EOF>> { CHECK_EOF(); }
+
+. { mrp_log_error("Unhandled token '%s'",
+ yy_res_text); }
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <murphy/common/log.h>
+#include <murphy/core/method.h>
+
+#include "builtins.h"
+
+static int builtin_echo(mrp_plugin_t *plugin, const char *name,
+ mrp_script_env_t *env)
+{
+ mrp_script_value_t *arg;
+ int i;
+ char buf[512], *t;
+
+ MRP_UNUSED(plugin);
+ MRP_UNUSED(name);
+
+ for (i = 0, arg = env->args, t = ""; i < env->narg; i++, arg++, t=" ")
+ printf("%s%s", t, mrp_print_value(buf, sizeof(buf), arg));
+
+ printf("\n");
+
+ return TRUE;
+}
+
+
+int export_builtins(void)
+{
+ mrp_method_descr_t methods[] = {
+ {
+ .name = "echo",
+ .signature = NULL ,
+ .native_ptr = NULL ,
+ .script_ptr = builtin_echo,
+ .plugin = NULL
+ },
+ { NULL, NULL, NULL, NULL, NULL }
+ }, *m;
+
+ for (m = methods; m->name != NULL; m++) {
+ if (mrp_export_method(m) < 0) {
+ mrp_log_error("Failed to export function '%s'.", m->name);
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SIMPLE_BUILTINS_H__
+#define __MURPHY_SIMPLE_BUILTINS_H__
+
+int export_builtins(void);
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/core/plugin.h> /* XXX TODO, needed for method.h */
+#include <murphy/core/method.h>
+
+#include "call.h"
+
+function_call_t *create_call(char *function, arg_t *args, int narg)
+{
+ function_call_t *c;
+
+ c = mrp_allocz(sizeof(*c));
+
+ if (c != NULL) {
+ c->name = mrp_strdup(function);
+
+ if (c->name != NULL) {
+ mrp_list_init(&c->hook);
+ c->args = args;
+ c->narg = narg;
+
+ return c;
+ }
+
+ mrp_free(c);
+ }
+
+ return NULL;
+}
+
+
+void destroy_call(function_call_t *c)
+{
+ if (c != NULL) {
+ mrp_list_delete(&c->hook);
+ mrp_free(c->name);
+ destroy_arguments(c->args, c->narg);
+ mrp_free(c);
+ }
+}
+
+
+int set_constant_value_arg(arg_t *arg, mrp_script_value_t *value)
+{
+ arg->cst.type = ARG_CONST_VALUE;
+ arg->cst.value = *value;
+
+ if (value->type == MRP_SCRIPT_TYPE_STRING) {
+ arg->cst.value.str = mrp_strdup(value->str);
+
+ if (arg->cst.value.str != NULL)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+
+int set_context_value_arg(arg_t *arg, char *name)
+{
+ arg->val.type = ARG_CONTEXT_VAR;
+ arg->val.name = mrp_strdup(name);
+
+ if (arg->val.name != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+int set_context_set_arg(arg_t *arg, char *name, mrp_script_value_t *value)
+{
+ arg->set.type = ARG_CONTEXT_SET;
+ arg->set.name = mrp_strdup(name);
+
+ if (arg->set.name == NULL)
+ return FALSE;
+
+ arg->set.id = -1;
+ arg->set.value = *value;
+
+ if (value->type == MRP_SCRIPT_TYPE_STRING) {
+ arg->set.value.str = mrp_strdup(value->str);
+
+ if (arg->set.value.str == NULL)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+void destroy_arguments(arg_t *args, int narg)
+{
+ arg_t *a;
+ int i;
+
+ for (i = 0, a = args; i < narg; i++, a++) {
+ if (a->type == ARG_CONST_VALUE &&
+ a->cst.value.type == MRP_SCRIPT_TYPE_STRING)
+ mrp_free(a->cst.value.str);
+ else if (a->type == ARG_CONTEXT_VAR)
+ mrp_free(a->val.name);
+ }
+
+ mrp_free(args);
+}
+
+
+int link_call(function_call_t *c)
+{
+ mrp_plugin_t *plugin;
+ int (*script_ptr)(mrp_plugin_t *plugin, const char *name,
+ mrp_script_env_t *env);
+
+ if (c->script_ptr == NULL) {
+ if (mrp_import_method(c->name, NULL, NULL, &script_ptr, &plugin) < 0) {
+ mrp_log_error("Failed to find method '%s'.", c->name);
+ return FALSE;
+ }
+ else {
+ c->script_ptr = script_ptr;
+ c->plugin = plugin;
+ }
+ }
+
+ return TRUE;
+}
+
+int execute_call(function_call_t *c, mrp_context_tbl_t *tbl)
+{
+ mrp_script_env_t env;
+ mrp_script_value_t args[c->narg];
+ arg_t *a;
+ int narg, n, status;
+
+ if (MRP_UNLIKELY(c->script_ptr == NULL)) {
+ if (!link_call(c))
+ return -ENOENT;
+ }
+
+ mrp_push_context_frame(tbl);
+
+ for (n = narg = 0, a = c->args; n < (int)MRP_ARRAY_SIZE(args); n++, a++) {
+ switch (a->type) {
+ case ARG_CONST_VALUE:
+ args[narg++] = a->cst.value;
+ break;
+ case ARG_CONTEXT_VAR:
+ if (a->val.id <= 0)
+ a->val.id = mrp_get_context_id(tbl, a->val.name);
+ if (mrp_get_context_value(tbl, a->val.id, args + narg) < 0) {
+ status = -ENOENT;
+ goto pop_frame;
+ }
+ narg++;
+ break;
+ case ARG_CONTEXT_SET:
+ if (a->set.id <= 0)
+ a->set.id = mrp_get_context_id(tbl, a->set.name);
+ if (mrp_set_context_value(tbl, a->set.id, &a->set.value) < 0) {
+ status = -errno;
+ goto pop_frame;
+ }
+ default:
+ status = -EINVAL;
+ goto pop_frame;
+ }
+ }
+
+ env.args = args;
+ env.narg = narg;
+ env.ctbl = tbl;
+
+ status = c->script_ptr(c->plugin, c->name, &env);
+
+ pop_frame:
+ mrp_pop_context_frame(tbl);
+
+ return status;
+}
+
+
+static void dump_arg(FILE *fp, arg_t *arg)
+{
+ mrp_script_value_t *val;
+ char vbuf[64];
+
+ switch (arg->type) {
+ case ARG_CONST_VALUE:
+ val = &arg->cst.value;
+ fprintf(fp, "%s", mrp_print_value(vbuf, sizeof(vbuf), val));
+ break;
+
+ case ARG_CONTEXT_VAR:
+ fprintf(fp, "&%s", arg->val.name);
+ break;
+
+ case ARG_CONTEXT_SET:
+ val = &arg->set.value;
+ fprintf(fp, "&%s=%s", arg->set.name,
+ mrp_print_value(vbuf, sizeof(vbuf), val));
+ break;
+
+ default:
+ fprintf(fp, "<unknown/unhandled argument type>");
+ }
+}
+
+
+void dump_call(FILE *fp, function_call_t *c)
+{
+ int i;
+ char *t;
+
+ fprintf(fp, " %s", c->name);
+
+ fprintf(fp, "(");
+ for (i = 0, t = ""; i < c->narg; i++, t = ", ") {
+ fprintf(fp, "%s", t);
+ dump_arg(fp, c->args + i);
+ }
+ fprintf(fp, ")\n");
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SIMPLE_SCRIPT_CALL_H__
+#define __MURPHY_SIMPLE_SCRIPT_CALL_H__
+
+#include <murphy/core/scripting.h>
+
+#include "simple-script.h"
+
+function_call_t *create_call(char *name, arg_t *args, int narg);
+int link_call(function_call_t *c);
+void destroy_call(function_call_t *c);
+void dump_call(FILE *fp, function_call_t *c);
+
+int set_constant_value_arg(arg_t *arg, mrp_script_value_t *value);
+int set_context_value_arg(arg_t *arg, char *name);
+int set_context_set_arg(arg_t *arg, char *name,
+ mrp_script_value_t *value);
+void destroy_arguments(arg_t *args, int narg);
+int execute_call(function_call_t *c, mrp_context_tbl_t *tbl);
+
+#endif /* __MURPHY_SIMPLE_SCRIPT_CALL_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SIMPLE_SCRIPT_PARSER_API_H__
+#define __MURPHY_SIMPLE_SCRIPT_PARSER_API_H__
+
+#include <stdio.h>
+
+#include <murphy/common/list.h>
+
+#include "simple-script.h"
+
+#define YY_SMPL_RINGBUF_SIZE (8 * 1024) /* token buffer size */
+
+/*
+ * a parsed script
+ */
+
+typedef struct {
+ mrp_list_hook_t statements; /* list of statements */
+ void *yybuf; /* scanner buffer */
+ int line; /* input line number */
+ char ringbuf[YY_SMPL_RINGBUF_SIZE]; /* token ringbuffer */
+ int offs; /* buffer insert offset */
+} yy_smpl_parser_t;
+
+
+
+
+
+int simple_parser_setup(yy_smpl_parser_t *parser, const char *script);
+void simple_parser_cleanup(yy_smpl_parser_t *parser);
+int simple_parser_parse(yy_smpl_parser_t *parser, const char *script);
+
+#endif /* __MURPHY_SIMPLE_SCRIPT_PARSER_API_H__ */
--- /dev/null
+%{ /* -*- c -*- */
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+
+#include "simple-parser-api.h"
+#include "token.h"
+#include "simple-scanner.h"
+#include "call.h"
+
+void yy_smpl_error(yy_smpl_parser_t *parser, const char *msg);
+
+static tkn_strarr_t *strarr_append(tkn_strarr_t *arr, char *str);
+static int initialize_argument(tkn_args_t *args, tkn_expr_t *expr);
+static int append_argument(tkn_args_t *args, tkn_expr_t *expr);
+static void cleanup_arguments(tkn_args_t *args);
+
+%}
+
+%union {
+ tkn_any_t any;
+ tkn_string_t string;
+ tkn_s8_t sint8;
+ tkn_u8_t uint8;
+ tkn_s16_t sint16;
+ tkn_u16_t uint16;
+ tkn_s32_t sint32;
+ tkn_u32_t uint32;
+ tkn_s64_t sint64;
+ tkn_u64_t uint64;
+ tkn_dbl_t dbl;
+ tkn_strarr_t *strarr;
+ tkn_string_t error;
+ tkn_args_t args;
+ tkn_expr_t expr;
+ tkn_value_t value;
+}
+
+%defines
+%parse-param { yy_smpl_parser_t *parser }
+%lex-param { yy_smpl_parser_t *parser }
+
+%token KEY_TARGET
+%token KEY_DEPENDS_ON
+%token KEY_UPDATE_SCRIPT
+%token KEY_END_SCRIPT
+
+%token <string> TKN_IDENT
+%token <string> TKN_CONTEXT_VAR
+%token <string> TKN_STRING
+
+%token <sint8> TKN_SINT8
+%token <uint8> TKN_UINT8
+%token <sint16> TKN_SINT16
+%token <uint16> TKN_UINT16
+%token <sint32> TKN_SINT32
+%token <uint32> TKN_UINT32
+%token <sint64> TKN_SINT64
+%token <uint64> TKN_UINT64
+%token <dbl> TKN_DOUBLE
+
+%token <string> TKN_PARENTH_OPEN "("
+%token <string> TKN_PARENTH_CLOSE ")"
+%token <string> TKN_COMMA ","
+%token <string> TKN_EQUAL "="
+
+%token <string> TKN_ERROR
+
+%type <value> constant_expression
+%type <string> context_value
+%type <expr> context_assign
+%type <expr> expression
+%type <args> arguments
+
+%%
+
+input: statements
+ ;
+
+statements:
+ statement
+| statements statement
+| statements error { YYABORT; }
+ ;
+
+statement: function_call
+ ;
+
+function_call:
+ TKN_IDENT "(" ")" {
+ function_call_t *c;
+
+ c = create_call($1.value, NULL, 0);
+
+ if (c != NULL)
+ mrp_list_append(&parser->statements, &c->hook);
+ else {
+ mrp_log_error("Failed to create new simple-script call.");
+ YYABORT;
+ }
+ }
+| TKN_IDENT "(" arguments ")" {
+ function_call_t *c;
+
+ c = create_call($1.value, $3.args, $3.narg);
+
+ if (c != NULL)
+ mrp_list_append(&parser->statements, &c->hook);
+ else {
+ mrp_log_error("Failed to create new simple-script call.");
+ YYABORT;
+ }
+ }
+ ;
+
+arguments:
+ expression {
+ if (!initialize_argument(&$$, &$1))
+ YYABORT;
+ }
+| arguments "," expression {
+ if (!append_argument(&$$, &$3)) {
+ cleanup_arguments(&$$);
+ YYABORT;
+ }
+ }
+| arguments error { YYABORT; }
+ ;
+
+expression:
+ constant_expression {
+ $$.cst.type = EXPR_CONSTANT;
+ $$.cst.value = $1.value;
+ }
+| context_value {
+ $$.val.type = EXPR_CONTEXT_VALUE;
+ $$.val.name = $1.value;
+ }
+| context_assign {
+ $$ = $1;
+ }
+ ;
+
+constant_expression:
+ TKN_STRING {
+ $$.value.type = MRP_SCRIPT_TYPE_STRING;
+ $$.value.str = $1.value;
+ }
+| TKN_SINT8 {
+ $$.value.type = MRP_SCRIPT_TYPE_SINT8;
+ $$.value.s8 = $1.value;
+ }
+| TKN_UINT8 {
+ $$.value.type = MRP_SCRIPT_TYPE_UINT8;
+ $$.value.u8 = $1.value;
+ }
+| TKN_SINT16 {
+ $$.value.type = MRP_SCRIPT_TYPE_SINT16;
+ $$.value.s16 = $1.value;
+ }
+| TKN_UINT16 {
+ $$.value.type = MRP_SCRIPT_TYPE_UINT16;
+ $$.value.u16 = $1.value;
+ }
+| TKN_SINT32 {
+ $$.value.type = MRP_SCRIPT_TYPE_SINT32;
+ $$.value.s32 = $1.value;
+ }
+| TKN_UINT32 {
+ $$.value.type = MRP_SCRIPT_TYPE_UINT32;
+ $$.value.u32 = $1.value;
+ }
+| TKN_SINT64 {
+ $$.value.type = MRP_SCRIPT_TYPE_SINT64;
+ $$.value.s64 = $1.value;
+ }
+| TKN_UINT64 {
+ $$.value.type = MRP_SCRIPT_TYPE_UINT64;
+ $$.value.u64 = $1.value;
+ }
+| TKN_DOUBLE {
+ $$.value.type = MRP_SCRIPT_TYPE_DOUBLE;
+ $$.value.dbl = $1.value;
+ }
+ ;
+
+context_value: TKN_CONTEXT_VAR { $$ = $1; }
+ ;
+
+context_assign: TKN_CONTEXT_VAR "=" constant_expression {
+ mrp_debug("context_assign");
+ $$.set.type = EXPR_CONTEXT_SET;
+ $$.set.name = $1.value;
+ $$.set.value = $3.value;
+ }
+ ;
+
+
+%%
+
+static int initialize_argument(tkn_args_t *args, tkn_expr_t *expr)
+{
+ arg_t *arg;
+
+ args->args = mrp_allocz(sizeof(*args->args));
+
+ if (args->args != NULL) {
+ arg = args->args;
+ args->narg = 1;
+
+ switch (expr->type) {
+ case EXPR_CONSTANT:
+ set_constant_value_arg(arg, &expr->cst.value);
+ break;
+ case EXPR_CONTEXT_VALUE:
+ set_context_value_arg(&args->args[0], expr->val.name);
+ break;
+
+ case EXPR_CONTEXT_SET:
+ set_context_set_arg(&args->args[0],
+ expr->set.name, &expr->set.value);
+ break;
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static int append_argument(tkn_args_t *args, tkn_expr_t *expr)
+{
+ arg_t *arg;
+
+ if (!mrp_reallocz(args->args, args->narg, args->narg + 1))
+ return FALSE;
+
+ arg = args->args + args->narg;
+ args->narg++;
+
+ switch (expr->type) {
+ case EXPR_CONSTANT:
+ set_constant_value_arg(arg, &expr->cst.value);
+ break;
+ case EXPR_CONTEXT_VALUE:
+ set_context_value_arg(&args->args[0], expr->val.name);
+ break;
+ case EXPR_CONTEXT_SET:
+ set_context_set_arg(&args->args[0], expr->set.name, &expr->set.value);
+
+ default:
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+static void cleanup_arguments(tkn_args_t *args)
+{
+ destroy_arguments(args->args, args->narg);
+}
+
+
+void yy_smpl_error(yy_smpl_parser_t *parser, const char *msg)
+{
+ MRP_UNUSED(parser);
+
+ mrp_log_error("parse error at line %d near token '%s': %s",
+ yy_smpl_lval.any.line, yy_smpl_lval.any.token, msg);
+}
+
+
+static tkn_strarr_t *strarr_append(tkn_strarr_t *arr, char *str)
+{
+ int n;
+
+ if (arr == NULL) {
+ arr = mrp_allocz(sizeof(*arr));
+ if (arr == NULL)
+ return NULL;
+ }
+
+ if (!mrp_reallocz(arr->strs, arr->nstr, arr->nstr + 1))
+ return NULL;
+
+ n = arr->nstr++;
+ arr->strs[n] = mrp_strdup(str);
+
+ if (arr->strs[n] != NULL)
+ return arr;
+ else {
+ if (n == 0) {
+ mrp_free(arr->strs);
+ mrp_free(arr);
+ }
+
+ return NULL;
+ }
+}
+
+
+int simple_parser_setup(yy_smpl_parser_t *parser, const char *script)
+{
+ MRP_UNUSED(strarr_append);
+
+ mrp_clear(parser);
+
+ mrp_list_init(&parser->statements);
+ parser->line = 1;
+
+ return simple_scanner_setup(parser, script);
+}
+
+
+void simple_parser_cleanup(yy_smpl_parser_t *parser)
+{
+ mrp_list_hook_t *p, *n;
+ function_call_t *c;
+
+ mrp_list_foreach(&parser->statements, p, n) {
+ c = mrp_list_entry(p, typeof(*c), hook);
+
+ destroy_call(c);
+ }
+
+ simple_scanner_cleanup(parser);
+}
+
+
+int simple_parser_parse(yy_smpl_parser_t *parser, const char *script)
+{
+ if (simple_parser_setup(parser, script))
+ if (yy_smpl_parse(parser) == 0)
+ return TRUE;
+
+ return FALSE;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SIMPLE_SCRIPT_SCANNER_H__
+#define __MURPHY_SIMPLE_SCRIPT_SCANNER_H__
+
+#include "murphy/resolver/scripting/simple/simple-parser-api.h"
+
+int simple_scanner_setup(yy_smpl_parser_t *parser, const char *script);
+void simple_scanner_cleanup(yy_smpl_parser_t *parser);
+int yy_smpl_lex(yy_smpl_parser_t *parser);
+
+#endif /* __MURPHY_RESOLVER_SCANNER_H__ */
--- /dev/null
+%{ /* -*- c -*- */
+
+#define YY_DECL int yy_smpl_lex(yy_smpl_parser_t *parser)
+
+#include <stdio.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include "murphy/resolver/resolver.h"
+#include "murphy/resolver/scripting/simple/simple-scanner.h"
+#include "murphy/resolver/scripting/simple/token.h"
+#include "murphy/resolver/scripting/simple/simple-parser-api.h"
+#include "murphy/resolver/scripting/simple/simple-parser.h"
+
+#define yy_smpl_create_buffer yy_smpl__create_buffer
+#define yy_smpl_delete_buffer yy_smpl__delete_buffer
+#define yy_smpl_switch_to_buffer yy_smpl__switch_to_buffer
+#define yy_smpl_scan_buffer yy_smpl__scan_buffer
+#define yy_smpl_scan_string yy_smpl__scan_string
+#define yy_smpl_input input
+#define yy_smpl_unput unput
+
+/*
+ * lexical analyser input sources
+ */
+
+int simple_scanner_setup(yy_smpl_parser_t *parser, const char *script)
+{
+ parser->yybuf = yy_smpl_scan_string(script);
+
+ if (parser->yybuf != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+
+void simple_scanner_cleanup(yy_smpl_parser_t *parser)
+{
+ if (parser->yybuf != NULL) {
+ yy_smpl_delete_buffer(parser->yybuf);
+ parser->yybuf = NULL;
+ }
+}
+
+
+/*
+ * ringbuffer of tokens
+ *
+ * To simplify the lifecycle management of tokens passed between the
+ * lexical analyser and the parser we collect them into a ring buffer
+ * instead of dynamic allocation. This simplifies both the lexical
+ * analyser and the parser and allows us to have sane owner allocates /
+ * owner frees allocation semantics. The price we pay for this is that
+ * the ring buffer must be big enough to accomodate all the unprocessed
+ * tokens between bison rule reductions.
+ */
+
+static char *save_token(yy_smpl_parser_t *parser, char *str, size_t size)
+{
+ char *token;
+
+ if (!size)
+ size = strlen(str);
+
+ if (parser->offs + size + 1 >= YY_SMPL_RINGBUF_SIZE)
+ parser->offs = 0;
+
+ token = parser->ringbuf + parser->offs;
+ parser->offs += size + 1;
+
+#ifdef __MURPHY_SIMPLE_SCRIPT_CHECK_RINGBUF__
+ if (*token != '\0') {
+ mrp_log_error("Token ring buffer overflow in simple-script lexical "
+ "analyser.");
+ exit(1);
+ }
+#endif
+
+ strncpy(token, str, size);
+ token[size] = '\0';
+
+ yy_smpl_lval.any.token = token;
+ yy_smpl_lval.any.line = parser->line;
+ yy_smpl_lval.any.size = size;
+
+ return token;
+}
+
+
+/*
+ * string token types (must include all token types passed via STRING_TOKEN)
+ */
+
+typedef enum {
+ STRING_TYPE_IDENT,
+ STRING_TYPE_CONTEXT_VAR,
+ STRING_TYPE_STRING,
+} string_type_t;
+
+
+#define KEYWORD_TOKEN(tkn) do { \
+ save_token(parser, yy_smpl_text, yy_smpl_leng); \
+ \
+ mrp_debug("KEY_%s", #tkn); \
+ \
+ return KEY_##tkn; \
+ } while (0)
+
+
+#define STRING_TOKEN(tkn) do { \
+ char *_t, *_v; \
+ int _l; \
+ \
+ switch (STRING_TYPE_##tkn) { \
+ case STRING_TYPE_STRING: \
+ _v = yy_smpl_text + 1; \
+ _l = yy_smpl_leng - 2; \
+ break; \
+ case STRING_TYPE_CONTEXT_VAR: \
+ _v = yy_smpl_text + 1; \
+ _l = yy_smpl_leng - 1; \
+ break; \
+ default: \
+ _v = yy_smpl_text; \
+ _l = yy_smpl_leng; \
+ } \
+ \
+ _t = save_token(parser, _v, _l); \
+ yy_smpl_lval.string.value = _t; \
+ \
+ mrp_debug("TKN_%s: '%s'", #tkn, _t); \
+ \
+ return TKN_##tkn; \
+ } while (0)
+
+
+#define OTHER_TOKEN(tkn) do { \
+ char *_t, *_v; \
+ int _l; \
+ \
+ _v = yy_smpl_text; \
+ _l = yy_smpl_leng; \
+ \
+ _t = save_token(parser, _v, _l); \
+ yy_smpl_lval.string.value = _t; \
+ \
+ mrp_debug("TKN_%s: '%s'", #tkn, _t); \
+ \
+ return TKN_##tkn; \
+ } while (0)
+
+
+#define INTEGER_TOKEN(tkn, type) do { \
+ typeof(yy_smpl_lval.type.value) _value; \
+ char *_tkn, *_end; \
+ \
+ if (#type[0] == 'u') \
+ _value = (typeof(_value)) \
+ strtoull(yy_smpl_text, &_end, 0); \
+ else \
+ _value = (typeof(_value)) \
+ strtoll(yy_smpl_text, &_end, 0); \
+ \
+ _tkn = save_token(parser, yy_smpl_text, yy_smpl_leng); \
+ \
+ if (!*_end) { \
+ yy_smpl_lval.type.value = _value; \
+ mrp_debug("TKN_%s: '%s'", #tkn, _tkn); \
+ \
+ return TKN_##tkn; \
+ } \
+ else { \
+ if ((_end[0] == 'S' || _end[0] == 'U') && \
+ ((_end[1] == '8' && !_end[2]) || \
+ (_end[1] == '1' && _end[2] == '6' && !_end[3]) || \
+ (_end[1] == '3' && _end[2] == '2' && !_end[3]) || \
+ (_end[1] == '6' && _end[2] == '4' && !_end[3]))) { \
+ yy_smpl_lval.type.value = _value; \
+ mrp_debug("TKN_%s: '%s'", #tkn, _tkn); \
+ \
+ return TKN_##tkn; \
+ } \
+ else { \
+ yy_smpl_lval.error.value = "couldn't parse integer"; \
+ mrp_debug("TKN_ERROR: failed to parse integer."); \
+ \
+ return TKN_ERROR; \
+ } \
+ } \
+ } while (0)
+
+
+#define DOUBLE_TOKEN() do { \
+ char *_tkn, *_end; \
+ \
+ yy_smpl_lval.dbl.value = strtod(yy_smpl_text, &_end); \
+ \
+ _tkn = save_token(parser, yy_smpl_text, yy_smpl_leng); \
+ \
+ if (!*_end) { \
+ mrp_debug("TKN_DOUBLE: '%s'", _tkn); \
+ \
+ return TKN_DOUBLE; \
+ } \
+ else { \
+ yy_smpl_lval.error.value = "couldn't parse integer"; \
+ mrp_debug("TKN_ERROR: failed to parse integer."); \
+ \
+ return TKN_ERROR; \
+ } \
+ } while (0)
+
+
+#define IGNORE_TOKEN(tkn) do { \
+ mrp_debug("ignore %s ('%s')", #tkn, yy_smpl_text); \
+ } while (0)
+
+
+#define PROCESS_ESCAPE() do { \
+ int _c; \
+ \
+ switch ((_c = yy_smpl_input())) { \
+ case '\n': \
+ mrp_debug("ignore escaped '\\n'"); \
+ parser->line++; \
+ break; \
+ default: \
+ mrp_debug("escaped '%c'", _c); \
+ yy_smpl_unput(_c); \
+ } \
+ } while (0)
+
+%}
+
+%option warn
+%option batch
+%option noyywrap
+
+
+WS [ \t]+
+EMPTY_LINE [ \t]*$
+ESCAPE \\
+IDENT [a-zA-Z_][a-zA-Z0-9_]+
+CONTEXT_VAR &{IDENT}
+EOL \n
+STRING ('[^\n']*')|(\"[^\n\"]*\")
+INTEGER [+-]?[0-9]+
+HEXAINT [+-]?0x[0-9a-fA-F]+
+DOUBLE [+-]?[0-9]+\.[0-9]+
+SINT8 ({INTEGER}|{HEXAINT})S8
+UINT8 ({INTEGER}|{HEXAINT})U8
+SINT16 ({INTEGER}|{HEXAINT})S16
+UINT16 ({INTEGER}|{HEXAINT})U16
+SINT32 ({INTEGER}|{HEXAINT})S32
+UINT32 ({INTEGER}|{HEXAINT})U32
+SINT64 ({INTEGER}|{HEXAINT})S64
+UINT64 ({INTEGER}|{HEXAINT})U64
+
+%%
+
+{IDENT} { STRING_TOKEN(IDENT); }
+{CONTEXT_VAR} { STRING_TOKEN(CONTEXT_VAR); }
+{STRING} { STRING_TOKEN(STRING); }
+
+{SINT8} { INTEGER_TOKEN(SINT8 , sint8 ); }
+{UINT8} { INTEGER_TOKEN(UINT8 , uint8 ); }
+{SINT16} { INTEGER_TOKEN(SINT16, sint16); }
+{UINT16} { INTEGER_TOKEN(UINT16, uint16); }
+{SINT32} { INTEGER_TOKEN(SINT32, sint32); }
+{UINT32} { INTEGER_TOKEN(UINT32, uint32); }
+{SINT64} { INTEGER_TOKEN(SINT64, sint64); }
+{UINT64} { INTEGER_TOKEN(UINT64, uint64); }
+{INTEGER} { INTEGER_TOKEN(SINT32, sint32); }
+{HEXAINT} { INTEGER_TOKEN(SINT32, sint32); }
+{DOUBLE} { DOUBLE_TOKEN(); }
+
+
+\( { OTHER_TOKEN(PARENTH_OPEN); }
+\) { OTHER_TOKEN(PARENTH_CLOSE); }
+, { OTHER_TOKEN(COMMA); }
+= { OTHER_TOKEN(EQUAL); }
+
+{WS} { /*IGNORE_TOKEN(WS);*/ }
+{EOL} { parser->line++; /*IGNORE_TOKEN(EOL);*/ }
+{EMPTY_LINE} { parser->line++; /*IGNORE_TOKEN(EMPTY_LINE);*/ }
+{ESCAPE} { PROCESS_ESCAPE(); }
+
+<<EOF>> { yy_smpl_pop_buffer_state();
+ parser->yybuf = NULL;
+ yyterminate(); }
+
+. { mrp_log_error("Unhandled token '%s'",
+ yy_smpl_text); }
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+#include <murphy/common/list.h>
+
+#include <murphy/resolver/resolver.h>
+#include "simple-parser-api.h"
+#include "call.h"
+#include "builtins.h"
+
+
+static void simple_dump(FILE *fp, simple_script_t *ss)
+{
+ mrp_list_hook_t *p, *n;
+ function_call_t *c;
+
+ if (ss != NULL) {
+ mrp_list_foreach(&ss->statements, p, n) {
+ c = mrp_list_entry(p, typeof(*c), hook);
+
+ dump_call(fp, c);
+ }
+ }
+}
+
+
+static int simple_compile(mrp_scriptlet_t *script)
+{
+ static int builtins_exported = FALSE;
+
+ yy_smpl_parser_t parser;
+ simple_script_t *ss;
+
+ if (!builtins_exported) {
+ if (!export_builtins())
+ return -1;
+ else
+ builtins_exported = TRUE;
+ }
+
+ if (simple_parser_parse(&parser, script->source)) {
+ ss = mrp_allocz(sizeof(*ss));
+
+ if (ss != NULL) {
+ script->compiled = ss;
+
+ mrp_list_move(&ss->statements, &parser.statements);
+ simple_parser_cleanup(&parser);
+
+ return 0;
+ }
+ else
+ simple_parser_cleanup(&parser);
+ }
+
+ return -1;
+}
+
+
+static int simple_prepare(mrp_scriptlet_t *s)
+{
+ simple_script_t *ss = s->compiled;
+ mrp_list_hook_t *p, *n;
+ function_call_t *c;
+
+ if (ss != NULL) {
+ mrp_list_foreach(&ss->statements, p, n) {
+ c = mrp_list_entry(p, typeof(*c), hook);
+
+ if (!link_call(c)) {
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ return 0;
+ }
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+
+static int simple_execute(mrp_scriptlet_t *s, mrp_context_tbl_t *tbl)
+{
+ simple_script_t *ss = s->compiled;
+ mrp_list_hook_t *p, *n;
+ function_call_t *c;
+ int status;
+
+ if (ss != NULL) {
+ mrp_list_foreach(&ss->statements, p, n) {
+ c = mrp_list_entry(p, typeof(*c), hook);
+
+ status = execute_call(c, tbl);
+
+ if (status <= 0)
+ return status;
+ }
+ }
+
+ return TRUE;
+}
+
+
+static void simple_cleanup(mrp_scriptlet_t *s)
+{
+ MRP_UNUSED(s);
+ MRP_UNUSED(simple_dump);
+
+ return;
+}
+
+MRP_REGISTER_INTERPRETER("simple",
+ simple_compile, simple_prepare,
+ simple_execute, simple_cleanup);
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SIMPLE_SCRIPT_H__
+#define __MURPHY_SIMPLE_SCRIPT_H__
+
+#include <murphy/core/plugin.h>
+#include <murphy/core/scripting.h>
+
+typedef struct {
+ mrp_list_hook_t statements; /* list of (call) statements */
+} simple_script_t;
+
+
+typedef enum {
+ ARG_UNKNOWN = 0,
+ ARG_CONST_VALUE,
+ ARG_CONTEXT_VAR,
+ ARG_CONTEXT_SET,
+} arg_type_t;
+
+
+typedef struct {
+ arg_type_t type; /* ARG_CONST_VALUE */
+ mrp_script_value_t value; /* actual value */
+} const_arg_t;
+
+
+typedef struct {
+ arg_type_t type; /* ARG_CONTEXT_VAR */
+ char *name; /* name of variable */
+ int id; /* variable id */
+} ctx_val_arg_t;
+
+
+typedef struct {
+ arg_type_t type; /* ARG_CONTEXT_SET */
+ char *name; /* name of variable */
+ int id; /* variable id */
+ mrp_script_value_t value; /* value to set to */
+} ctx_set_arg_t;
+
+
+typedef union {
+ arg_type_t type; /* argument type */
+ const_arg_t cst; /* constant argument */
+ ctx_val_arg_t val; /* context variable value */
+ ctx_set_arg_t set; /* context variable assignment */
+} arg_t;
+
+
+typedef struct {
+ char *name; /* name of the function to call */
+ arg_t *args; /* arguments to pass */
+ int narg; /* number of arguments */
+ mrp_list_hook_t hook; /* to list of statements */
+
+ int (*script_ptr)(mrp_plugin_t *plugin, const char *name,
+ mrp_script_env_t *env);
+ mrp_plugin_t *plugin;
+} function_call_t;
+
+
+#endif /* __MURPHY_SIMPLE_SCRIPT_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SIMPLE_SCRIPT_TOKEN_H__
+#define __MURPHY_SIMPLE_SCRIPT_TOKEN_H__
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ * common token fields
+ */
+
+#define SIMPLE_TOKEN_FIELDS \
+ const char *token; /* token string */ \
+ int line; /* and on this line */ \
+ size_t size /* token size */
+
+/*
+ * a generic token
+ */
+
+typedef struct {
+ SIMPLE_TOKEN_FIELDS;
+} tkn_any_t;
+
+
+/*
+ * a string token
+ */
+
+typedef struct {
+ SIMPLE_TOKEN_FIELDS;
+ char *value;
+} tkn_string_t;
+
+
+#define DEFINE_INTEGER_TOKEN(_ttype, _ctype) \
+ typedef struct { \
+ SIMPLE_TOKEN_FIELDS; \
+ _ctype value; \
+ } tkn_##_ttype##_t
+
+#define DEFINE_ARRAY_TOKEN(_ttype, _cype) \
+ typedef struct { \
+ SIMPLE_TOKEN_FIELDS; \
+ _ctype *values; \
+ int nvalue; \
+ } tkn_##_ttype##_arr
+
+
+DEFINE_INTEGER_TOKEN(u8 , uint8_t );
+DEFINE_INTEGER_TOKEN(s8 , int8_t );
+DEFINE_INTEGER_TOKEN(u16, uint16_t);
+DEFINE_INTEGER_TOKEN(s16, int16_t);
+DEFINE_INTEGER_TOKEN(u32, uint32_t);
+DEFINE_INTEGER_TOKEN(s32, int32_t);
+DEFINE_INTEGER_TOKEN(u64, uint64_t);
+DEFINE_INTEGER_TOKEN(s64, int64_t);
+
+typedef struct {
+ SIMPLE_TOKEN_FIELDS;
+ double value;
+} tkn_dbl_t;
+
+/*
+DEFINE_ARRAY_TOKEN(u8 , uint8_t );
+DEFINE_ARRAY_TOKEN(s8 , int8_t );
+DEFINE_ARRAY_TOKEN(u16, uint16_t);
+DEFINE_ARRAY_TOKEN(u16, uint16_t);
+DEFINE_ARRAY_TOKEN(s16, int16_t);
+DEFINE_ARRAY_TOKEN(u32, uint32_t);
+DEFINE_ARRAY_TOKEN(s32, int32_t);
+DEFINE_ARRAY_TOKEN(u64, uint64_t);
+DEFINE_ARRAY_TOKEN(s64, int64_t);
+DEFINE_ARRAY_TOKEN(str, char * );
+*/
+
+
+/*
+ * string array tokens
+ */
+
+typedef struct {
+ SIMPLE_TOKEN_FIELDS;
+ int nstr;
+ char **strs;
+} tkn_strarr_t;
+
+
+/*
+ * function call arguments
+ */
+
+typedef struct {
+ SIMPLE_TOKEN_FIELDS;
+ arg_t *args;
+ int narg;
+} tkn_args_t;
+
+
+/*
+ * a constant value
+ */
+
+typedef struct {
+ SIMPLE_TOKEN_FIELDS;
+ mrp_script_value_t value;
+} tkn_value_t;
+
+/*
+ * an expression
+ */
+
+typedef enum {
+ EXPR_UNKNOWN = 0,
+ EXPR_CONSTANT, /* a constant value */
+ EXPR_CONTEXT_VALUE, /* a context variable value */
+ EXPR_CONTEXT_SET /* a context variable assignment */
+} expr_type_t;
+
+
+typedef struct {
+ expr_type_t type; /* EXPR_CONSTANT */
+ mrp_script_value_t value; /* constant value with a type */
+} const_expr_t;
+
+typedef struct {
+ expr_type_t type; /* EXPR_CONTEXT_VALUE */
+ char *name; /* context variable name */
+} ctx_val_expr_t;
+
+typedef struct {
+ expr_type_t type; /* EXPR_CONTEXT_SET */
+ char *name; /* context variable name */
+ mrp_script_value_t value; /* value to set */
+} ctx_set_expr_t;
+
+typedef union {
+ expr_type_t type; /* expression type, EXPR_* */
+ const_expr_t cst; /* constant expression */
+ ctx_val_expr_t val; /* context variable value */
+ ctx_set_expr_t set; /* context variable assignment */
+} tkn_expr_t;
+
+#ifdef __MURPHY_SIMPLE_SCRIPT_CHECK_RINGBUF__
+# define SIMPLE_TOKEN_DONE(t) memset((t).token, 0, (t).size)
+#else
+# define SIMPLE_TOKEN_DONE(t) do {} while (0)
+#endif
+
+#define SIMPLE_TOKEN_SAVE(str, size) save_token((str), (size))
+
+
+#endif /* __MURPHY_SIMPLE_SCRIPT_TOKEN_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+
+#include "scanner.h"
+#include "resolver.h"
+#include "resolver-types.h"
+#include "fact.h"
+#include "target.h"
+#include "target-sorter.h"
+
+
+/*
+ * dependency graph used to determine target update orders
+ */
+
+typedef struct {
+ mrp_resolver_t *r; /* resolver context */
+ int *edges; /* edges between nodes */
+ int nnode; /* number of graph nodes */
+} graph_t;
+
+
+static graph_t *build_graph(mrp_resolver_t *r);
+static int sort_graph(graph_t *g, int target_idx);
+static void free_graph(graph_t *g);
+static void dump_graph(graph_t *g, FILE *fp);
+
+
+int sort_targets(mrp_resolver_t *r)
+{
+ graph_t *g;
+ int i, status;
+
+ g = build_graph(r);
+
+ if (g != NULL) {
+ dump_graph(g, stdout);
+
+ status = 0;
+
+ for (i = 0; i < r->ntarget; i++) {
+ target_t *t = r->targets + i;
+
+ mrp_free(t->update_targets);
+ mrp_free(t->update_facts);
+ mrp_free(t->fact_stamps);
+ mrp_free(t->directs);
+ t->update_targets = NULL;
+ t->update_facts = NULL;
+ t->fact_stamps = NULL;
+ t->ndirect = 0;
+ }
+
+ for (i = 0; i < r->ntarget; i++) {
+ if (sort_graph(g, i) < 0) {
+ mrp_log_error("Failed to determine update order for "
+ "resolver target '%s'.", r->targets[i].name);
+
+ if (errno == ELOOP)
+ mrp_log_error("Cyclic dependency detected.");
+
+ status = -1;
+ break;
+ }
+ }
+
+ free_graph(g);
+ }
+ else
+ status = -1;
+
+ return status;
+}
+
+
+static inline int fact_id(graph_t *g, char *fact)
+{
+ int i;
+
+ for (i = 0; i < g->r->nfact; i++)
+ if (!strcmp(fact, g->r->facts[i].name))
+ return i;
+
+ return -1;
+}
+
+
+static inline int target_id(graph_t *g, char *target)
+{
+ int i;
+
+ for (i = 0; i < g->r->ntarget; i++)
+ if (!strcmp(target, g->r->targets[i].name))
+ return g->r->nfact + i;
+
+ return -1;
+}
+
+
+static inline char *node_name(graph_t *g, int id)
+{
+ if (id < g->r->nfact)
+ return g->r->facts[id].name;
+ else
+ return g->r->targets[id - g->r->nfact].name;
+}
+
+
+static inline int node_id(graph_t *g, char *name)
+{
+ if (name[0] == '$')
+ return fact_id(g, name);
+ else
+ return target_id(g, name);
+}
+
+
+static inline int *edge_markp(graph_t *g, int n1, int n2)
+{
+ static int invalid = 0;
+
+ if (n1 >= 0 && n2 >= 0)
+ return g->edges + (n1 * g->nnode) + n2;
+ else
+ return &invalid;
+}
+
+
+static inline void mark_node(graph_t *g, int node)
+{
+ *edge_markp(g, node, node) = 1;
+}
+
+
+static inline void unmark_node(graph_t *g, int node)
+{
+ *edge_markp(g, node, node) = 0;
+}
+
+
+static inline int node_present(graph_t *g, int node)
+{
+ return *edge_markp(g, node, node) == 1;
+}
+
+
+static graph_t *build_graph(mrp_resolver_t *r)
+{
+ graph_t *g;
+ int tid, did, i, j;
+ target_t *t;
+
+ g = mrp_allocz(sizeof(*g));
+
+ if (g != NULL) {
+ g->r = r;
+ g->nnode = r->nfact + r->ntarget;
+ g->edges = mrp_allocz(g->nnode * g->nnode * sizeof(*g->edges));
+
+ if (g->edges == NULL)
+ goto fail;
+
+ for (i = 0; i < r->ntarget; i++) {
+ t = r->targets + i;
+ tid = r->nfact + i;
+
+ for (j = 0; j < t->ndepend; j++) {
+ mrp_debug("adding edge: %s <- %s", t->depends[j], t->name);
+ did = node_id(g, t->depends[j]);
+
+ if (did < 0)
+ goto fail;
+
+ *edge_markp(g, did, tid) = 1;
+ }
+ }
+ }
+
+ return g;
+
+ fail:
+ if (g != NULL) {
+ mrp_free(g->edges);
+ mrp_free(g);
+ }
+ return NULL;
+}
+
+
+static int mark_present_nodes(graph_t *g, int target_idx)
+{
+ int tid, did, i;
+ target_t *t;
+
+ tid = g->r->nfact + target_idx;
+ t = g->r->targets + target_idx;
+
+ if (node_present(g, tid))
+ return TRUE;
+
+ mark_node(g, tid);
+
+ for (i = 0; i < t->ndepend; i++) {
+ did = node_id(g, t->depends[i]);
+
+ if (did < 0)
+ return FALSE;
+
+ if (*t->depends[i] == '$')
+ mark_node(g, did);
+ else {
+ if (!mark_present_nodes(g, did - g->r->nfact))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/*
+ * queues we use for topological sorting
+ */
+
+typedef struct {
+ int size; /* max queue capacity */
+ int *items; /* queue item buffer */
+ int head; /* push index */
+ int tail; /* pop index */
+} que_t;
+
+#define EMPTY_QUE {.items = NULL } /* initializer for empty queue */
+
+
+static int que_init(que_t *q, int size)
+{
+ mrp_free(q->items);
+
+ q->items = mrp_alloc(size * sizeof(*q->items));
+
+ if (q->items != NULL) {
+ q->size = size;
+ q->head = 0;
+ q->tail = 0;
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static void que_cleanup(que_t *q)
+{
+ mrp_free(q->items);
+ q->items = NULL;
+}
+
+
+static void que_push(que_t *q, int item)
+{
+ /* we know the max size, so we don't check for overflow here */
+ q->items[q->tail++] = item;
+ q->tail %= q->size;
+}
+
+
+static int que_pop(que_t *q, int *itemp)
+{
+ if (q->head != q->tail) {
+ *itemp = q->items[q->head++];
+ q->head %= q->size;
+
+ return TRUE;
+ }
+ else {
+ *itemp = -1;
+ return FALSE;
+ }
+}
+
+
+static int sort_graph(graph_t *g, int target_idx)
+{
+ /*
+ * Notes:
+ *
+ * We perform a topological sort of the dependency graph here
+ * for our target with the given idx. We include only nodes
+ * for facts and targets which are relevant for our target.
+ * These are the ones which our target directly or indirectly
+ * depends on. We use the otherwise unused diagonal (no target
+ * can depend on itself) of our edge matrix to mark which nodes
+ * are present in the graph. Then we use the following algorithm
+ * to sort the subgraph of relevant nodes:
+ *
+ * initialize que L to be empty
+ * initialize que Q with all nodes without incoming edges
+ * while Q is not empty
+ * pop a node <n> from Q
+ * push <n> to L
+ * remove all edges that start from <n>
+ * for all nodes <m> where we removed an incoming node
+ * if <m> has no more incoming edges
+ * push <m> to Q
+ *
+ * if there are any remaining edges
+ * return an error about cyclic dependency
+ * else
+ * L is the sorted subgraph (our target is the last item in L)
+ *
+ * The resulted sort order of our target is then used as the
+ * dependency check/update order when the resolver is asked to
+ * update that target.
+ */
+
+ target_t *target;
+ int edges[g->nnode * g->nnode];
+ que_t L = EMPTY_QUE, Q = EMPTY_QUE;
+ int i, j, m, id, node, nedge, nfact, ntarget;
+
+ target = g->r->targets + target_idx;
+
+ /* save full graph */
+ memcpy(edges, g->edges, sizeof(edges));
+
+ if (!que_init(&L, g->nnode + 1) || !que_init(&Q, g->nnode))
+ goto fail;
+
+ /* find and mark relevant nodes in the graph */
+ mark_present_nodes(g, target_idx);
+
+ mrp_debug("-- target %s --", target->name);
+ /*dump_graph(g, stdout);*/
+
+ /* push all relevant facts, they do not depend on anything */
+ for (i = 0; i < g->r->nfact; i++) {
+ id = i;
+ if (node_present(g, id)) {
+ que_push(&Q, id);
+ unmark_node(g, id);
+ }
+ }
+
+ /* push all relevant targets that have no dependencies */
+ for (i = 0; i < g->r->ntarget; i++) {
+ id = g->r->nfact + i;
+ if (g->r->targets[i].depends == NULL && node_present(g, id)) {
+ que_push(&Q, id);
+ unmark_node(g, id);
+ }
+ }
+
+ /* try sorting the marked subgraph */
+ while (que_pop(&Q, &node)) {
+ que_push(&L, node);
+
+ mrp_debug("popped node %s", node_name(g, node));
+
+ for (m = 0; m < g->nnode; m++) {
+ if (m == node || !node_present(g, m))
+ continue;
+ *edge_markp(g, node, m) = 0;
+ nedge = 0;
+ for (j = 0; j < g->nnode; j++) {
+ if (j == m)
+ continue;
+ if (node_present(g, j) && *edge_markp(g, j, m))
+ nedge++;
+ }
+ if (nedge == 0) {
+ mrp_debug("node %s empty, pushing it", node_name(g, m));
+ que_push(&Q, m);
+ unmark_node(g, m);
+ }
+ else
+ mrp_debug("node %s not empty yet", node_name(g, m));
+ }
+ }
+
+ /* check if the subgraph has any remaining edges */
+ nedge = 0;
+ for (node = 0; node < g->nnode; node++) {
+ if (!node_present(g, node))
+ continue;
+ for (m = 0; m < g->nnode; m++) {
+ if (m == node || !node_present(g, m))
+ continue;
+ if (*edge_markp(g, node, m) == 1) {
+ errno = ELOOP;
+ goto fail;
+ }
+ }
+ }
+
+ mrp_debug("----- %s: graph sorted successfully -----", target->name);
+
+ for (i = 0; i < L.tail; i++)
+ mrp_debug(" %s", node_name(g, L.items[i]));
+ mrp_debug("-----");
+
+ /* save the result in the given target */
+ if (L.tail > 0) {
+ nfact = 0;
+ ntarget = 0;
+
+ for (i = 0; i < L.tail; i++) {
+ if (L.items[i] < g->r->nfact)
+ nfact++;
+ else
+ ntarget++;
+ }
+
+ if (nfact > 0) {
+ target->update_facts = mrp_alloc_array(int, nfact + 1);
+ target->fact_stamps = mrp_allocz_array(uint32_t, nfact);
+
+ if (target->update_facts != NULL && target->fact_stamps != NULL) {
+ for (i = 0; i < nfact; i++)
+ target->update_facts[i] = L.items[i];
+ target->update_facts[i] = -1;
+ }
+ else
+ goto fail;
+ }
+
+ if (ntarget > 0) {
+ target->update_targets = mrp_alloc_array(int, ntarget + 1);
+ if (target->update_targets != NULL) {
+ for (i = 0; i < ntarget; i++)
+ target->update_targets[i] = L.items[nfact+i] - g->r->nfact;
+ target->update_targets[i] = -1;
+ }
+ else
+ goto fail;
+ }
+
+ target->ndirect = 0;
+ target->directs = mrp_allocz_array(int, target->ndepend);
+
+ if (target->ndepend == 0 || target->directs != NULL) {
+ fact_t *f;
+ target_t *t;
+
+ for (i = 0; i < target->ndepend; i++) {
+ if (*target->depends[i] != '$')
+ continue;
+
+ f = lookup_fact(g->r, target->depends[i]);
+
+ if (f != NULL)
+ target->directs[target->ndirect++] = f - g->r->facts;
+ else
+ target->directs[target->ndirect++] = -1;
+ }
+
+ for (i = 0; i < target->ndepend; i++) {
+ if (*target->depends[i] == '$')
+ continue;
+
+ t = lookup_target(g->r, target->depends[i]);
+
+ if (t != NULL)
+ target->directs[target->ndirect++] = g->r->nfact +
+ t - g->r->targets;
+ else
+ target->directs[target->ndirect++] = -1;
+ }
+ }
+ else
+ goto fail;
+ }
+
+ que_cleanup(&L);
+ que_cleanup(&Q);
+
+ /* restore the original full graph */
+ memcpy(g->edges, edges, sizeof(edges));
+
+ return 0;
+
+ fail:
+ que_cleanup(&L);
+ que_cleanup(&Q);
+
+ memcpy(g->edges, edges, sizeof(edges));
+
+ return -1;
+}
+
+
+static void free_graph(graph_t *g)
+{
+ if (g != NULL) {
+ mrp_free(g->edges);
+ mrp_free(g);
+ }
+}
+
+
+static void dump_graph(graph_t *g, FILE *fp)
+{
+ int i, j;
+
+ fprintf(fp, "Graph edges:\n");
+
+ fprintf(fp, " %20.20s ", "");
+ for (i = 0; i < g->nnode; i++)
+ fprintf(fp, " %d", i % 10);
+ fprintf(fp, "\n");
+
+ for (i = 0; i < g->nnode; i++) {
+ fprintf(fp, " %20.20s: ", node_name(g, i));
+ for (j = 0; j < g->nnode; j++)
+ fprintf(fp, "%d ", *edge_markp(g, i, j));
+ fprintf(fp, "\n");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_TARGET_SORTER_H__
+#define __MURPHY_RESOLVER_TARGET_SORTER_H__
+
+#include "resolver-types.h"
+#include "resolver.h"
+
+int sort_targets(mrp_resolver_t *r);
+
+#endif /* __MURPHY_RESOLVER_TARGET_SORTER_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <errno.h>
+#include <alloca.h>
+
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+
+#include <murphy/core/scripting.h>
+
+#include "resolver-types.h"
+#include "resolver.h"
+#include "fact.h"
+#include "events.h"
+#include "target-sorter.h"
+#include "target.h"
+
+
+
+int create_targets(mrp_resolver_t *r, yy_res_parser_t *parser)
+{
+ mrp_list_hook_t *lp, *ln;
+ yy_res_target_t *pt;
+ int auto_update;
+
+ auto_update = -1;
+
+ mrp_list_foreach(&parser->targets, lp, ln) {
+ pt = mrp_list_entry(lp, typeof(*pt), hook);
+
+ if (create_target(r, pt->name, (const char **)pt->depends, pt->ndepend,
+ pt->script_type, pt->script_source) == NULL)
+
+ return -1;
+
+ if (parser->auto_update != NULL) {
+ if (!strcmp(parser->auto_update, pt->name))
+ auto_update = r->ntarget - 1;
+ }
+ }
+
+ if (auto_update >= 0)
+ r->auto_update = r->targets + auto_update;
+ else {
+ if (parser->auto_update != NULL) {
+ mrp_log_error("Auto-update target '%s' does not exist.",
+ parser->auto_update);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void purge_target(target_t *t)
+{
+ int i;
+
+ mrp_free(t->name);
+ mrp_free(t->update_facts);
+ mrp_free(t->update_targets);
+ mrp_free(t->fact_stamps);
+ mrp_free(t->directs);
+
+ for (i = 0; i < t->ndepend; i++)
+ mrp_free(t->depends[i]);
+ mrp_free(t->depends);
+
+ mrp_destroy_script(t->script);
+}
+
+
+void destroy_targets(mrp_resolver_t *r)
+{
+ target_t *t;
+ int i;
+
+ for (i = 0, t = r->targets; i < r->ntarget; i++, t++)
+ purge_target(t);
+
+ mrp_free(r->targets);
+
+ if (r->auto_scheduled != NULL) {
+ mrp_del_deferred(r->auto_scheduled);
+ r->auto_scheduled = NULL;
+ }
+}
+
+
+target_t *create_target(mrp_resolver_t *r, const char *target,
+ const char **depends, int ndepend,
+ const char *script_type, const char *script_source)
+{
+ target_t *t;
+ size_t old_size, new_size;
+ int i, j, found, nduplicate;
+
+ for (i = 0, t = r->targets; i < r->ntarget; i++, t++) {
+ if (!strcmp(t->name, target)) {
+ errno = EEXIST;
+ return NULL;
+ }
+ }
+
+ old_size = sizeof(*r->targets) * r->ntarget;
+ new_size = sizeof(*r->targets) * (r->ntarget + 1);
+
+ if (!mrp_reallocz(r->targets, old_size, new_size))
+ return NULL;
+
+ t = r->targets + r->ntarget++;
+ t->name = mrp_strdup(target);
+
+ if (t->name == NULL)
+ goto undo_and_fail;
+
+ if (depends != NULL) {
+ t->depends = mrp_allocz_array(char *, ndepend);
+
+ if (t->depends != NULL) {
+ nduplicate = 0;
+ for (i = 0; i < ndepend; i++) {
+ found = FALSE;
+ for (j = 0; j < i; j++)
+ if (!strcmp(depends[i], depends[j]))
+ found = TRUE;
+ if (!found) {
+ t->depends[i - nduplicate] = mrp_strdup(depends[i]);
+
+ if (t->depends[i] == NULL)
+ goto undo_and_fail;
+ }
+ else
+ nduplicate++;
+ }
+
+ t->ndepend = ndepend - nduplicate;
+
+ if (nduplicate > 0) {
+ mrp_reallocz(t->depends, ndepend, t->ndepend);
+ mrp_log_warning("Filtered out %d duplicate%s dependencies "
+ "from target '%s'.", nduplicate,
+ nduplicate == 1 ? "" : "s", t->name);
+ }
+ }
+ else
+ goto undo_and_fail;
+ }
+
+ for (i = 0; i < t->ndepend; i++) {
+ if (*t->depends[i] == '$')
+ if (!create_fact(r, t->depends[i]))
+ goto undo_and_fail;
+ }
+
+ if (script_source != NULL) {
+ t->script = mrp_create_script(script_type, script_source);
+
+ if (t->script == NULL) {
+ if (errno == ENOENT)
+ mrp_log_error("Unsupported script type '%s' used in "
+ "target '%s'.", script_type, t->name);
+ else
+ mrp_log_error("Failed to set up script for target '%s'.",
+ t->name);
+
+ goto undo_and_fail;
+ }
+ }
+
+ return t;
+
+
+ undo_and_fail:
+ purge_target(t);
+ mrp_realloc(r->targets, old_size);
+ r->ntarget--;
+
+ return NULL;
+}
+
+
+int generate_autoupdate_target(mrp_resolver_t *r, const char *name)
+{
+ const char **depends;
+ int ndepend, i;
+ target_t *t, *at;
+
+ if (r->auto_update != NULL)
+ return FALSE;
+
+ mrp_debug("constructing autoupdate target '%s'...", name);
+
+ if (r->ntarget > 0) {
+ depends = alloca(r->ntarget * sizeof(depends[0]));
+ ndepend = 0;
+
+ if (sort_targets(r) != 0) {
+ mrp_debug("failed to sort dependency graph");
+ return FALSE;
+ }
+
+ for (i = 0, t = r->targets; i < r->ntarget; i++, t++) {
+ if (t->update_facts != NULL && t->update_facts[0] >= 0) {
+ mrp_debug(" including target '%s' (%s)", t->name,
+ fact_name(r, t->update_facts[0]));
+ depends[ndepend] = t->name;
+ ndepend++;
+ }
+ else
+ mrp_debug(" excluding target '%s'", t->name);
+ }
+ }
+ else {
+ depends = NULL;
+ ndepend = 0;
+ }
+
+ at = create_target(r, name, depends, ndepend, NULL, NULL);
+
+ if (at != NULL) {
+ r->auto_update = at;
+
+ return (sort_targets(r) == 0);
+ }
+ else
+ return FALSE;
+}
+
+
+int compile_target_scripts(mrp_resolver_t *r)
+{
+ target_t *t;
+ int i;
+
+ for (i = 0, t = r->targets; i < r->ntarget; i++, t++) {
+ if (!t->prepared) {
+ if (mrp_compile_script(t->script) < 0) {
+ mrp_log_error("Failed to compile script for target '%s'.",
+ t->name);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int prepare_target_scripts(mrp_resolver_t *r)
+{
+ target_t *t;
+ int i;
+
+ for (i = 0, t = r->targets; i < r->ntarget; i++, t++) {
+ if (!t->prepared) {
+ if (mrp_prepare_script(t->script) == 0)
+ t->prepared = TRUE;
+ else {
+ mrp_log_error("Failed to prepare script for target '%s'.",
+ t->name);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int older_than_facts(mrp_resolver_t *r, target_t *t)
+{
+ int i, id;
+
+ /*
+ * If a target does not depend directly or indirectly on any
+ * facts, it always needs to be updated and is considered to
+ * be older than its (nonexistent) fact dependencies even if
+ * this seems a bit unintuitive at first. If there are fact
+ * dependencies the target is considered older if any of the
+ * facts have a newer stamp than the target.
+ */
+
+ if (t->update_facts == NULL)
+ return TRUE;
+ else {
+#ifdef CHECK_TRANSITIVE_CLOSURE_OF_FACTS
+ for (i = 0; (id = t->update_facts[i]) >= 0; i++) {
+ if (fact_stamp(r, id) > t->fact_stamps[i])
+ return TRUE;
+ }
+#else
+ for (i = 0; i < t->ndirect; i++) {
+ id = t->directs[i];
+
+ if (id < r->nfact) {
+ if (fact_stamp(r, id) > t->fact_stamps[i])
+ return TRUE;
+ }
+ }
+#endif
+ }
+
+ return FALSE;
+}
+
+
+static int older_than_targets(mrp_resolver_t *r, target_t *t)
+{
+ int i, id;
+ target_t *dep;
+
+ /*
+ * Although the target itself is always the last item in its own
+ * sorted target dependencies (as the list is the topologically
+ * sorted dependency graph) we don't special case this out here as
+ * a target cannot be newer than itself by definition.
+ */
+
+ for (i = 0; (id = t->update_targets[i]) >= 0; i++) {
+ dep = r->targets + id;
+
+ if (dep->stamp > t->stamp)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static void save_fact_stamps(mrp_resolver_t *r, target_t *t, uint32_t *buf)
+{
+ int id, idx, i;
+
+ if (t->update_facts != NULL) {
+ id = t - r->targets;
+ idx = id * r->nfact;
+
+ for (i = 0; (id = t->update_facts[i]) >= 0; i++, idx++)
+ buf[idx] = t->fact_stamps[i];
+ }
+}
+
+
+static void restore_fact_stamps(mrp_resolver_t *r, target_t *t, uint32_t *buf)
+{
+ int id, idx, i;
+
+ if (t->update_facts != NULL) {
+ id = t - r->targets;
+ idx = id * r->nfact;
+
+ for (i = 0; (id = t->update_facts[i]) >= 0; i++, idx++)
+ t->fact_stamps[i] = buf[idx];
+ }
+}
+
+
+static void save_target_stamps(mrp_resolver_t *r, target_t *t, uint32_t *buf)
+{
+ target_t *dep;
+ int i, id;
+
+ for (i = 0; (id = t->update_targets[i]) >= 0; i++) {
+ dep = r->targets + id;
+ save_fact_stamps(r, dep, buf);
+ }
+}
+
+
+static void restore_target_stamps(mrp_resolver_t *r, target_t *t, uint32_t *buf)
+{
+ target_t *dep;
+ int i, id;
+
+ for (i = 0; (id = t->update_targets[i]) >= 0; i++) {
+ dep = r->targets + id;
+ restore_fact_stamps(r, dep, buf);
+ }
+}
+
+
+static void update_target_stamps(mrp_resolver_t *r, target_t *t)
+{
+ int i, id;
+
+ if (t->update_facts != NULL)
+ for (i = 0; (id = t->update_facts[i]) >= 0; i++)
+ t->fact_stamps[i] = fact_stamp(r, id);
+
+ t->stamp = r->stamp;
+}
+
+
+static int update_target(mrp_resolver_t *r, target_t *t)
+{
+ mqi_handle_t tx;
+ target_t *dep;
+ uint32_t stamps[r->ntarget * r->nfact];
+ int i, id, status, needs_update, level;
+
+ tx = start_transaction(r);
+
+ if (tx == MQI_HANDLE_INVALID) {
+ if (errno != 0)
+ return -errno;
+ else
+ return -EINVAL;
+ }
+
+ r->stamp = r->stamp + 1;
+
+ level = r->level++;
+ emit_resolver_event(r, RESOLVER_UPDATE_STARTED, t->name, level);
+
+ save_target_stamps(r, t, stamps);
+
+ status = TRUE;
+ needs_update = older_than_facts(r, t);
+
+ for (i = 0; (id = t->update_targets[i]) >= 0; i++) {
+ dep = r->targets + id;
+
+ if (dep == t)
+ break;
+
+ /* hmm... is this really needed? */
+ if (older_than_facts(r, dep) || older_than_targets(r, dep)) {
+ needs_update = TRUE;
+ status = mrp_execute_script(dep->script, r->ctbl);
+
+ if (status <= 0)
+ break;
+ else
+ update_target_stamps(r, dep);
+ }
+ }
+
+ if (needs_update && status > 0) {
+ status = mrp_execute_script(t->script, r->ctbl);
+
+ if (status > 0)
+ update_target_stamps(r, t);
+ }
+
+ if (status <= 0) {
+ rollback_transaction(r, tx);
+ restore_target_stamps(r, t, stamps);
+ emit_resolver_event(r, RESOLVER_UPDATE_FAILED, t->name, level);
+ }
+ else {
+ if (!commit_transaction(r, tx)) {
+ restore_target_stamps(r, t, stamps);
+ if (errno != 0)
+ status = -errno;
+ else
+ status = -EINVAL;
+ }
+ }
+
+ if (status <= 0)
+ emit_resolver_event(r, RESOLVER_UPDATE_FAILED, t->name, level);
+ else
+ emit_resolver_event(r, RESOLVER_UPDATE_DONE , t->name, level);
+
+ r->level--;
+
+ return status;
+}
+
+
+target_t *lookup_target(mrp_resolver_t *r, const char *name)
+{
+ target_t *t;
+ int i;
+
+ for (i = 0, t = r->targets; i < r->ntarget; i++, t++) {
+ if (!strcmp(t->name, name))
+ return t;
+ }
+
+ return NULL;
+}
+
+
+int update_target_by_name(mrp_resolver_t *r, const char *name)
+{
+ target_t *t = lookup_target(r, name);
+
+ if (t != NULL)
+ return update_target(r, t);
+ else
+ return FALSE;
+}
+
+
+int update_target_by_id(mrp_resolver_t *r, int id)
+{
+ if (id < r->ntarget)
+ return update_target(r, r->targets + id);
+ else
+ return FALSE;
+}
+
+
+static int autoupdate_target(mrp_resolver_t *r)
+{
+ if (r->auto_update != NULL)
+ return mrp_resolver_update_targetl(r, r->auto_update->name, NULL);
+ else
+ return TRUE;
+}
+
+
+static void autoupdate_cb(mrp_deferred_t *d, void *user_data)
+{
+ mrp_resolver_t *r = (mrp_resolver_t *)user_data;
+
+ mrp_debug("running scheduled target autoupdate");
+ mrp_disable_deferred(d);
+ autoupdate_target(r);
+}
+
+
+int schedule_target_autoupdate(mrp_resolver_t *r)
+{
+ if (r->auto_update != NULL) {
+ if (r->ctx != NULL && r->auto_scheduled == NULL)
+ r->auto_scheduled = mrp_add_deferred(r->ctx->ml, autoupdate_cb, r);
+
+ if (r->auto_scheduled != NULL)
+ mrp_enable_deferred(r->auto_scheduled);
+ else
+ return FALSE;
+
+ mrp_debug("scheduled target autoupdate (%s)", r->auto_update->name);
+ }
+
+ return TRUE;
+}
+
+
+void dump_targets(mrp_resolver_t *r, FILE *fp)
+{
+ int i, j, idx;
+ target_t *t;
+
+ for (i = 0; i < r->ntarget; i++) {
+ t = r->targets + i;
+ fprintf(fp, "#%d: %s (@%u)\n", i, t->name, t->stamp);
+
+ fprintf(fp, " dependencies:");
+ if (t->depends != NULL) {
+ for (j = 0; j < t->ndepend; j++)
+ fprintf(fp, " %s", t->depends[j]);
+ fprintf(fp, "\n");
+
+ fprintf(fp, " facts to check:");
+ if (t->update_facts != NULL) {
+ for (j = 0; (idx = t->update_facts[j]) >= 0; j++)
+ fprintf(fp, " %s (@%u)", r->facts[idx].name,
+ t->fact_stamps[j]);
+ fprintf(fp, "\n");
+ }
+ else
+ fprintf(fp, "<none>\n");
+
+ fprintf(fp, " target update order:");
+ if (t->update_targets != NULL) {
+ for (j = 0; (idx = t->update_targets[j]) >= 0; j++)
+ fprintf(fp, " %s (@%u)", r->targets[idx].name,
+ r->targets[idx].stamp);
+ fprintf(fp, "\n");
+ }
+ else
+ fprintf(fp, "<none>\n");
+
+ fprintf(fp, " direct dependencies:");
+ if (t->ndirect > 0) {
+ for (j = 0; j < t->ndirect; j++) {
+ idx = t->directs[j];
+ if (idx < r->nfact)
+ fprintf(fp, " %s", r->facts[idx].name);
+ else
+ fprintf(fp, " %s", r->targets[idx-r->nfact].name);
+ }
+ fprintf(fp, "\n");
+ }
+ else
+ fprintf(fp, "<none>\n");
+ }
+ else
+ fprintf(fp, " <none>\n");
+
+ if (t->script != NULL) {
+ if (t->script->source != NULL) {
+ fprintf(fp, " update script (%s):\n",
+ t->script->interpreter->name);
+ fprintf(fp, "%s", t->script->source);
+ fprintf(fp, " end script\n");
+ }
+ else if (t->script->data != NULL) {
+ fprintf(fp, " precompiled update (%s):\n",
+ t->script->interpreter->name);
+ fprintf(fp, " %p\n", t->script->data);
+ fprintf(fp, " end script\n");
+ }
+ }
+ else
+ fprintf(fp, " no update script\n");
+ }
+}
+
+typedef enum {
+ DOT_NODE_TYPE_FACT,
+ DOT_NODE_TYPE_TABLE,
+ DOT_NODE_TYPE_SINK,
+ DOT_NODE_TYPE_SELECT,
+ DOT_NODE_TYPE_OTHER,
+} dot_node_type_t;
+
+
+static dot_node_type_t dot_node_type(char *name)
+{
+ if (!name)
+ return DOT_NODE_TYPE_OTHER;
+
+ if (name[0] == '$')
+ return DOT_NODE_TYPE_FACT;
+
+ if (strncmp(name, "_table_", 7) == 0)
+ return DOT_NODE_TYPE_TABLE;
+
+ if (strncmp(name, "_sink_", 6) == 0)
+ return DOT_NODE_TYPE_SINK;
+
+ if (strncmp(name, "_select_", 8) == 0)
+ return DOT_NODE_TYPE_SELECT;
+
+ return DOT_NODE_TYPE_OTHER;
+}
+
+
+static char *dot_fix(char *name)
+{
+ dot_node_type_t t = dot_node_type(name);
+
+ switch(t) {
+ case DOT_NODE_TYPE_FACT:
+ /* remove illegal characters */
+ return &name[1];
+
+ case DOT_NODE_TYPE_TABLE:
+ return &name[7];
+
+ case DOT_NODE_TYPE_SINK:
+ return &name[6];
+
+ case DOT_NODE_TYPE_SELECT:
+ return &name[8];
+
+ default:
+ break;
+ }
+ return name;
+}
+
+
+static char *dot_get_shape(dot_node_type_t t)
+{
+ switch(t) {
+ case DOT_NODE_TYPE_FACT:
+ case DOT_NODE_TYPE_TABLE:
+ return "box";
+
+ case DOT_NODE_TYPE_SINK:
+ return "trapezium";
+
+ case DOT_NODE_TYPE_SELECT:
+ return "diamond";
+
+ default:
+ break;
+ }
+ return "ellipse";
+}
+
+
+void mrp_resolver_dump_dot_graph(mrp_resolver_t *r, FILE *fp)
+{
+ int i, j;
+ target_t *t;
+
+ fprintf(fp, "digraph decision_graph {\n");
+
+ /* vertexes */
+ for (i = 0; i < r->ntarget; i++) {
+ dot_node_type_t i_type;
+ char *name;
+
+ t = r->targets + i;
+ name = dot_fix(t->name);
+ if (strcmp(name, "autoupdate") == 0)
+ continue;
+
+ i_type = dot_node_type(t->name);
+ fprintf(fp, " %s [shape=%s];\n", name, dot_get_shape(i_type));
+ }
+
+ fprintf(fp, "\n");
+
+ /* edges */
+ for (i = 0; i < r->ntarget; i++) {
+ t = r->targets + i;
+ char *i_name = dot_fix(t->name);
+
+ if (strcmp(i_name, "autoupdate") == 0)
+ continue;
+
+ if (t->depends != NULL) {
+ for (j = 0; j < t->ndepend; j++) {
+ char *j_name = dot_fix(t->depends[j]);
+
+ fprintf(fp, " %s -> %s;\n", i_name, j_name);
+ }
+ }
+ }
+
+ fprintf(fp, "}\n");
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_TARGET_H__
+#define __MURPHY_RESOLVER_TARGET_H__
+
+#include "resolver-types.h"
+#include "resolver.h"
+#include "parser-api.h"
+
+int create_targets(mrp_resolver_t *r, yy_res_parser_t *parser);
+void destroy_targets(mrp_resolver_t *r);
+target_t *create_target(mrp_resolver_t *r, const char *target,
+ const char **depends, int ndepend,
+ const char *script_type, const char *script_source);
+int generate_autoupdate_target(mrp_resolver_t *r, const char *name);
+int compile_target_scripts(mrp_resolver_t *r);
+int prepare_target_scripts(mrp_resolver_t *r);
+
+int update_target_by_name(mrp_resolver_t *r, const char *name);
+int update_target_by_id(mrp_resolver_t *r, int id);
+int schedule_target_autoupdate(mrp_resolver_t *r);
+
+target_t *lookup_target(mrp_resolver_t *s, const char *name);
+void dump_targets(mrp_resolver_t *r, FILE *fp);
+
+/** Dump the resolver dependency graph in DOT format. */
+void mrp_resolver_dump_dot_graph(mrp_resolver_t *r, FILE *fp);
+
+
+#endif /* __MURPHY_RESOLVER_TARGET_H__ */
--- /dev/null
+#auto-update-target all
+
+target all
+ depends on video_route audio_route \
+ audio_volume audio_cork audio_mute \
+ $vibra $backlight
+# signal_enforcement_points(&target='all', 'audio', 'video', 1, -2, +3)
+# foobar(&just='a test', +1, -2, 3.141, -1U8, 0xffffffffffU64)
+# hex(-0xf)
+ update script
+ echo('Running actions for target "all"...')
+ echo('foo', 'bar', 'foobar...', 1, 2, 3.141, -9.81)
+ end script
+
+include "test-input-video"
+include "test-input-audio"
+
+target luatest
+ depends on $audio_playback_owner
+ update script (lua)
+ print("Hello from a resolver update Lua scriptlet...")
+ end script
\ No newline at end of file
--- /dev/null
+target audio_route
+ depends on $audio_device_selectable $audio_output_configuration \
+ $resource_owner
+ update script
+ echo('Running audio_route actions...', 3, 2, 1, 0)
+ end script
+
+target audio_volume
+ depends on $resource_owner $resource_set
+ update script
+ echo('Running actions for target "audio_volume"...')
+ end script
+
+target audio_cork
+ depends on $resource_owner
+ update script
+ echo('Running actions for target "audio_cork"...')
+ end script
+
+target audio_mute
+ depends on $mute
+ update script
+ echo('Running actions for target "audio_mute"...')
+ end script
--- /dev/null
+target video_route
+ depends on $video_device_selectable $gconf
+ update script
+ echo('Running actions for target "video_route"...')
+ end script
--- /dev/null
+noinst_PROGRAMS = parser-test
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir) $(JSON_CFLAGS)
+
+# parser test
+parser_test_SOURCES = parser-test.c
+parser_test_CFLAGS = $(AM_CFLAGS)
+parser_test_LDADD = ../../libmurphy-resolver.la \
+ ../../murphy-db/mqi/libmqi.la \
+ ../../murphy-db/mdb/libmdb.la \
+ ../../libmurphy-common.la
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#define _GNU_SOURCE
+#include <getopt.h>
+
+#include <murphy/common.h>
+#include <murphy/core/method.h>
+#include <murphy/resolver/resolver.h>
+
+
+typedef struct {
+ const char *file;
+ mrp_resolver_t *r;
+ int log_mask;
+ const char *log_target;
+ int debug;
+} context_t;
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options] [transport-address]\n\n"
+ "The possible options are:\n"
+ " -f, --file input file to user\n"
+ " -t, --log-target=TARGET log target to use\n"
+ " TARGET is one of stderr,stdout,syslog, or a logfile path\n"
+ " -l, --log-level=LEVELS logging level to use\n"
+ " LEVELS is a comma separated list of info, error and warning\n"
+ " -v, --verbose increase logging verbosity\n"
+ " -d, --debug enable given debug confguration\n"
+ " -D, --list-debug list known debug sites\n"
+ " -h, --help show help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void config_set_defaults(context_t *ctx)
+{
+ mrp_clear(ctx);
+ ctx->file = "test-input";
+ ctx->log_mask = MRP_LOG_UPTO(MRP_LOG_DEBUG);
+ ctx->log_target = MRP_LOG_TO_STDERR;
+}
+
+
+int parse_cmdline(context_t *ctx, int argc, char **argv)
+{
+# define OPTIONS "f:l:t:d:vh"
+ struct option options[] = {
+ { "file" , required_argument, NULL, 'f' },
+ { "log-level" , required_argument, NULL, 'l' },
+ { "log-target", required_argument, NULL, 't' },
+ { "verbose" , optional_argument, NULL, 'v' },
+ { "debug" , required_argument, NULL, 'd' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ config_set_defaults(ctx);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'f':
+ ctx->file = optarg;
+ break;
+
+ case 'v':
+ ctx->log_mask <<= 1;
+ ctx->log_mask |= 1;
+ break;
+
+ case 'l':
+ ctx->log_mask = mrp_log_parse_levels(optarg);
+ if (ctx->log_mask < 0)
+ print_usage(argv[0], EINVAL, "invalid log level '%s'", optarg);
+ break;
+
+ case 't':
+ ctx->log_target = mrp_log_parse_target(optarg);
+ if (!ctx->log_target)
+ print_usage(argv[0], EINVAL, "invalid log target '%s'", optarg);
+ break;
+
+ case 'd':
+ ctx->debug = TRUE;
+ mrp_debug_set_config(optarg);
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%c'", opt);
+ }
+ }
+
+ return TRUE;
+}
+
+
+int main(int argc, char *argv[])
+{
+ context_t c;
+ int i;
+ char *target;
+
+ if (!parse_cmdline(&c, argc, argv))
+ exit(1);
+
+ mrp_log_set_mask(c.log_mask);
+ mrp_log_set_target(c.log_target);
+
+ if (c.debug)
+ mrp_debug_enable(TRUE);
+
+ c.r = mrp_resolver_parse(NULL, NULL, c.file);
+
+ if (c.r == NULL)
+ mrp_log_error("Failed to parse input file '%s'.", c.file);
+ else {
+ mrp_log_info("Input file '%s' parsed successfully.", c.file);
+ mrp_resolver_dump_targets(c.r, stdout);
+ mrp_resolver_dump_facts(c.r, stdout);
+
+ mrp_resolver_declare_variable(c.r, "var1", MRP_SCRIPT_TYPE_STRING);
+ mrp_resolver_declare_variable(c.r, "var2", MRP_SCRIPT_TYPE_STRING);
+ mrp_resolver_declare_variable(c.r, "var3", MRP_SCRIPT_TYPE_BOOL);
+ mrp_resolver_declare_variable(c.r, "var4", MRP_SCRIPT_TYPE_SINT32);
+ mrp_resolver_declare_variable(c.r, "var5", MRP_SCRIPT_TYPE_UINT32);
+ mrp_resolver_declare_variable(c.r, "var6", MRP_SCRIPT_TYPE_UNKNOWN);
+
+ for (i = optind; i < argc; i++) {
+ target = argv[i];
+ printf("========== Target %s ==========\n", target);
+
+ if (mrp_resolver_update_targetl(c.r, argv[i],
+ "var1", MRP_SCRIPT_STRING("foo"),
+ "var2", MRP_SCRIPT_STRING("bar"),
+ "var3", MRP_SCRIPT_BOOL(TRUE),
+ "var4", MRP_SCRIPT_SINT32(-1),
+ "var5", MRP_SCRIPT_UINT32(123),
+ "var6", MRP_SCRIPT_DOUBLE(3.141),
+ NULL) > 0)
+ printf("Resolved OK.\n");
+ else
+ printf("Resolving FAILED.\n");
+
+#if 0
+ {
+ int nvariable = 6;
+ const char *variables[nvariable];
+ mrp_script_value_t values[nvariable];
+
+ variables[0] = "var1";
+ values[0] = MRP_SCRIPT_VALUE_STRING("foo");
+ variables[1] = "var2";
+ values[1] = MRP_SCRIPT_VALUE_STRING("bar");
+ variables[2] = "var3";
+ values[2] = MRP_SCRIPT_VALUE_BOOL(TRUE);
+ variables[3] = "var4";
+ values[3] = MRP_SCRIPT_VALUE_SINT32(-3);
+ variables[4] = "var5";
+ values[4] = MRP_SCRIPT_VALUE_UINT32(369);
+ variables[5] = "var6";
+ values[5] = MRP_SCRIPT_VALUE_SINT32(-3141);
+
+ if (mrp_resolver_update_targetv(c.r, argv[i], variables, values,
+ nvariable) > 0)
+ printf("Resolved OK.\n");
+ else
+ printf("Resolving FAILED.\n");
+ }
+#endif
+ }
+ }
+
+ mrp_resolver_destroy(c.r);
+ c.r = NULL;
+
+ return 0;
+}
--- /dev/null
+target all
+ depends on target1 target2
+
+target target1
+ depends on target3
+
+target target2
+ depends on target4 target5
+
+target target3
+ depends on $fact1
+
+target target4
+ depends on $fact2 $fact3
+
+target target5
+ depends on target6
+
+target target6
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOLVER_TOKEN_H__
+#define __MURPHY_RESOLVER_TOKEN_H__
+
+#include <stdint.h>
+#include <stdlib.h>
+
+/*
+ * common token fields
+ */
+
+#define RESOLVER_TOKEN_FIELDS \
+ const char *token; /* token string */ \
+ const char *source; /* encountered in this source */ \
+ int line; /* and on this line */ \
+ size_t size /* token size */
+
+/*
+ * a generic token
+ */
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+} tkn_any_t;
+
+
+/*
+ * a string token
+ */
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+ char *value;
+} tkn_string_t;
+
+
+/*
+ * signed and unsigned 16-bit integer tokens
+ */
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+ int16_t value;
+} tkn_s16_t;
+
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+ uint16_t value;
+} tkn_u16_t;
+
+
+/*
+ * signed and unsigned 32-bit integer tokens
+ */
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+ int32_t value;
+} tkn_s32_t;
+
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+ uint32_t value;
+} tkn_u32_t;
+
+
+typedef struct {
+ RESOLVER_TOKEN_FIELDS;
+ int nstr;
+ char **strs;
+} tkn_strarr_t;
+
+#ifdef __MURPHY_RESOLVER_CHECK_RINGBUF__
+# define RESOLVER_TOKEN_DONE(t) memset((t).token, 0, (t).size)
+#else
+# define RESOLVER_TOKEN_DONE(t) do {} while (0)
+#endif
+
+#define RESOLVER_TOKEN_SAVE(str, size) save_token((str), (size))
+
+
+#endif /* __MURPHY_RESOLVER_TOKEN_H__ */
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/log.h>
+
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/config-api.h>
+
+#include <murphy-db/mqi.h>
+
+#include "application-class.h"
+#include "resource-set.h"
+#include "resource-owner.h"
+#include "zone.h"
+
+#define CLASS_MAX 64
+#define NAME_LENGTH 24
+
+#define CLASS_NAME_IDX 0
+#define PRIORITY_IDX 1
+
+
+/*
+ * sorting key bit layout
+ *
+ * +---------+----+----+--------+
+ * | 31 - 29 | 28 | 27 | 26 - 0 |
+ * +---------+----+----+--------+
+ * | | | |
+ * | | | +---- 0x07ffffff stamp of the last request
+ * | | +------------ 0x08000000 state (set if acquiring)
+ * | +----------------- 0x10000000 usage (set if shared)
+ * +------------------------ 0xe0000000 priority (0-7)
+ */
+#define MASK(b) (((uint32_t)1 << (b)) - (uint32_t)1)
+
+#define STAMP_SHIFT 0
+#define STATE_SHIFT (STAMP_SHIFT + MRP_KEY_STAMP_BITS)
+#define USAGE_SHIFT (STATE_SHIFT + MRP_KEY_STATE_BITS)
+#define PRIORITY_SHIFT (USAGE_SHIFT + MRP_KEY_USAGE_BITS)
+
+#define STAMP_MASK MASK(MRP_KEY_STAMP_BITS)
+#define STATE_MASK MASK(MRP_KEY_STATE_BITS)
+#define USAGE_MASK MASK(MRP_KEY_USAGE_BITS)
+#define PRIORITY_MASK MASK(MRP_KEY_PRIORITY_BITS)
+
+#define STAMP_KEY(p) (((uint32_t)(p) & STAMP_MASK) << STAMP_SHIFT)
+#define STATE_KEY(p) (((uint32_t)(p) & STATE_MASK) << STATE_SHIFT)
+#define USAGE_KEY(p) (((uint32_t)(p) & USAGE_MASK) << USAGE_SHIFT)
+#define PRIORITY_KEY(p) (((uint32_t)(p) & PRIORITY_MASK) << PRIORITY_SHIFT)
+
+#define STAMP_MAX STAMP_MASK
+
+typedef struct {
+ const char *class_name;
+ uint32_t priority;
+} class_row_t;
+
+
+static MRP_LIST_HOOK(class_list);
+static mrp_htbl_t *name_hash;
+
+static void init_name_hash(void);
+static int add_to_name_hash(mrp_application_class_t *);
+#if 0
+static void remove_from_name_hash(mrp_application_class_t *);
+#endif
+
+static mqi_handle_t get_database_table(void);
+static void insert_into_application_class_table(const char *, uint32_t);
+
+
+mrp_application_class_t *mrp_application_class_create(const char *name,
+ uint32_t pri,
+ bool modal,
+ bool share,
+ mrp_resource_order_t order)
+{
+ mrp_application_class_t *class;
+ mrp_list_hook_t *insert_before, *clhook, *n;
+ uint32_t zone;
+
+ MRP_ASSERT(name, "invalid argument");
+
+ if (modal && share) {
+ mrp_log_error("Class '%s' is both modal and shared. "
+ "Sharing will be disabled", name);
+ share = false;
+ }
+
+ /* looping through all classes to check the uniqueness of the
+ name & priority of the new class and find the insertion point */
+ insert_before = &class_list;
+
+ mrp_list_foreach_back(&class_list, clhook, n) {
+ class = mrp_list_entry(clhook, mrp_application_class_t, list);
+
+ if (!strcasecmp(name, class->name)) {
+ mrp_log_warning("Multiple definitions for class '%s'", name);
+ return NULL;
+ }
+
+ if (pri == class->priority) {
+ mrp_log_error("Priority clash. Classes '%s' and '%s' would have "
+ "the same priority", name, class->name);
+ }
+
+ if (pri < class->priority)
+ insert_before = &class->list;
+ }
+
+ if (!(class = mrp_allocz(sizeof(mrp_application_class_t)))) {
+ mrp_log_error("Memory alloc failure. Can't create resource class '%s'",
+ name);
+ return NULL;
+ }
+
+ class->name = mrp_strdup(name);
+ class->priority = pri;
+ class->modal = modal;
+ class->share = share;
+ class->order = order;
+
+ for (zone = 0; zone < MRP_ZONE_MAX; zone++)
+ mrp_list_init(&class->resource_sets[zone]);
+
+ /* list do not have insert_before function,
+ so don't be mislead by the name */
+ mrp_list_append(insert_before, &class->list);
+
+ add_to_name_hash(class);
+
+ insert_into_application_class_table(class->name, class->priority);
+
+ return class;
+}
+
+
+mrp_application_class_t *mrp_application_class_find(const char *name)
+{
+ mrp_application_class_t *class = NULL;
+
+ if (name_hash && name)
+ class = mrp_htbl_lookup(name_hash, (void *)name);
+
+ return class;
+}
+
+mrp_application_class_t *mrp_application_class_iterate_classes(void **cursor)
+{
+ mrp_list_hook_t *entry;
+
+ MRP_ASSERT(cursor, "invalid argument");
+
+ entry = (*cursor == NULL) ? class_list.prev : (mrp_list_hook_t *)*cursor;
+
+ if (entry == &class_list)
+ return NULL;
+
+ *cursor = entry->prev;
+
+ return mrp_list_entry(entry, mrp_application_class_t, list);
+}
+
+mrp_resource_set_t *
+mrp_application_class_iterate_rsets(mrp_application_class_t *class,
+ uint32_t zone,
+ void **cursor)
+{
+ mrp_list_hook_t *list, *entry;
+
+ MRP_ASSERT(class && zone < MRP_ZONE_MAX && cursor, "invalid argument");
+
+ list = class->resource_sets + zone;
+ entry = (*cursor == NULL) ? list->prev : (mrp_list_hook_t *)*cursor;
+
+ if (entry == list)
+ return NULL;
+
+ *cursor = entry->prev;
+
+ return mrp_list_entry(entry, mrp_resource_set_t, class.list);
+}
+
+const char *mrp_application_class_get_name(mrp_application_class_t *class)
+{
+ if (!class || !class->name)
+ return "<unknown class>";
+
+ return class->name;
+}
+
+
+const char **mrp_application_class_get_all_names(uint32_t buflen,
+ const char **buf)
+{
+ mrp_list_hook_t *entry, *n;
+ mrp_application_class_t *class;
+ bool freeit = false;
+ uint32_t i = 0;
+
+ MRP_ASSERT(!buf || (buf && buflen > 1), "invalid argument");
+
+ if (!buf) {
+ freeit = true;
+ buflen = CLASS_MAX;
+ if (!(buf = mrp_allocz(sizeof(const char *) * buflen))) {
+ mrp_log_error("Memory alloc failure. Can't get class names");
+ return NULL;
+ }
+ }
+
+ mrp_list_foreach(&class_list, entry, n) {
+ class = mrp_list_entry(entry, mrp_application_class_t, list);
+
+ if (i >= buflen-1) {
+ if (freeit)
+ mrp_free(buf);
+ return NULL;
+ }
+
+ buf[i++] = class->name;
+ }
+
+ buf[i] = NULL;
+
+ return buf;
+}
+
+uint32_t mrp_application_class_get_priority(mrp_application_class_t *class)
+{
+ return class ? class->priority : 0;
+}
+
+int mrp_application_class_add_resource_set(const char *class_name,
+ const char *zone_name,
+ mrp_resource_set_t *rset,
+ uint32_t reqid)
+{
+ mrp_application_class_t *class;
+ mrp_zone_t *zone;
+
+
+ MRP_ASSERT(class_name && rset && zone_name, "invalid argument");
+ MRP_ASSERT(!rset->class.ptr || !mrp_list_empty(&rset->class.list),
+ "attempt to add multiple times the same resource set");
+
+ if (!(class = mrp_application_class_find(class_name)))
+ return -1;
+
+ if (!(zone = mrp_zone_find_by_name(zone_name)))
+ return -1;
+
+ rset->class.ptr = class;
+ rset->zone = mrp_zone_get_id(zone);
+
+ if (rset->state == mrp_resource_acquire)
+ mrp_resource_set_acquire(rset, reqid);
+ else {
+ rset->request.id = reqid;
+
+ if (rset->state == mrp_resource_no_request)
+ rset->state = mrp_resource_release;
+
+ mrp_application_class_move_resource_set(rset);
+
+ mrp_resource_set_notify(rset, MRP_RESOURCE_EVENT_CREATED);
+
+ mrp_resource_owner_update_zone(rset->zone, rset, reqid);
+ }
+
+ return 0;
+}
+
+void mrp_application_class_move_resource_set(mrp_resource_set_t *rset)
+{
+ mrp_application_class_t *class;
+ mrp_list_hook_t *list, *lentry, *n, *insert_before;
+ mrp_resource_set_t *rentry;
+ uint32_t key;
+ uint32_t zone;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ mrp_list_delete(&rset->class.list);
+
+ class = rset->class.ptr;
+ zone = rset->zone;
+
+ list = insert_before = class->resource_sets + zone;
+ key = mrp_application_class_get_sorting_key(rset);
+
+ mrp_list_foreach_back(list, lentry, n) {
+ rentry = mrp_list_entry(lentry, mrp_resource_set_t, class.list);
+
+ if (key >= mrp_application_class_get_sorting_key(rentry))
+ break;
+
+ insert_before = lentry;
+ }
+
+ mrp_list_append(insert_before, &rset->class.list);
+}
+
+uint32_t mrp_application_class_get_sorting_key(mrp_resource_set_t *rset)
+{
+ mrp_application_class_t *class;
+ bool lifo;
+ uint32_t rqstamp;
+ uint32_t priority;
+ uint32_t usage;
+ uint32_t state;
+ uint32_t stamp;
+ uint32_t key;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ class = rset->class.ptr;
+ lifo = (class->order == MRP_RESOURCE_ORDER_LIFO);
+
+ rqstamp = rset->request.stamp;
+
+ priority = PRIORITY_KEY(rset->class.priority);
+ usage = USAGE_KEY(rset->resource.share ? 1 : 0);
+ state = STATE_KEY(rset->state == mrp_resource_acquire ? 1 : 0);
+ stamp = STAMP_KEY(lifo ? rqstamp : STAMP_MAX - rqstamp);
+
+ key = priority | usage | state | stamp;
+
+ return key;
+}
+
+
+int mrp_application_class_print(char *buf, int len, bool with_rsets)
+{
+#define PRINT(fmt, args...) \
+ do { if (p<e) { p += snprintf(p, e-p, fmt , ##args); } } while (0)
+
+ mrp_zone_t *zone;
+ mrp_application_class_t *class;
+ mrp_resource_set_t *rset;
+ mrp_list_hook_t *clen, *n;
+ mrp_list_hook_t *list, *rsen, *m;
+ uint32_t zid;
+ char *p, *e;
+ int width, l;
+ int clcnt, rscnt;
+
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(buf, "invalid argument");
+
+ e = (p = buf) + len;
+ clcnt = rscnt = 0;
+ width = 0;
+
+ if (!with_rsets) {
+ mrp_list_foreach(&class_list, clen, n) {
+ class = mrp_list_entry(clen, mrp_application_class_t, list);
+ if ((l = strlen(class->name)) > width)
+ width = l;
+ }
+ }
+
+ PRINT("Application classes:\n");
+
+ mrp_list_foreach_back(&class_list, clen, n) {
+ class = mrp_list_entry(clen, mrp_application_class_t, list);
+ clcnt++;
+
+ if (with_rsets)
+ PRINT(" %3u - %s ", class->priority, class->name);
+ else
+ PRINT(" %-*s ", width, class->name);
+
+ if (class->modal)
+ PRINT(" modal");
+ if (class->share)
+ PRINT(" share");
+
+ PRINT("\n");
+
+ if (!with_rsets)
+ continue;
+
+ for (zid = 0; zid < MRP_ZONE_MAX; zid++) {
+ zone = mrp_zone_find_by_id(zid);
+ list = class->resource_sets + zid;
+
+ if (!mrp_list_empty(list)) {
+ if (!zone) {
+ PRINT(" Resource-sets in zone %u:\n", zid);
+ }
+ else {
+ PRINT(" Resource-sets in %s zone:", zone->name);
+ p += mrp_zone_attribute_print(zone, p, e-p);
+ PRINT("\n");
+ }
+
+ mrp_list_foreach_back(list, rsen, m) {
+ rset = mrp_list_entry(rsen, mrp_resource_set_t,class.list);
+ p += mrp_resource_set_print(rset, 13, p, e-p);
+ }
+ }
+ }
+ }
+
+ if (!clcnt)
+ PRINT(" <none>\n");
+
+ return p - buf;
+
+#undef PRINT
+}
+
+
+static void init_name_hash(void)
+{
+ mrp_htbl_config_t cfg;
+
+ if (!name_hash) {
+ cfg.nentry = CLASS_MAX;
+ cfg.comp = mrp_string_comp;
+ cfg.hash = mrp_string_hash;
+ cfg.free = NULL;
+ cfg.nbucket = cfg.nentry / 2;
+
+ name_hash = mrp_htbl_create(&cfg);
+
+ MRP_ASSERT(name_hash, "failed to make name_hash for resource classes");
+ }
+}
+
+
+static int add_to_name_hash(mrp_application_class_t *class)
+{
+ MRP_ASSERT(class && class->name, "invalid argument");
+
+ init_name_hash();
+
+ if (!mrp_htbl_insert(name_hash, (void *)class->name, class))
+ return -1;
+
+ return 0;
+}
+
+#if 0
+static void remove_from_name_hash(mrp_application_class_t *class)
+{
+ mrp_application_class_t *deleted;
+
+ if (class && class->name && name_hash) {
+ deleted = mrp_htbl_remove(name_hash, (void *)class->name, false);
+
+ MRP_ASSERT(!deleted || deleted == class, "confused with data "
+ "structures when deleting resource-class from name hash");
+
+ /* in case we were not compiled with debug enabled */
+ if (deleted != class) {
+ mrp_log_error("confused with data structures when deleting "
+ "resource-class '%s' from name hash", class->name);
+ }
+ }
+}
+#endif
+
+
+static mqi_handle_t get_database_table(void)
+{
+ MQI_COLUMN_DEFINITION_LIST(coldefs,
+ MQI_COLUMN_DEFINITION( "name" , MQI_VARCHAR(NAME_LENGTH) ),
+ MQI_COLUMN_DEFINITION( "priority" , MQI_UNSIGNED )
+ );
+
+ MQI_INDEX_DEFINITION(indexdef,
+ MQI_INDEX_COLUMN("priority")
+ );
+
+ static mqi_handle_t table = MQI_HANDLE_INVALID;
+ static char *name = "application_classes";
+
+ if (table == MQI_HANDLE_INVALID) {
+ mqi_open();
+
+ table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
+
+ if (table == MQI_HANDLE_INVALID)
+ mrp_log_error("Can't create table '%s': %s", name,strerror(errno));
+ }
+
+ return table;
+}
+
+static void insert_into_application_class_table(const char *name, uint32_t pri)
+{
+ MQI_COLUMN_SELECTION_LIST(cols,
+ MQI_COLUMN_SELECTOR(CLASS_NAME_IDX, class_row_t, class_name),
+ MQI_COLUMN_SELECTOR(PRIORITY_IDX , class_row_t, priority )
+ );
+
+ class_row_t row;
+ mqi_handle_t table = get_database_table();
+ class_row_t *rows[2] = {&row, NULL};
+
+ MRP_ASSERT(name, "invalid argument");
+ MRP_ASSERT(table != MQI_HANDLE_INVALID, "database problem");
+
+ row.class_name = name;
+ row.priority = pri;
+
+ if (MQI_INSERT_INTO(table, cols, rows) != 1)
+ mrp_log_error("Failed to add application class '%s' to database",name);
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_APPLICATION_CLASS_H__
+#define __MURPHY_APPLICATION_CLASS_H__
+
+#include <murphy/common/list.h>
+
+#include "data-types.h"
+
+
+
+struct mrp_application_class_s {
+ mrp_list_hook_t list;
+ const char *name;
+ uint32_t priority;
+ bool share;
+ bool modal;
+ mrp_resource_order_t order;
+ mrp_list_hook_t resource_sets[MRP_ZONE_MAX];
+};
+
+mrp_application_class_t *mrp_application_class_find(const char *);
+mrp_application_class_t *mrp_application_class_iterate_classes(void **);
+mrp_resource_set_t *
+mrp_application_class_iterate_rsets(mrp_application_class_t*,uint32_t,void**);
+
+void mrp_application_class_move_resource_set(mrp_resource_set_t *);
+
+uint32_t mrp_application_class_get_sorting_key(mrp_resource_set_t *);
+
+
+#endif /* __MURPHY_APPLICATION_CLASS_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include "attribute.h"
+#include "client-api.h"
+
+static mrp_attr_value_t *get_attr_value_from_list(mrp_attr_t *, const char *,
+ mqi_data_type_t);
+
+
+int mrp_attribute_copy_definitions(mrp_attr_def_t *from, mrp_attr_def_t *to)
+{
+ mrp_attr_def_t *s, *d;
+
+ MRP_ASSERT(to,"invalid argument");
+
+ if (from) {
+ for (s = from, d = to; s->name; s++, d++) {
+
+ if (!(d->name = mrp_strdup(s->name)))
+ goto no_memory;
+
+ d->access = s->access;
+
+ if ((d->type = s->type) != mqi_string)
+ d->value = s->value;
+ else {
+ if (!(d->value.string = mrp_strdup(s->value.string))) {
+ mrp_free((void *)d->name);
+ memset(d, 0, sizeof(*d));
+ goto no_memory;
+ }
+ }
+ }
+ }
+
+ return 0;
+
+ no_memory:
+ mrp_log_error("Memory alloc failure. Can't copy attribute definition");
+ return -1;
+}
+
+
+mrp_attr_t *mrp_attribute_get_value(uint32_t idx,
+ mrp_attr_t *value,
+ uint32_t nattr,
+ mrp_attr_def_t *defs,
+ mrp_attr_value_t *attrs)
+{
+ mrp_attr_t *vdst;
+ mrp_attr_def_t *adef;
+
+ MRP_ASSERT(!nattr || (nattr > 0 && defs && attrs), "invalid argument");
+ MRP_ASSERT(idx < nattr, "invalid argument");
+
+ if ((vdst = value) || (vdst = mrp_alloc(sizeof(mrp_attr_t)))) {
+ adef = defs + idx;
+
+ if (!(adef->access & MRP_RESOURCE_READ))
+ memset(vdst, 0, sizeof(mrp_attr_t));
+ else {
+ vdst->name = adef->name;
+ vdst->type = adef->type;
+ vdst->value = attrs[idx];
+ }
+ }
+
+ return vdst;
+}
+
+
+mrp_attr_t *mrp_attribute_get_all_values(uint32_t nvalue,
+ mrp_attr_t *values,
+ uint32_t nattr,
+ mrp_attr_def_t *defs,
+ mrp_attr_value_t *attrs)
+{
+ mrp_attr_def_t *adef;
+ mrp_attr_t *vdst, *vend;
+ uint32_t i;
+
+ MRP_ASSERT((!nvalue || (nvalue > 0 && values)) &&
+ (!nattr || (nattr > 0 && defs)),
+ "invalid argument");
+
+ if (nvalue)
+ nvalue--;
+ else {
+ for (i = 0; i < nattr; i++) {
+ if (!attrs || (attrs && (defs[i].access & MRP_RESOURCE_READ)))
+ nvalue++;
+ }
+
+ if (!(values = mrp_allocz(sizeof(mrp_attr_t) * (nvalue + 1)))) {
+ mrp_log_error("Memory alloc failure. Can't get attributes");
+ return NULL;
+ }
+ }
+
+ vend = (vdst = values) + nvalue;
+
+ for (i = 0; i < nattr && vdst < vend; i++) {
+ adef = defs + i;
+
+ if (!(adef->access && MRP_RESOURCE_READ))
+ continue;
+
+ vdst->name = adef->name;
+ vdst->type = adef->type;
+ vdst->value = attrs ? attrs[i] : adef->value;
+
+ vdst++;
+ }
+
+ memset(vdst, 0, sizeof(*vdst));
+
+ return values;
+}
+
+int mrp_attribute_set_values(mrp_attr_t *values,
+ uint32_t nattr,
+ mrp_attr_def_t *defs,
+ mrp_attr_value_t *attrs)
+{
+ mrp_attr_def_t *adef;
+ mrp_attr_value_t *vsrc;
+ mrp_attr_value_t *vdst;
+ uint32_t i;
+
+
+ MRP_ASSERT(!nattr || (nattr > 0 && defs && attrs),
+ "invalid arguments");
+
+ for (i = 0; i < nattr; i++) {
+ adef = defs + i;
+ vdst = attrs + i;
+
+ if (!(adef->access & MRP_RESOURCE_WRITE) ||
+ !(vsrc = get_attr_value_from_list(values, adef->name, adef->type)))
+ vsrc = &adef->value; /* default value */
+
+ if (adef->type != mqi_string)
+ *vdst = *vsrc;
+ else if (vdst->string != vsrc->string) {
+ /* if the string is not the same, change it */
+ mrp_free((void *)vdst->string);
+ if (!(vdst->string = mrp_strdup(vsrc->string)))
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int mrp_attribute_print(uint32_t nattr,
+ mrp_attr_def_t *adefs,
+ mrp_attr_value_t *avals,
+ char *buf,
+ int len)
+{
+#define PRINT(fmt, args...) if (p<e) { p += snprintf(p, e-p, fmt , ##args); }
+
+ mrp_attr_def_t *adef;
+ mrp_attr_value_t *aval;
+ uint32_t i;
+ char *p, *e;
+
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(adefs && avals && buf, "invalid argument");
+
+ e = (p = buf) + len;
+
+ for (i = 0; i < nattr; i++) {
+ adef = adefs + i;
+ aval = avals + i;
+
+ PRINT(" %s:", adef->name);
+
+ switch (adef->type) {
+ case mqi_string: PRINT("'%s'", aval->string ); break;
+ case mqi_integer: PRINT("%d" , aval->integer ); break;
+ case mqi_unsignd: PRINT("%u" , aval->unsignd ); break;
+ case mqi_floating: PRINT("%lf" , aval->floating); break;
+ default: PRINT(" <unsupported type>" ); break;
+ }
+
+ }
+
+ return p - buf;
+
+#undef PRINT
+}
+
+
+static mrp_attr_value_t *get_attr_value_from_list(mrp_attr_t *list,
+ const char *name,
+ mqi_data_type_t type)
+{
+ mrp_attr_t *attr;
+
+ MRP_ASSERT(name, "invalid argument");
+
+ if (list) {
+ for (attr = list; attr->name; attr++) {
+ if (!strcasecmp(name, attr->name) && type == attr->type)
+ return &attr->value;
+ }
+ }
+
+ return NULL;
+}
+
+void mrp_resource_set_free_attribute(mrp_attr_t *attr)
+{
+ if (!attr)
+ return;
+
+ mrp_free(attr);
+}
+
+mrp_attr_t *mrp_resource_set_get_attribute_by_name(
+ mrp_resource_set_t *resource_set, const char *resource_name,
+ const char *attribute_name)
+{
+ mrp_attr_t *attr = NULL, *attrs;
+ uint32_t res_id;
+ mrp_attr_t attr_buf[128];
+ uint32_t attr_idx = 0;
+
+ memset(attr_buf, 0, sizeof(attr_buf));
+
+ res_id = mrp_resource_definition_get_resource_id_by_name(resource_name);
+ attrs = mrp_resource_definition_read_all_attributes(res_id, 128, attr_buf);
+
+ if (!attrs)
+ return NULL;
+
+ while (attrs->name != NULL) {
+ if (strcmp(attrs->name, attribute_name) == 0) {
+
+ mrp_attr_t *buf = mrp_allocz(sizeof(mrp_attr_t));
+ mrp_resource_set_read_attribute(resource_set, resource_name,
+ attr_idx, buf);
+
+ attr = buf;
+
+ break;
+ }
+ attr_idx++;
+ attrs++;
+ }
+
+ return attr;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_ATTRIBUTE_H__
+#define __MURPHY_ATTRIBUTE_H__
+
+#include "data-types.h"
+
+
+int mrp_attribute_copy_definitions(mrp_attr_def_t *, mrp_attr_def_t *);
+mrp_attr_t *mrp_attribute_get_value(uint32_t, mrp_attr_t *, uint32_t,
+ mrp_attr_def_t *, mrp_attr_value_t *);
+mrp_attr_t *mrp_attribute_get_all_values(uint32_t, mrp_attr_t *, uint32_t,
+ mrp_attr_def_t *, mrp_attr_value_t *);
+int mrp_attribute_set_values(mrp_attr_t *, uint32_t, mrp_attr_def_t *,
+ mrp_attr_value_t *);
+
+int mrp_attribute_print(uint32_t, mrp_attr_def_t *, mrp_attr_value_t *,
+ char *, int);
+
+
+#endif /* __MURPHY_ATTRIBUTE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_CLIENT_API_H__
+#define __MURPHY_RESOURCE_CLIENT_API_H__
+
+#include <murphy/resource/common-api.h>
+
+mrp_resource_client_t *mrp_resource_client_create(const char *name,
+ void *user_data);
+void mrp_resource_client_destroy(mrp_resource_client_t *client);
+
+mrp_resource_set_t *mrp_resource_client_find_set(mrp_resource_client_t *client,
+ uint32_t resource_set_id);
+
+
+const char **mrp_zone_get_all_names(uint32_t buflen, const char **buf);
+
+const char **mrp_resource_definition_get_all_names(uint32_t buflen,
+ const char **buf);
+
+uint32_t mrp_resource_definition_get_resource_id_by_name(const char *name);
+
+mrp_attr_t *
+mrp_resource_definition_read_all_attributes(uint32_t resource_id,
+ uint32_t buflen,
+ mrp_attr_t *buf);
+
+const char **mrp_application_class_get_all_names(uint32_t buflen,
+ const char **buf);
+
+int mrp_application_class_add_resource_set(const char *class_name,
+ const char *zone_name,
+ mrp_resource_set_t *resource_set,
+ uint32_t request_id);
+
+mrp_resource_set_t *mrp_resource_set_create(mrp_resource_client_t *client,
+ bool auto_release,
+ bool dont_wait,
+ uint32_t priority,
+ mrp_resource_event_cb_t event_cb,
+ void *user_data);
+void mrp_resource_set_destroy(mrp_resource_set_t *resource_set);
+
+uint32_t mrp_get_resource_set_id(mrp_resource_set_t *resource_set);
+
+mrp_resource_state_t
+mrp_get_resource_set_state(mrp_resource_set_t *resource_set);
+
+mrp_resource_mask_t
+mrp_get_resource_set_grant(mrp_resource_set_t *resource_set);
+
+mrp_resource_mask_t
+mrp_get_resource_set_advice(mrp_resource_set_t *resource_set);
+
+mrp_resource_client_t *
+mrp_get_resource_set_client(mrp_resource_set_t *resource_set);
+
+
+int mrp_resource_set_add_resource(mrp_resource_set_t *resource_set,
+ const char *resource_name,
+ bool shared,
+ mrp_attr_t *attribute_list,
+ bool mandatory);
+
+mrp_attr_t *
+mrp_resource_set_read_attribute(mrp_resource_set_t *resource_set,
+ const char *resource_name,
+ uint32_t attribute_index,
+ mrp_attr_t *buf);
+
+mrp_attr_t *
+mrp_resource_set_read_all_attributes(mrp_resource_set_t *resource_set,
+ const char *resource_name,
+ uint32_t buflen,
+ mrp_attr_t *buf);
+
+int mrp_resource_set_write_attributes(mrp_resource_set_t *resource_set,
+ const char *resource_name,
+ mrp_attr_t *attribute_list);
+
+void mrp_resource_set_acquire(mrp_resource_set_t *resource_set,
+ uint32_t request_id);
+
+void mrp_resource_set_release(mrp_resource_set_t *resource_set,
+ uint32_t request_id);
+
+mrp_resource_t *
+mrp_resource_set_iterate_resources(mrp_resource_set_t *resource_set,void **it);
+
+uint32_t mrp_resource_get_id(mrp_resource_t *resource);
+const char *mrp_resource_get_name(mrp_resource_t *resource);
+mrp_resource_mask_t mrp_resource_get_mask(mrp_resource_t *resource);
+bool mrp_resource_is_shared(mrp_resource_t *resource);
+
+/* Find a resource set given the resource set id. */
+mrp_resource_set_t *mrp_resource_set_find_by_id(uint32_t id);
+
+/* Get a single attribute object that contains the current value. */
+mrp_attr_t *mrp_resource_set_get_attribute_by_name(
+ mrp_resource_set_t *resource_set, const char *resource_name,
+ const char *attribute_name);
+
+/* Free the object obtained with mrp_resource_set_get_attribute_by_name. */
+void mrp_resource_set_free_attribute(mrp_attr_t *attr);
+
+#endif /* __MURPHY_RESOURCE_CLIENT_API_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_COMMON_API_H__
+#define __MURPHY_RESOURCE_COMMON_API_H__
+
+#include <murphy/resource/data-types.h>
+
+
+mrp_attr_t *mrp_resource_read_attribute(mrp_resource_t *resource,
+ uint32_t attribute_index,
+ mrp_attr_t *buf);
+mrp_attr_t *mrp_resource_read_all_attributes(mrp_resource_t *resource,
+ uint32_t buflen,
+ mrp_attr_t *buf);
+int mrp_resource_write_attributes(mrp_resource_t *resource, mrp_attr_t *attrs);
+
+
+#endif /* __MURPHY_RESOURCE_COMMON_API_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_CONFIG_API_H__
+#define __MURPHY_RESOURCE_CONFIG_API_H__
+
+#include <murphy/resource/data-types.h>
+
+void mrp_resource_configuration_init(void);
+
+int mrp_zone_definition_create(mrp_attr_def_t *attrdefs);
+uint32_t mrp_zone_create(const char *name, mrp_attr_t *attrs);
+
+mrp_application_class_t *mrp_application_class_create(const char *name,
+ uint32_t priority,
+ bool modal,
+ bool share,
+ mrp_resource_order_t order);
+
+int mrp_application_class_print(char *buf, int len, bool with_resource_sets);
+
+int mrp_resource_owner_print(char *buf, int len);
+
+
+#endif /* __MURPHY_RESOURCE_CONFIG_API_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+#include <murphy/common/debug.h>
+#include <murphy/core/lua-bindings/murphy.h>
+#include <murphy/core/lua-utils/object.h>
+
+#include "config-lua.h"
+#include "resource-lua.h"
+#include "config-api.h"
+#include "client-api.h"
+#include "manager-api.h"
+#include "zone.h"
+#include "application-class.h"
+#include "resource.h"
+#include "resource-set.h"
+#include "resource-owner.h"
+#include "attribute.h"
+
+#define ZONE_CLASS MRP_LUA_CLASS_SIMPLE(zone)
+#define APPCLASS_CLASS MRP_LUA_CLASS_SIMPLE(application_class)
+#define ZONE_ATTR_CLASS MRP_LUA_CLASS(zone, attributes)
+#define RESCLASS_CLASS MRP_LUA_CLASS(resource, class)
+#define RESMETHOD_CLASS MRP_LUA_CLASS_SIMPLE(resource)
+
+#define ATTRIBUTE_CLASSID MRP_LUA_CLASSID_ROOT "attribute"
+#define RESOURCE_CLASSID MRP_LUA_CLASSID_ROOT "resource.instance"
+
+typedef enum field_e field_t;
+typedef enum attr_owner_e attr_owner_t;
+typedef struct appclass_s appclass_t;
+typedef struct zone_s zone_t;
+typedef struct resclass_s resclass_t;
+typedef struct resource_s resource_t;
+typedef struct attr_def_s attr_def_t;
+typedef struct attr_s attr_t;
+
+typedef bool (*attribute_access_t)(attr_t *, int, mrp_attr_t *);
+
+enum field_e {
+ ATTRIBUTES = 1,
+ CLASS,
+ NAME,
+ PRIORITY,
+ SHAREABLE,
+ MANDATORY,
+ MODAL,
+ SHARE,
+ GRANT,
+ ORDER,
+ SHARED,
+ METHOD,
+ OWNERS,
+ RECALC,
+ VETO,
+ ID
+};
+
+enum attr_owner_e {
+ ZONE = 1,
+ RESOURCE
+};
+
+struct appclass_s {
+ const char *name;
+};
+
+struct zone_s {
+ uint32_t id;
+ const char *name;
+ int attr_tbl;
+};
+
+struct resclass_s {
+ uint32_t id;
+ const char *name;
+ mrp_attr_def_t *attrs;
+};
+
+struct resource_s {
+ uint32_t rsetid;
+ uint32_t resid;
+ const char *name;
+ int attr_tbl;
+};
+
+struct attr_def_s {
+ int nattr;
+ mrp_attr_def_t *attrs;
+};
+
+struct attr_s {
+ struct {
+ attr_owner_t type;
+ void *data;
+ } owner;
+ attr_def_t *def;
+ attribute_access_t fetch;
+ attribute_access_t update;
+};
+
+
+static void attributes_class_create(lua_State *);
+static void appclass_class_create(lua_State *);
+static void zone_class_create(lua_State *);
+static void resclass_class_create(lua_State *);
+static void resource_class_create(lua_State *);
+static void resource_methods_create(lua_State *);
+
+
+static int attributes_create(lua_State *, attr_owner_t, void *,
+ attr_def_t *, attribute_access_t,
+ attribute_access_t);
+static int attributes_getvalue(lua_State *);
+static int attributes_setvalue(lua_State *);
+static int attributes_getlength(lua_State *);
+static attr_t *check_attributes(lua_State *, int);
+static int push_attributes(lua_State *, int);
+
+static int appclass_create(lua_State *);
+static int appclass_getfield(lua_State *);
+static int appclass_setfield(lua_State *);
+static void appclass_destroy(void *);
+/* static appclass_t *check_appclass(lua_State *, int); */
+static appclass_t *to_appclass(lua_State *, int);
+
+static int zone_create(lua_State *);
+static int zone_getfield(lua_State *);
+static int zone_setfield(lua_State *);
+static void zone_destroy(void *);
+/* static zone_t *check_zone(lua_State *, int); */
+static zone_t *to_zone(lua_State *, int);
+
+static int zone_attr_create(lua_State *);
+static int zone_attr_getfield(lua_State *);
+static int zone_attr_setfield(lua_State *);
+static void zone_attr_destroy(void *);
+/* static attr_def_t *check_zone_attr(lua_State *, int); */
+/* static attr_def_t *to_zone_attr(lua_State *, int); */
+static bool fetch_zone_attribute(attr_t *, int, mrp_attr_t *);
+static bool update_zone_attribute(attr_t *, int, mrp_attr_t *);
+
+static int resclass_create_from_lua(lua_State *);
+static int resclass_getfield(lua_State *);
+static int resclass_setfield(lua_State *);
+static void resclass_destroy(void *);
+/* static resclass_t *check_resclass(lua_State *, int); */
+static resclass_t *to_resclass(lua_State *, int);
+
+static int resource_getfield(lua_State *);
+static int resource_setfield(lua_State *);
+static resource_t *check_resource(lua_State *, int);
+static bool fetch_resource_attribute(attr_t *, int, mrp_attr_t *);
+static bool update_resource_attribute(attr_t *, int, mrp_attr_t *);
+
+static mrp_lua_resmethod_t *resmethod_create_from_c(lua_State *);
+static int resmethod_create_from_lua(lua_State *);
+static int resmethod_getfield(lua_State *);
+static int resmethod_setfield(lua_State *);
+static void resmethod_destroy(void *);
+static mrp_lua_resmethod_t *to_resmethod(lua_State *, int);
+
+static mrp_attr_def_t *check_attrdefs(lua_State *, int, int *);
+static void free_attrdefs(mrp_attr_def_t *);
+static mrp_attr_t *check_attrs(lua_State *, int, attr_def_t *);
+static void free_attrs(mrp_attr_t *);
+static int check_attrindex(lua_State *, int, attr_def_t *);
+static int check_boolean(lua_State *, int);
+static mrp_resource_order_t check_order(lua_State *, int);
+static int push_order(lua_State *, mrp_resource_order_t);
+static field_t field_check(lua_State *, int, const char **);
+static field_t field_name_to_type(const char *, size_t);
+
+static int method_recalc(lua_State *);
+
+
+MRP_LUA_METHOD_LIST_TABLE (
+ zone_attr_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (zone_attr_create)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ resclass_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (resclass_create_from_lua)
+);
+
+
+MRP_LUA_METHOD_LIST_TABLE (
+ attributes_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (attributes_getvalue)
+ MRP_LUA_OVERRIDE_SETFIELD (attributes_setvalue)
+ MRP_LUA_OVERRIDE_GETLENGTH (attributes_getlength)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ zone_attr_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (zone_attr_create)
+ MRP_LUA_OVERRIDE_GETFIELD (zone_attr_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (zone_attr_setfield)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ resclass_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (resclass_create_from_lua)
+ MRP_LUA_OVERRIDE_GETFIELD (resclass_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (resclass_setfield)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ resource_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (resource_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (resource_setfield)
+);
+
+MRP_LUA_CLASS_DEF_SIMPLE (
+ application_class, /* main class & constructor name */
+ appclass_t, /* userdata type */
+ appclass_destroy, /* userdata destructor */
+ MRP_LUA_METHOD_LIST ( /* main class methods */
+ MRP_LUA_METHOD_CONSTRUCTOR (appclass_create)
+ ),
+ MRP_LUA_METHOD_LIST ( /* main class overrides */
+ MRP_LUA_OVERRIDE_CALL (appclass_create)
+ MRP_LUA_OVERRIDE_GETFIELD (appclass_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (appclass_setfield)
+ )
+);
+
+MRP_LUA_CLASS_DEF_SIMPLE (
+ zone, /* main class & constructor name */
+ zone_t, /* userdata type */
+ zone_destroy, /* userdata destructor */
+ MRP_LUA_METHOD_LIST ( /* main class methods */
+ MRP_LUA_METHOD_CONSTRUCTOR (zone_create)
+ ),
+ MRP_LUA_METHOD_LIST ( /* main class overrides */
+ MRP_LUA_OVERRIDE_CALL (zone_create)
+ MRP_LUA_OVERRIDE_GETFIELD (zone_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (zone_setfield)
+ )
+);
+
+MRP_LUA_CLASS_DEF (
+ zone, /* main class name */
+ attributes, /* constructor name */
+ attr_def_t, /* userdata type */
+ zone_attr_destroy, /* userdata destructor */
+ zone_attr_methods, /* class methods */
+ zone_attr_overrides /* class overrides */
+);
+
+MRP_LUA_CLASS_DEF (
+ resource, /* main class name */
+ class, /* constructor name */
+ resclass_t, /* userdata type */
+ resclass_destroy, /* userdata destructor */
+ resclass_methods, /* class methods */
+ resclass_overrides /* class overrides */
+);
+
+
+MRP_LUA_CLASS_DEF_SIMPLE (
+ resource, /* main class name */
+ mrp_lua_resmethod_t, /* userdata type */
+ resmethod_destroy, /* userdata destructor */
+ MRP_LUA_METHOD_LIST (
+ MRP_LUA_METHOD_CONSTRUCTOR (resmethod_create_from_lua)
+ ),
+ MRP_LUA_METHOD_LIST (
+ MRP_LUA_OVERRIDE_CALL (resmethod_create_from_lua)
+ MRP_LUA_OVERRIDE_GETFIELD (resmethod_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (resmethod_setfield)
+ )
+);
+
+attr_def_t *zone_attr_defs;
+attr_def_t *resource_attr_defs[MRP_RESOURCE_MAX];
+mrp_lua_resmethod_t *resource_methods;
+
+
+void mrp_resource_configuration_init(void)
+{
+ static bool initialised = false;
+
+ lua_State *L;
+
+ if (!initialised && (L = mrp_lua_get_lua_state())) {
+
+ appclass_class_create(L);
+ zone_class_create(L);
+ resclass_class_create(L);
+ resource_class_create(L);
+
+ mrp_resource_lua_init(L);
+
+ resource_methods_create(L);
+
+ mrp_debug("lua classes are ready for resource "
+ "configuration and management");
+
+ initialised = true;
+ }
+}
+
+
+mrp_lua_resmethod_t *mrp_lua_get_resource_methods(void)
+{
+ return resource_methods;
+}
+
+uint32_t mrp_lua_to_resource_id(lua_State *L, int t)
+{
+ resclass_t *rc = to_resclass(L, t);
+
+ return rc ? rc->id : MRP_RESOURCE_ID_INVALID;
+}
+
+void mrp_lua_resclass_create_from_c(uint32_t id)
+{
+ lua_State *L;
+ mrp_resource_def_t *rdef;
+ resclass_t *resclass;
+ attr_def_t *adef;
+ mrp_attr_def_t *attrs;
+ int nattr;
+
+ if ((L = mrp_lua_get_lua_state())) {
+
+ if (!(rdef = mrp_resource_definition_find_by_id(id)))
+ luaL_error(L, "invalid resource definition ID %d", id);
+
+ resclass = (resclass_t *)mrp_lua_create_object(L, RESCLASS_CLASS,
+ rdef->name,0);
+ adef = mrp_allocz(sizeof(attr_def_t));
+ nattr = rdef->nattr;
+ attrs = mrp_allocz(sizeof(mrp_attr_def_t) * (nattr + 1));
+
+ if (!nattr)
+ mrp_attribute_copy_definitions(rdef->attrdefs, attrs);
+
+ if (!resclass)
+ luaL_error(L, "invalid or duplicate name '%s'", rdef->name);
+ if (!adef)
+ luaL_error(L,"can't to allocate memory for attribute definitions");
+
+ resclass->id = id;
+ resclass->name = mrp_strdup(rdef->name);
+ resclass->attrs = attrs;
+
+ adef->nattr = nattr;
+ adef->attrs = attrs;
+
+ resource_attr_defs[id] = adef;
+
+ lua_pop(L, 1);
+
+ mrp_log_info("resource class '%s' created", rdef->name);
+ }
+}
+
+int mrp_lua_resource_create(lua_State *L, mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef;
+ attr_def_t *adef;
+ resource_t *r;
+
+ MRP_LUA_ENTER;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ if (!(rdef = res->def))
+ lua_pushnil(L);
+ else {
+ adef = resource_attr_defs[rdef->id];
+
+ MRP_ASSERT(resource_attr_defs[rdef->id], "can't find attribute defs");
+
+ r = lua_newuserdata(L, sizeof(resource_t));
+
+ r->rsetid = res->rsetid;
+ r->resid = rdef->id;
+ r->name = rdef->name;
+ r->attr_tbl = attributes_create(L, RESOURCE,r, adef,
+ fetch_resource_attribute,
+ update_resource_attribute);
+
+ luaL_getmetatable(L, RESOURCE_CLASSID);
+ lua_setmetatable(L, -2);
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+
+static void attributes_class_create(lua_State *L)
+{
+ /* create a metatable for attributes */
+ luaL_newmetatable(L, ATTRIBUTE_CLASSID);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, attributes_overrides, 0);
+}
+
+static void appclass_class_create(lua_State *L)
+{
+ mrp_lua_create_object_class(L, APPCLASS_CLASS);
+}
+
+static void zone_class_create(lua_State *L)
+{
+ mrp_lua_create_object_class(L, ZONE_CLASS);
+ mrp_lua_create_object_class(L, ZONE_ATTR_CLASS);
+
+ attributes_class_create(L);
+
+ zone_attr_defs = mrp_lua_create_object(L, ZONE_ATTR_CLASS, NULL,0);
+ mrp_lua_set_object_name(L, ZONE_ATTR_CLASS, "attributes");
+ lua_pop(L, 1);
+}
+
+static void resclass_class_create(lua_State *L)
+{
+ mrp_lua_create_object_class(L, RESCLASS_CLASS);
+}
+
+static int resource_destructor(lua_State *L)
+{
+ resource_t *r;
+
+ if ((r = lua_touserdata(L, -1)) != NULL) {
+ mrp_debug("destroying Lua resource %p", r);
+ luaL_unref(L, LUA_REGISTRYINDEX, r->attr_tbl);
+ r->attr_tbl = LUA_NOREF;
+ }
+
+ return 0;
+}
+
+static void resource_class_create(lua_State *L)
+{
+ /* create a metatable for resource instances */
+ luaL_newmetatable(L, RESOURCE_CLASSID);
+ lua_pushcfunction(L, resource_destructor);
+ lua_setfield(L, -2, "__gc");
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, resource_overrides, 0);
+}
+
+static void resource_methods_create(lua_State *L)
+{
+ typedef struct {
+ const char *name;
+ lua_CFunction func;
+ } method_def_t;
+
+ static method_def_t method_defs[] = {
+ { "recalc", method_recalc },
+ { NULL , NULL }
+ };
+
+ method_def_t *md;
+
+ mrp_lua_create_object_class(L, RESMETHOD_CLASS);
+ resource_methods = resmethod_create_from_c(L);
+
+ for (md = method_defs; md->name; md++) {
+ lua_pushstring(L, md->name);
+ lua_pushcfunction(L, md->func);
+ lua_rawset(L, -3);
+ }
+}
+
+
+static int attributes_create(lua_State *L,
+ attr_owner_t type, void *data,
+ attr_def_t *def,
+ attribute_access_t fetch,
+ attribute_access_t update)
+{
+ attr_t *attr;
+ int tblref;
+
+ MRP_LUA_ENTER;
+
+ attr = lua_newuserdata(L, sizeof(attr_t));
+
+ attr->owner.type = type;
+ attr->owner.data = data;
+ attr->def = def;
+ attr->fetch = fetch;
+ attr->update = update;
+
+ luaL_getmetatable(L, ATTRIBUTE_CLASSID);
+ lua_setmetatable(L, -2);
+
+ tblref = luaL_ref(L, LUA_REGISTRYINDEX);
+
+ MRP_LUA_LEAVE(tblref);
+}
+
+static int attributes_getvalue(lua_State *L)
+{
+ attr_t *attr = check_attributes(L, 1);
+ int idx = check_attrindex(L, 2, attr->def);
+ mrp_attr_def_t *def = attr->def->attrs + idx;
+ mrp_attr_t av;
+
+ MRP_LUA_ENTER;
+
+ if (idx < 0) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ if (!(def->access & MRP_RESOURCE_READ)) {
+ luaL_error(L, "attempt to read a non-readable attribute %s",
+ def->name);
+ MRP_LUA_LEAVE(0);
+ }
+
+ if (!attr->fetch(attr, idx, &av)) {
+ lua_pushnil(L);
+ MRP_LUA_LEAVE(1);
+ }
+
+ switch (def->type) {
+ case mqi_string:
+ if (av.value.string)
+ lua_pushstring(L, av.value.string);
+ else
+ lua_pushnil(L);
+ break;
+ case mqi_integer:
+ case mqi_unsignd:
+ lua_pushinteger(L, av.value.integer);
+ break;
+ case mqi_floating:
+ lua_pushnumber(L, av.value.floating);
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int attributes_setvalue(lua_State *L)
+{
+ attr_t *attr = check_attributes(L, 1);
+ int idx = check_attrindex(L, 2, attr->def);
+ mrp_attr_def_t *def = attr->def->attrs + idx;
+ mrp_attr_t av;
+
+ MRP_LUA_ENTER;
+
+ if (idx < 0)
+ luaL_error(L, "attribute %s dows not exist", def->name);
+
+ if (!(def->access & MRP_RESOURCE_WRITE))
+ luaL_error(L, "attempt to read a readonly attribute %s", def->name);
+
+ switch (def->type) {
+ case mqi_string:
+ av.value.string = luaL_checkstring(L, 3);
+ break;
+ case mqi_integer:
+ av.value.integer = luaL_checkinteger(L, 3);
+ break;
+ case mqi_unsignd:
+ if ((av.value.integer = luaL_checkinteger(L, 3)) < 0) {
+ luaL_error(L, "attempt to update an unsigned attribute "
+ "with negative value");
+ }
+ break;
+ case mqi_floating:
+ av.value.floating = luaL_checknumber(L, 3);
+ break;
+ default:
+ luaL_error(L, "internal error: invalid attribute type");
+ break;
+ }
+
+ if (!attr->update(attr, idx, &av))
+ luaL_error(L, "attribute update failed");
+
+ MRP_LUA_LEAVE(0);
+}
+
+static int attributes_getlength(lua_State *L)
+{
+ attr_t *attr = check_attributes(L, 1);
+ attr_def_t *def = attr->def;
+
+ MRP_LUA_ENTER;
+
+ lua_pushinteger(L, def ? def->nattr : 0);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static attr_t *check_attributes(lua_State *L, int idx)
+{
+ return (attr_t *)luaL_checkudata(L, idx, ATTRIBUTE_CLASSID);
+}
+
+static int push_attributes(lua_State *L, int attr_tbl)
+{
+ lua_rawgeti(L, LUA_REGISTRYINDEX, attr_tbl);
+ return 1;
+}
+
+
+static int appclass_create(lua_State *L)
+{
+ appclass_t *appclass;
+ size_t fldnamlen;
+ const char *fldnam;
+ int priority = 0;
+ int modal = -1;
+ int share = -1;
+ mrp_resource_order_t order = 0;
+ const char *name = NULL;
+
+ MRP_LUA_ENTER;
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case PRIORITY:
+ priority = luaL_checkint(L, -1);
+ break;
+
+ case MODAL:
+ modal = check_boolean(L, -1);
+ break;
+
+ case SHARE:
+ share = check_boolean(L, -1);
+ break;
+
+ case ORDER:
+ order = check_order(L, -1);
+ break;
+
+ default:
+ luaL_error(L, "unexpected field '%s'", fldnam);
+ break;
+ }
+ }
+
+ if (!name)
+ luaL_error(L, "missing or wrong name field");
+ if (modal < 0)
+ luaL_error(L, "missing or wrong modal field");
+ if (modal && share)
+ luaL_error(L, "modal class can't share");
+ if (share < 0)
+ luaL_error(L, "missing or wrong share field");
+ if (!order)
+ luaL_error(L, "missing or wrong order field");
+ if (priority < 0)
+ luaL_error(L, "negative priority");
+ if (!mrp_application_class_create(name, priority, modal, share, order))
+ luaL_error(L, "failed to create application class '%s'", name);
+
+ appclass = (appclass_t *)mrp_lua_create_object(L, APPCLASS_CLASS, name,0);
+
+ if (!appclass)
+ luaL_error(L, "invalid or duplicate name '%s'", name);
+ else {
+ appclass->name = name;
+ mrp_log_info("application class '%s' created", name);
+ }
+ MRP_LUA_LEAVE(1);
+}
+
+static int appclass_getfield(lua_State *L)
+{
+ appclass_t *appclass = to_appclass(L, 1);
+ field_t fld = field_check(L, 2, NULL);
+ mrp_application_class_t *ac;
+
+ MRP_LUA_ENTER;
+
+ lua_pop(L, 1);
+
+ if (!appclass || !(ac = mrp_application_class_find(appclass->name)))
+ lua_pushnil(L);
+ else {
+ switch (fld) {
+ case NAME: lua_pushstring(L, ac->name); break;
+ case PRIORITY: lua_pushinteger(L, ac->priority); break;
+ case MODAL: lua_pushboolean(L, ac->modal); break;
+ case SHARE: lua_pushboolean(L, ac->share); break;
+ case ORDER: push_order(L, ac->order); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int appclass_setfield(lua_State *L)
+{
+ MRP_LUA_ENTER;
+
+ luaL_error(L, "can't modify application classes after definition");
+
+ MRP_LUA_LEAVE(0);
+}
+
+static void appclass_destroy(void *data)
+{
+ appclass_t *appclass = (appclass_t *)data;
+
+ MRP_LUA_ENTER;
+
+ mrp_free((void *)appclass->name);
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+#if 0
+static appclass_t *check_appclass(lua_State *L, int idx)
+{
+ return (appclass_t *)mrp_lua_check_object(L, APPCLASS_CLASS, idx);
+}
+#endif
+
+static appclass_t *to_appclass(lua_State *L, int idx)
+{
+ return (appclass_t *)mrp_lua_to_object(L, APPCLASS_CLASS, idx);
+}
+
+
+static int zone_create(lua_State *L)
+{
+ zone_t *zone;
+ size_t fldnamlen;
+ const char *fldnam;
+ uint32_t id;
+ const char *name = NULL;
+ mrp_attr_t *attrs = NULL;
+
+ MRP_LUA_ENTER;
+
+ MRP_ASSERT(zone_attr_defs, "invocation prior to initialization");
+
+ if (!zone_attr_defs->attrs)
+ luaL_error(L, "attempt to create zone before defining attributes");
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case ATTRIBUTES:
+ attrs = check_attrs(L, -1, zone_attr_defs);
+ break;
+
+ default:
+ luaL_error(L, "unexpected field '%s'", fldnam);
+ break;
+ }
+ }
+
+ if (!name)
+ luaL_error(L, "missing or wrong name field");
+ if ((id = mrp_zone_create(name,attrs)) == MRP_ZONE_ID_INVALID)
+ luaL_error(L, "failed to create zone");
+
+ free_attrs(attrs);
+
+ zone = (zone_t *)mrp_lua_create_object(L, ZONE_CLASS, name,0);
+
+ if (!zone)
+ luaL_error(L, "invalid or duplicate name '%s'", name);
+ else {
+ zone->id = id;
+ zone->name = name;
+ zone->attr_tbl = attributes_create(L, ZONE,zone, zone_attr_defs,
+ fetch_zone_attribute,
+ update_zone_attribute);
+
+ mrp_log_info("zone '%s' created", name);
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int zone_getfield(lua_State *L)
+{
+ zone_t *zone = to_zone(L, 1);
+ field_t fld = field_check(L, 2, NULL);
+
+ MRP_LUA_ENTER;
+
+ lua_pop(L, 1);
+
+ if (!zone) {
+ /* attempt to access a zone definition field */
+ switch (fld) {
+ case ATTRIBUTES: mrp_lua_push_object(L, zone_attr_defs); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+ else {
+ /* attempt to access a zone instance field */
+ switch (fld) {
+ case ATTRIBUTES: push_attributes(L, zone->attr_tbl); break;
+ case ID: lua_pushinteger(L, zone->id + 1); break;
+ case NAME: lua_pushstring(L, zone->name); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int zone_setfield(lua_State *L)
+{
+ zone_t *zone = to_zone(L, 1);
+ field_t fld = field_check(L, 2, NULL);
+
+ MRP_LUA_ENTER;
+
+ if (zone || fld != ATTRIBUTES)
+ luaL_error(L, "zones can't be exetended after definition");
+
+ MRP_LUA_LEAVE(0);
+}
+
+static void zone_destroy(void *data)
+{
+ /* zone_t *zone = (zone_t *)data; */
+
+ MRP_UNUSED(data);
+
+ MRP_LUA_ENTER;
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+#if 0
+static zone_t *check_zone(lua_State *L, int idx)
+{
+ return (zone_t *)mrp_lua_check_object(L, ZONE_CLASS, idx);
+}
+#endif
+
+static zone_t *to_zone(lua_State *L, int idx)
+{
+ return (zone_t *)mrp_lua_to_object(L, ZONE_CLASS, idx);
+}
+
+static int zone_attr_create(lua_State *L)
+{
+ mrp_attr_def_t *attrs;
+ int nattr;
+
+ MRP_LUA_ENTER;
+
+ MRP_ASSERT(zone_attr_defs, "invocation prior to initialization");
+
+ if (zone_attr_defs->attrs)
+ luaL_error(L, "zone attributes already defined");
+ else {
+ attrs = check_attrdefs(L, 2, &nattr);
+
+ mrp_zone_definition_create(attrs);
+
+ zone_attr_defs->nattr = nattr;
+ zone_attr_defs->attrs = attrs;
+ }
+
+ mrp_lua_push_object(L, zone_attr_defs);
+
+ mrp_log_info("zone attributes defined");
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int zone_attr_getfield(lua_State *L)
+{
+ zone_t *zone;
+ int idx;
+
+ MRP_LUA_ENTER;
+
+ MRP_ASSERT(zone_attr_defs, "invocation prior to initialization");
+
+ if (!(zone = to_zone(L, 1))) {
+ mrp_debug("zone attribute definition => attribute index");
+ if ((idx = check_attrindex(L, 2, zone_attr_defs)) < 0)
+ lua_pushnil(L);
+ else
+ lua_pushinteger(L, idx);
+ }
+ else {
+ mrp_debug("zone attribute => nil");
+ lua_pushnil(L);
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int zone_attr_setfield(lua_State *L)
+{
+ MRP_UNUSED(L);
+
+ MRP_LUA_ENTER;
+
+ MRP_LUA_LEAVE(0);
+}
+
+
+static void zone_attr_destroy(void *data)
+{
+ /* attr_def_t *attr = (attr_def_t *)data; */
+
+ MRP_UNUSED(data);
+
+ MRP_LUA_ENTER;
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+#if 0
+static attr_def_t *check_zone_attr(lua_State *L, int idx)
+{
+ return (attr_def_t *)mrp_lua_check_object(L, ZONE_ATTR_CLASS, idx);
+}
+#endif
+
+#if 0
+static attr_def_t *to_zone_attr(lua_State *L, int idx)
+{
+ return (attr_def_t *)mrp_lua_to_object(L, ZONE_ATTR_CLASS, idx);
+}
+#endif
+
+static bool fetch_zone_attribute(attr_t *attr, int idx, mrp_attr_t *retval)
+{
+ mrp_zone_t *z;
+ zone_t *zone;
+
+ if (attr->owner.type == ZONE && (zone = (zone_t *)attr->owner.data)) {
+ if ((z = mrp_zone_find_by_id(zone->id))) {
+ if (mrp_zone_read_attribute(z, idx, retval))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool update_zone_attribute(attr_t *attr, int idx, mrp_attr_t *value)
+{
+ mrp_zone_t *z;
+ zone_t *zone;
+
+ MRP_UNUSED(idx);
+ MRP_UNUSED(value);
+
+ if (attr->owner.type == ZONE && (zone = (zone_t *)attr->owner.data)) {
+ if ((z = mrp_zone_find_by_id(zone->id))) {
+#if 0
+ if (mrp_zone_write_attribute(z, idx, value))
+ return true;
+#endif
+ }
+ }
+
+ return false;
+}
+
+static int resclass_create_from_lua(lua_State *L)
+{
+ resclass_t *resclass;
+ size_t fldnamlen;
+ const char *fldnam;
+ int nattr = 0;
+ uint32_t id;
+ const char *name = NULL;
+ mrp_attr_def_t *attrs = NULL;
+ bool shareable = false;
+ mrp_resource_mgr_ftbl_t *ftbl = NULL;
+ void *mgrdata = NULL;
+ attr_def_t *adef;
+
+ MRP_LUA_ENTER;
+
+ MRP_LUA_FOREACH_FIELD(L, 2, fldnam, fldnamlen) {
+
+ switch (field_name_to_type(fldnam, fldnamlen)) {
+
+ case NAME:
+ name = mrp_strdup(luaL_checkstring(L, -1));
+ break;
+
+ case SHAREABLE:
+ luaL_argcheck(L, lua_isboolean(L,-1), 2, "attempt to assign "
+ "non-boolean value to 'shareable' field");
+ shareable = lua_toboolean(L, -1);
+ break;
+
+ case ATTRIBUTES:
+ attrs = check_attrdefs(L, -1, &nattr);
+ break;
+
+ default:
+ luaL_error(L, "unexpected field '%s'", fldnam);
+ break;
+ }
+ }
+
+ if (!name)
+ luaL_error(L, "missing or wrong name field");
+
+ id = mrp_resource_definition_create(name, shareable, attrs,ftbl,mgrdata);
+
+ MRP_ASSERT(id < MRP_RESOURCE_MAX, "resource id is out of range");
+
+ if (id == MRP_RESOURCE_ID_INVALID)
+ luaL_error(L, "failed to register resource class '%s'", name);
+
+ resclass = (resclass_t *)mrp_lua_create_object(L, RESCLASS_CLASS, name,0);
+ adef = mrp_allocz(sizeof(attr_def_t));
+
+ if (!resclass)
+ luaL_error(L, "invalid or duplicate name '%s'", name);
+ if (!adef)
+ luaL_error(L, "failed to allocate memory for attribute definitions");
+
+ resclass->id = id;
+ resclass->name = name;
+ resclass->attrs = attrs;
+
+ adef->nattr = nattr;
+ adef->attrs = attrs;
+
+ resource_attr_defs[id] = adef;
+
+ mrp_log_info("resource class '%s' created", name);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int resclass_getfield(lua_State *L)
+{
+ resclass_t *rc = to_resclass(L, 1);
+ field_t fld = field_check(L, 2, NULL);
+ mrp_resource_def_t *rd;
+
+ MRP_LUA_ENTER;
+
+ lua_pop(L, 1);
+
+ if (!rc || !(rd = mrp_resource_definition_find_by_name(rc->name)))
+ lua_pushnil(L);
+ else {
+ switch (fld) {
+ case NAME: lua_pushstring(L, rd->name); break;
+ case ID: lua_pushinteger(L, rd->id + 1); break;
+ case SHAREABLE: lua_pushboolean(L, rd->shareable); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int resclass_setfield(lua_State *L)
+{
+ MRP_LUA_ENTER;
+
+ luaL_error(L, "can't modify resource classes after definition");
+
+ MRP_LUA_LEAVE(1);
+}
+
+static void resclass_destroy(void *data)
+{
+ resclass_t *rc = (resclass_t *)data;
+
+ MRP_LUA_ENTER;
+
+ mrp_free((void *)rc->name);
+ free_attrdefs(rc->attrs);
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+#if 0
+static resclass_t *check_resclass(lua_State *L, int idx)
+{
+ return (resclass_t *)mrp_lua_check_object(L, RESCLASS_CLASS, idx);
+}
+#endif
+
+static resclass_t *to_resclass(lua_State *L, int idx)
+{
+ return (resclass_t *)mrp_lua_to_object(L, RESCLASS_CLASS, idx);
+}
+
+static int resource_getfield(lua_State *L)
+{
+ const char *name;
+ resource_t *res = check_resource(L, 1);
+ field_t fld = field_check(L, 2, &name);
+ mrp_resource_set_t *s;
+ mrp_resource_t *r;
+ mrp_resource_mask_t m;
+
+ MRP_LUA_ENTER;
+
+ switch (fld) {
+
+ case ATTRIBUTES:
+ push_attributes(L, res->attr_tbl);
+ break;
+
+ case SHARED:
+ case SHARE:
+ if (!(r = mrp_resource_set_find_resource(res->rsetid, res->name)))
+ lua_pushnil(L);
+ else
+ lua_pushboolean(L, r->shared);
+ break;
+
+ default:
+ if (!(s = mrp_resource_set_find_by_id(res->rsetid))) {
+ lua_pushnil(L);
+ break;
+ } else {
+ m = ((mrp_resource_mask_t)1) << res->resid;
+ }
+
+ switch (fld) {
+ case MANDATORY:
+ lua_pushboolean(L, s->resource.mask.mandatory & m ? true : false);
+ break;
+ case GRANT:
+ lua_pushboolean(L, s->resource.mask.grant & m ? true : false);
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+
+ break;
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int resource_setfield(lua_State *L)
+{
+ MRP_UNUSED(L);
+
+ MRP_LUA_ENTER;
+
+ MRP_LUA_LEAVE(0);
+}
+
+static resource_t *check_resource(lua_State *L, int idx)
+{
+ return (resource_t *)luaL_checkudata(L, idx, RESOURCE_CLASSID);
+}
+
+static bool fetch_resource_attribute(attr_t *attr, int idx, mrp_attr_t *retval)
+{
+ mrp_resource_set_t *rset;
+ resource_t *resource;
+ mrp_attr_t *a;
+
+ if (attr->owner.type == RESOURCE) {
+ if ((resource = (resource_t *)attr->owner.data)) {
+ if ((rset = mrp_resource_set_find_by_id(resource->rsetid))) {
+ a = mrp_resource_set_read_attribute(rset, resource->name,
+ idx, retval);
+ return a ? true : false;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+static bool update_resource_attribute(attr_t *attr, int idx, mrp_attr_t *value)
+{
+ mrp_resource_set_t *rset;
+ resource_t *resource;
+ mrp_attr_t values[2];
+ int sts;
+
+ MRP_UNUSED(idx);
+
+ if (attr->owner.type == RESOURCE) {
+ if ((resource = (resource_t *)attr->owner.data)) {
+ if ((rset = mrp_resource_set_find_by_id(resource->rsetid))) {
+ memcpy(values + 0, value, sizeof(mrp_attr_t));
+ memset(values + 1, 0, sizeof(mrp_attr_t));
+
+ sts = mrp_resource_set_write_attributes(rset, resource->name,
+ values);
+ return (sts < 0) ? false : true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static mrp_lua_resmethod_t *resmethod_create_from_c(lua_State *L)
+{
+ mrp_lua_resmethod_t *method = mrp_lua_create_object(L, RESMETHOD_CLASS,
+ "method",0);
+
+ if (!method)
+ luaL_error(L, "invalid or duplicate name 'method'");
+
+ return method;
+}
+
+static int resmethod_create_from_lua(lua_State *L)
+{
+ MRP_LUA_ENTER;
+
+ luaL_error(L, "singleton object has already been created");
+
+ lua_pushnil(L);
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int resmethod_getfield(lua_State *L)
+{
+ const char *name;
+ mrp_lua_resmethod_t *method = to_resmethod(L, 1);
+ field_t fld = field_check(L, 2, &name);
+
+ MRP_LUA_ENTER;
+
+ lua_pop(L, 1);
+
+ if (!method) {
+ /* attempt to access a resclass or owners */
+ switch (fld) {
+ case METHOD: mrp_lua_push_object(L, resource_methods); break;
+ case OWNERS: lua_pushstring(L,name); lua_rawget(L,1); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+ else {
+ /* attempt to access a method member */
+ if (!resource_methods)
+ lua_pushnil(L);
+ else {
+ switch (fld) {
+ case VETO:
+ case RECALC:
+ lua_pushstring(L, name);
+ lua_rawget(L, 1);
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int resmethod_setfield(lua_State *L)
+{
+ const char *name;
+ mrp_lua_resmethod_t *method = to_resmethod(L, 1);
+ field_t fld = field_check(L, 2, &name);
+
+ MRP_LUA_ENTER;
+
+ if (method) {
+ switch (fld) {
+ case VETO:
+ lua_pushstring(L, name);
+ lua_pushvalue(L, 3);
+ method->veto = mrp_funcarray_check(L, -1);
+ lua_rawset(L, 1);
+ break;
+ default:
+ luaL_error(L, "invalid method '%s'", name);
+ break;
+ }
+ }
+
+ MRP_LUA_LEAVE(0);
+}
+
+static void resmethod_destroy(void *data)
+{
+ mrp_lua_resmethod_t *method = (mrp_lua_resmethod_t *)data;
+
+ MRP_LUA_ENTER;
+
+ method->veto = NULL;
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static mrp_lua_resmethod_t *to_resmethod(lua_State *L, int idx)
+{
+ return (mrp_lua_resmethod_t *)mrp_lua_to_object(L, RESMETHOD_CLASS, idx);
+}
+
+
+static mrp_attr_def_t *check_attrdefs(lua_State *L, int t, int *ret_len)
+{
+ mrp_attr_def_t attrdefs[128];
+ mrp_attr_def_t *ad, *end, *dup;
+ size_t i;
+ const char *name;
+ const char *string;
+ const char *access;
+ bool value_set;
+ int len;
+ size_t size;
+ size_t namlen;
+
+ t = (t < 0) ? lua_gettop(L) + t + 1 : t;
+
+ luaL_checktype(L, t, LUA_TTABLE);
+
+ end = (ad = attrdefs) + (MRP_ARRAY_SIZE(attrdefs) - 1);
+
+ MRP_LUA_FOREACH_FIELD(L, t, name, namlen) {
+ if (!name[0])
+ luaL_error(L, "invalid attribute definition");
+ if (ad >= end)
+ luaL_error(L, "too many attributes");
+
+ ad->name = mrp_strdup(name);
+ ad->type = mqi_error;
+ ad->access = MRP_RESOURCE_READ;
+
+ value_set = false;
+
+ luaL_checktype(L, -1, LUA_TTABLE);
+
+
+ for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
+ if (lua_type(L, -2) != LUA_TNUMBER)
+ goto error;
+
+ i = lua_tointeger(L, -2);
+
+ switch (i) {
+ case 1:
+ ad->type = lua_tointeger(L, -1);
+ break;
+ case 2:
+ switch (ad->type) {
+ case mqi_string:
+ if ((string = lua_tostring(L, -1))) {
+ ad->value.string = mrp_strdup(string);
+ value_set = true;
+ }
+ break;
+ case mqi_integer:
+ ad->value.integer = lua_tointeger(L, -1);
+ value_set = true;
+ break;
+ case mqi_unsignd:
+ ad->value.integer = lua_tointeger(L, -1);
+ value_set = true;
+ break;
+ case mqi_floating:
+ ad->value.floating = lua_tonumber(L, -1);
+ value_set = true;
+ break;
+ default:
+ break;
+ }
+ break;
+ case 3:
+ if (!(access = lua_tostring(L, -1)))
+ ad->type = mqi_error;
+ else {
+ if (!strcasecmp(access, "read"))
+ ad->access = MRP_RESOURCE_READ;
+ else if (!strcasecmp(access, "write"))
+ ad->access = MRP_RESOURCE_WRITE;
+ else if (!strcasecmp(access, "rw"))
+ ad->access = MRP_RESOURCE_RW;
+ else
+ ad->type = mqi_error;
+ }
+ break;
+ default:
+ ad->type = mqi_error;
+ break;
+ }
+ } /* for */
+
+ if (!value_set ||
+ (ad->type != mqi_string &&
+ ad->type != mqi_integer &&
+ ad->type != mqi_unsignd &&
+ ad->type != mqi_floating ))
+ goto error;
+
+ ad++;
+ } /* foreach */
+
+ memset(ad, 0, sizeof(mrp_attr_def_t));
+
+ len = ad - attrdefs;
+ size = sizeof(mrp_attr_def_t) * (len+1);
+ dup = mrp_alloc(size);
+
+ if (!dup)
+ luaL_error(L, "failed to allocate %u byte memory", size);
+
+ memcpy(dup, attrdefs, size);
+
+ if (ret_len)
+ *ret_len = len;
+
+ return dup;
+
+ error:
+ luaL_argerror(L, t, "malformed attribute definition");
+ return NULL;
+}
+
+
+static void free_attrdefs(mrp_attr_def_t *attrdefs)
+{
+ mrp_attr_def_t *ad;
+
+ if (attrdefs) {
+ for (ad = attrdefs; ad->name; ad++) {
+ mrp_free((void *)ad->name);
+ if (ad->type == mqi_string)
+ mrp_free((void *)ad->value.string);
+ }
+ mrp_free(attrdefs);
+ }
+}
+
+static int attr_name_to_index(const char *name, attr_def_t *def)
+{
+ mrp_attr_def_t *attrs = def->attrs;
+ int idx;
+
+ for (idx = 0; idx < def->nattr; idx++) {
+ if (!strcmp(name, attrs[idx].name))
+ return idx;
+ }
+
+ return -1;
+}
+
+static mrp_attr_t *check_attrs(lua_State *L, int t, attr_def_t *defs)
+{
+ mrp_attr_t attr[128];
+ mrp_attr_t *at, *end, *dup;
+ const char *name;
+ size_t namlen;
+ size_t len;
+ size_t size;
+ int i;
+
+ t = (t < 0) ? lua_gettop(L) + t + 1 : t;
+
+ luaL_checktype(L, t, LUA_TTABLE);
+
+ end = (at = attr) + (MRP_ARRAY_SIZE(attr) - 1);
+
+ MRP_LUA_FOREACH_FIELD(L, t, name, namlen) {
+ if (!name[0])
+ luaL_error(L, "invalid attribute definition");
+ if (at >= end)
+ luaL_error(L, "too many attributes");
+ if ((i = attr_name_to_index(name, defs)) < 0)
+ luaL_error(L, "attribute %s do not exist", name);
+
+ at->name = mrp_strdup(name);
+
+ switch ((at->type = defs->attrs[i].type)) {
+ case mqi_string:
+ at->value.string = mrp_strdup(luaL_checkstring(L,-1));
+ break;
+ case mqi_integer:
+ at->value.integer = luaL_checkinteger(L,-1);
+ break;
+ case mqi_unsignd:
+ if ((at->value.integer = luaL_checkinteger(L,-1)) < 0)
+ luaL_error(L, "attempt to give negative value to an "
+ "unsigned integer");
+ break;
+ default:
+ luaL_error(L, "Internal error: invalid type for attribute");
+ break;
+ }
+
+ at++;
+ }
+
+ memset(at, 0, sizeof(mrp_attr_t));
+
+ len = at - attr;
+ size = sizeof(mrp_attr_t) * (len + 1);
+
+ dup = mrp_alloc(size);
+ memcpy(dup, attr, size);
+
+ return dup;
+}
+
+
+static void free_attrs(mrp_attr_t *attrs)
+{
+ mrp_attr_t *at;
+
+ if (attrs) {
+ for (at = attrs; at->name; at++) {
+ mrp_free((void *)at->name);
+ if (at->type == mqi_string)
+ mrp_free((void *)at->value.string);
+ }
+ mrp_free(attrs);
+ }
+}
+
+
+static int check_attrindex(lua_State *L, int arg, attr_def_t *def)
+{
+ const char *name;
+ int idx;
+
+ if (!def || !def->attrs)
+ return -1;
+
+ switch (lua_type(L, arg)) {
+ case LUA_TNUMBER:
+ idx = lua_tointeger(L, arg);
+ return (idx >= 0 && idx < def->nattr) ? idx : -1;
+ case LUA_TSTRING:
+ name = lua_tostring(L, arg);
+ return attr_name_to_index(name, def);
+ default:
+ return -1;
+ }
+}
+
+
+static int check_boolean(lua_State *L, int idx)
+{
+ if (!lua_isboolean(L, idx))
+ luaL_argerror(L, idx, "expected boolean");
+ return lua_toboolean(L, idx) ? 1 : 0;
+}
+
+static mrp_resource_order_t check_order(lua_State *L, int idx)
+{
+ const char *str = luaL_checkstring(L, idx);
+
+ if (!strcasecmp(str, "fifo"))
+ return MRP_RESOURCE_ORDER_FIFO;
+
+ if (!strcasecmp(str, "lifo"))
+ return MRP_RESOURCE_ORDER_LIFO;
+
+ luaL_error(L, "invalid value for order ('fifo' or 'lifo' accepted only)");
+
+ return MRP_RESOURCE_ORDER_UNKNOWN;
+}
+
+static int push_order(lua_State *L, mrp_resource_order_t order)
+{
+ const char *str;
+
+ switch (order) {
+ case MRP_RESOURCE_ORDER_FIFO: str = "fifo"; break;
+ case MRP_RESOURCE_ORDER_LIFO: str = "lifo"; break;
+ default: str = "<unknown>"; break;
+ }
+
+ lua_pushstring(L, str);
+
+ return 1;
+}
+
+
+static field_t field_check(lua_State *L, int idx, const char **ret_fldnam)
+{
+ const char *fldnam;
+ size_t fldnamlen;
+ field_t fldtyp;
+
+ if (!(fldnam = lua_tolstring(L, idx, &fldnamlen)))
+ fldtyp = 0;
+ else
+ fldtyp = field_name_to_type(fldnam, fldnamlen);
+
+ if (ret_fldnam)
+ *ret_fldnam = fldnam;
+
+ return fldtyp;
+}
+
+
+static field_t field_name_to_type(const char *name, size_t len)
+{
+ switch (len) {
+
+ case 2:
+ if (!strcmp(name, "id"))
+ return ID;
+ break;
+
+ case 4:
+ if (!strcmp(name, "name"))
+ return NAME;
+ if (!strcmp(name, "veto"))
+ return VETO;
+ break;
+
+ case 5:
+ if (!strcmp(name, "class"))
+ return CLASS;
+ if (!strcmp(name, "modal"))
+ return MODAL;
+ if (!strcmp(name, "share"))
+ return SHARE;
+ if (!strcmp(name, "grant"))
+ return GRANT;
+ if (!strcmp(name, "order"))
+ return ORDER;
+ break;
+
+ case 6:
+ if (!strcmp(name, "method"))
+ return METHOD;
+ if (!strcmp(name, "owners"))
+ return OWNERS;
+ if (!strcmp(name, "shared"))
+ return SHARED;
+ if (!strcmp(name, "recalc"))
+ return RECALC;
+ break;
+
+ case 8:
+ if (!strcmp(name, "priority"))
+ return PRIORITY;
+ break;
+
+ case 9:
+ if (!strcmp(name, "mandatory"))
+ return MANDATORY;
+ if (!strcmp(name, "shareable"))
+ return SHAREABLE;
+ break;
+
+ case 10:
+ if (!strcmp(name, "attributes"))
+ return ATTRIBUTES;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int method_recalc(lua_State *L)
+{
+ const char *zone_name;
+ mrp_zone_t *zone;
+
+ if (lua_type(L, 1) == LUA_TSTRING && (zone_name = lua_tostring(L, 1))) {
+
+ if (!(zone = mrp_zone_find_by_name(zone_name))) {
+ luaL_error(L, "can't recalculate resources in zone '%s': "
+ "no such zone", zone_name);
+ }
+
+ mrp_resource_owner_recalc(zone->id);
+ }
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_CONFIG_LUA_H__
+#define __MURPHY_RESOURCE_CONFIG_LUA_H__
+
+#include <lua.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/resource/data-types.h>
+
+typedef struct mrp_lua_resmethod_s mrp_lua_resmethod_t;
+
+struct mrp_lua_resmethod_s {
+ mrp_funcarray_t *veto;
+};
+
+
+mrp_lua_resmethod_t *mrp_lua_get_resource_methods(void);
+
+uint32_t mrp_lua_to_resource_id(lua_State *, int);
+
+int mrp_lua_resource_create(lua_State *, mrp_resource_t *);
+
+
+#endif /* __MURPHY_RESOURCE_CONFIG_LUA_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_DATA_TYPES_H__
+#define __MURPHY_DATA_TYPES_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <murphy-db/mqi-types.h>
+
+/*
+ * Remember: this should be smaller than
+ * sizeof(mrp_zone_mask_t) * 8
+ */
+#define MRP_ZONE_MAX 8
+#define MRP_ZONE_MASK (((mrp_zone_mask_t)1 << MRP_ZONE_MAX) - 1)
+
+#define MRP_KEY_STAMP_BITS 27
+#define MRP_KEY_STATE_BITS 1
+#define MRP_KEY_USAGE_BITS 1
+#define MRP_KEY_PRIORITY_BITS 3
+
+#define MRP_ZONE_ID_INVALID (~(uint32_t)0)
+#define MRP_RESOURCE_ID_INVALID (~(uint32_t)0)
+#define MRP_RESOURCE_REQNO_INVALID (~(uint32_t)0)
+
+#define MRP_RESOURCE_MAX (sizeof(mrp_resource_mask_t) * 8)
+#define MRP_ATTRIBUTE_MAX (sizeof(mrp_attribute_mask_t) * 8)
+
+typedef enum mrp_resource_state_e mrp_resource_state_t;
+typedef enum mrp_resource_order_e mrp_resource_order_t;
+typedef enum mrp_resource_access_e mrp_resource_access_t;
+typedef enum mrp_resource_event_e mrp_resource_event_t;
+
+typedef struct mrp_resource_client_s mrp_resource_client_t;
+typedef union mrp_attr_value_u mrp_attr_value_t;
+typedef struct mrp_attr_def_s mrp_attr_def_t;
+typedef struct mrp_attr_s mrp_attr_t;
+typedef struct mrp_zone_def_s mrp_zone_def_t;
+typedef struct mrp_zone_s mrp_zone_t;
+typedef struct mrp_application_class_s mrp_application_class_t;
+typedef struct mrp_resource_owner_s mrp_resource_owner_t;
+typedef struct mrp_resource_set_s mrp_resource_set_t;
+typedef struct mrp_resource_def_s mrp_resource_def_t;
+typedef struct mrp_resource_s mrp_resource_t;
+typedef struct mrp_resource_mgr_ftbl_s mrp_resource_mgr_ftbl_t;
+typedef struct mrp_resource_mgr_s mrp_resource_mgr_t;
+
+typedef struct mrp_resource_ownersref_s mrp_resource_ownersref_t;
+typedef struct mrp_resource_setref_s mrp_resource_setref_t;
+
+typedef uint32_t mrp_resource_mask_t;
+typedef uint32_t mrp_attribute_mask_t;
+typedef uint32_t mrp_zone_mask_t;
+
+
+enum mrp_resource_state_e {
+ mrp_resource_no_request = 0,
+ mrp_resource_release,
+ mrp_resource_acquire,
+};
+
+
+enum mrp_resource_access_e {
+ MRP_RESOURCE_ACCESS_NONE = 0,
+ MRP_RESOURCE_READ = 1,
+ MRP_RESOURCE_WRITE = 2,
+ MRP_RESOURCE_RW = (MRP_RESOURCE_READ | MRP_RESOURCE_WRITE)
+};
+
+enum mrp_resource_order_e {
+ MRP_RESOURCE_ORDER_UNKNOWN = 0,
+ MRP_RESOURCE_ORDER_FIFO,
+ MRP_RESOURCE_ORDER_LIFO
+};
+
+union mrp_attr_value_u {
+ const char *string;
+ int32_t integer;
+ uint32_t unsignd;
+ double floating;
+};
+
+struct mrp_attr_def_s {
+ const char *name;
+ mrp_resource_access_t access;
+ mqi_data_type_t type;
+ mrp_attr_value_t value;
+};
+
+struct mrp_attr_s {
+ const char *name;
+ mqi_data_type_t type;
+ mrp_attr_value_t value;
+};
+
+
+typedef void (*mrp_resource_event_cb_t)(uint32_t, mrp_resource_set_t *, void*);
+
+enum mrp_resource_event_e {
+ MRP_RESOURCE_EVENT_UNKNOWN = 0,
+ MRP_RESOURCE_EVENT_CREATED,
+ MRP_RESOURCE_EVENT_DESTROYED,
+ MRP_RESOURCE_EVENT_ACQUIRE,
+ MRP_RESOURCE_EVENT_RELEASE
+};
+
+typedef void (*mrp_manager_notify_func_t)(mrp_resource_event_t, mrp_zone_t *,
+ mrp_application_class_t *,
+ mrp_resource_t *, void *);
+typedef void (*mrp_manager_init_func_t)(mrp_zone_t *, void *);
+typedef bool (*mrp_manager_alloc_func_t)(mrp_zone_t *,mrp_resource_t *,void*);
+typedef void (*mrp_manager_free_func_t)(mrp_zone_t *,mrp_resource_t *,void *);
+typedef bool (*mrp_manager_advice_func_t)(mrp_zone_t *,mrp_resource_t*,void *);
+typedef void (*mrp_manager_commit_func_t)(mrp_zone_t *, void *);
+
+struct mrp_resource_mgr_ftbl_s {
+ mrp_manager_notify_func_t notify;
+ mrp_manager_init_func_t init;
+ mrp_manager_alloc_func_t allocate;
+ mrp_manager_free_func_t free;
+ mrp_manager_advice_func_t advice;
+ mrp_manager_commit_func_t commit;
+};
+
+
+
+#endif /* __MURPHY_DATA_TYPES_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <errno.h>
+
+#include <murphy/common.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#include <murphy/resource/client-api.h>
+
+#define MAX_ATTRS 128
+
+/*
+ * Bindings to the resource library
+ */
+
+#define ATTRIBUTE_LUA_CLASS MRP_LUA_CLASS(attribute, lua)
+#define RESOURCE_LUA_CLASS MRP_LUA_CLASS(resource, lua)
+#define RESOURCE_SET_LUA_CLASS MRP_LUA_CLASS(resource_set, lua)
+
+typedef struct resource_lua_s resource_lua_t;
+typedef struct resource_set_lua_s resource_set_lua_t;
+typedef struct attribute_lua_s attribute_lua_t;
+
+struct attribute_lua_s {
+ lua_State *L; /* Lua execution context */
+
+ resource_set_lua_t *resource_set;
+
+ int initialized;
+ resource_lua_t *parent;
+};
+
+struct resource_lua_s {
+ lua_State *L; /* Lua execution context */
+
+ bool available; /* status */
+ bool acquired; /* status*/
+ bool shared; /* bookkeeping */
+ bool mandatory; /* bookkeeping */
+ char *resource_name; /* bookkeeping */
+
+ resource_set_lua_t *parent; /* resource set the resource belongs to */
+
+ int attributes; /* for lua bindings */
+ attribute_lua_t *real_attributes; /* real attributes */
+};
+
+struct resource_set_lua_s {
+ lua_State *L; /* Lua execution context */
+
+ mrp_resource_set_t *resource_set; /* associated murphy resource set */
+
+ int id; /* resource set internal id */
+ int callback; /* callback */
+ bool available; /* status */
+ bool acquired; /* status */
+ bool autorelease; /* input */
+ bool dont_wait; /* input */
+ char *zone; /* input */
+ char *application_class; /* input */
+ int32_t priority; /* input */
+
+ bool committed; /* resource set is locked and cannot be edited anymore */
+ bool initialized; /* resource set was returned to the Lua user */
+
+ mrp_htbl_t *resources;
+};
+
+static mrp_resource_client_t *client = NULL;
+uint32_t n_sets = 0;
+
+/* resource set */
+
+static int resource_set_lua_create(lua_State *L);
+
+static int resource_set_get_resources(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+static int resource_set_get_id(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+static int resource_set_add_resource(lua_State *L);
+static int resource_set_acquire(lua_State *L);
+static int resource_set_release(lua_State *L);
+
+static void resource_set_lua_changed(void *data, lua_State *L, int member);
+static void resource_set_lua_destroy(void *data);
+static int resource_set_lua_stringify(lua_State *L);
+
+#define RS_OFFS(m) MRP_OFFSET(resource_set_lua_t, m)
+#define RS_RDONLY MRP_LUA_CLASS_READONLY
+#define RS_NOTIFY MRP_LUA_CLASS_NOTIFY
+#define RS_NOFLAGS MRP_LUA_CLASS_NOFLAGS
+#define RS_RAWGETTER MRP_LUA_CLASS_RAWGETTER
+
+MRP_LUA_METHOD_LIST_TABLE(resource_set_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(resource_set_lua_create)
+ MRP_LUA_METHOD(addResource, resource_set_add_resource)
+ MRP_LUA_METHOD(acquire, resource_set_acquire)
+ MRP_LUA_METHOD(release, resource_set_release));
+
+MRP_LUA_METHOD_LIST_TABLE(resource_set_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (resource_set_lua_create)
+ MRP_LUA_OVERRIDE_STRINGIFY(resource_set_lua_stringify));
+
+MRP_LUA_MEMBER_LIST_TABLE(resource_set_lua_members,
+ MRP_LUA_CLASS_INTEGER("id", RS_OFFS(id), NULL, resource_set_get_id, RS_RDONLY)
+ MRP_LUA_CLASS_STRING("application_class", RS_OFFS(application_class), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_STRING("zone", RS_OFFS(zone), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_ANY("resources", RS_OFFS(resources), NULL, resource_set_get_resources, RS_RDONLY | RS_RAWGETTER)
+ MRP_LUA_CLASS_BOOLEAN("dont_wait", RS_OFFS(dont_wait), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("autorelease", RS_OFFS(autorelease), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("available", RS_OFFS(available), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("acquired", RS_OFFS(acquired), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_INTEGER("priority", RS_OFFS(priority), NULL, NULL, RS_RDONLY)
+ MRP_LUA_CLASS_LFUNC("callback", RS_OFFS(callback), NULL, NULL, RS_NOTIFY));
+
+/* be careful! needs to be in the same order as the member list table */
+typedef enum {
+ RESOURCE_SET_MEMBER_ID,
+ RESOURCE_SET_MEMBER_APPLICATION_CLASS,
+ RESOURCE_SET_MEMBER_ZONE,
+ RESOURCE_SET_MEMBER_RESOURCES,
+ RESOURCE_SET_MEMBER_DONT_WAIT,
+ RESOURCE_SET_MEMBER_AUTORELEASE,
+ RESOURCE_SET_MEMBER_AVAILABLE,
+ RESOURCE_SET_MEMBER_ACQUIRED,
+ RESOURCE_SET_MEMBER_PRIORITY,
+ RESOURCE_SET_MEMBER_CALLBACK,
+} resource_set_member_t;
+
+MRP_LUA_DEFINE_CLASS(resource_set, lua, resource_set_lua_t, resource_set_lua_destroy,
+ resource_set_lua_methods, resource_set_lua_overrides,
+ resource_set_lua_members, NULL, resource_set_lua_changed, NULL, NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_DYNAMIC);
+
+/* resource */
+
+static int resource_lua_create(lua_State *L);
+static void resource_lua_destroy(void *data);
+static int resource_lua_stringify(lua_State *L);
+static void resource_lua_changed(void *data, lua_State *L, int member);
+
+/* get the whole attribute table */
+static int resource_get_attributes(void *data, lua_State *L, int member, mrp_lua_value_t *v);
+static int resource_set_attributes(void *data, lua_State *L, int member, mrp_lua_value_t *v);
+
+#define R_OFFS(m) MRP_OFFSET(resource_lua_t, m)
+#define R_RDONLY MRP_LUA_CLASS_READONLY
+#define R_NOTIFY MRP_LUA_CLASS_NOTIFY
+#define R_NOFLAGS MRP_LUA_CLASS_NOFLAGS
+#define R_RAWGETTER MRP_LUA_CLASS_RAWGETTER
+#define R_RAWSETTER MRP_LUA_CLASS_RAWSETTER
+
+MRP_LUA_METHOD_LIST_TABLE(resource_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(resource_lua_create));
+
+MRP_LUA_METHOD_LIST_TABLE(resource_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (resource_lua_create)
+ MRP_LUA_OVERRIDE_STRINGIFY(resource_lua_stringify));
+
+MRP_LUA_MEMBER_LIST_TABLE(resource_lua_members,
+ MRP_LUA_CLASS_ANY("attributes", R_OFFS(attributes), resource_set_attributes, resource_get_attributes, R_RAWGETTER | R_RAWSETTER)
+ MRP_LUA_CLASS_STRING("resource_name", R_OFFS(resource_name), NULL, NULL, R_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("available", R_OFFS(available), NULL, NULL, R_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("acquired", R_OFFS(acquired), NULL, NULL, R_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("shared", R_OFFS(shared), NULL, NULL, R_RDONLY)
+ MRP_LUA_CLASS_BOOLEAN("mandatory", R_OFFS(mandatory), NULL, NULL, R_RDONLY));
+
+/* be careful! needs to be in the same order as the member list table */
+typedef enum {
+ RESOURCE_MEMBER_ATTRIBUTES,
+ RESOURCE_MEMBER_RESOURCE_NAME,
+ RESOURCE_MEMBER_AVAILABLE,
+ RESOURCE_MEMBER_ACQUIRED,
+ RESOURCE_MEMBER_SHARED,
+ RESOURCE_MEMBER_MANDATORY,
+} resource_member_t;
+
+MRP_LUA_DEFINE_CLASS(resource, lua, resource_lua_t, resource_lua_destroy,
+ resource_lua_methods, resource_lua_overrides,
+ resource_lua_members, NULL, resource_lua_changed, NULL, NULL,
+ MRP_LUA_CLASS_NOFLAGS);
+
+
+/* attribute table */
+
+/* The problem is that attribute table is a member of resource, meaning that
+ we will get events when the full attribute table is changed but won't get
+ any when a unique element of the attribute table is accessed. The solution
+ is to have the attributes handled as an object, whose constructor initializes
+ the attributes from the resource library. Any access to an attribute will
+ be seen as an access to a member and intercepted with "changed". */
+
+static int attribute_lua_create(lua_State *L);
+static void attribute_lua_destroy(void *data);
+static int attribute_lua_stringify(lua_State *L);
+static void attribute_lua_changed(void *data, lua_State *L, int member);
+static int attribute_lua_getfield(lua_State *L);
+static int attribute_lua_setfield(lua_State *L);
+
+#define A_OFFS(m) MRP_OFFSET(attribute_lua_t, m)
+#define A_RDONLY MRP_LUA_CLASS_READONLY
+#define A_NOTIFY MRP_LUA_CLASS_NOTIFY
+#define A_NOFLAGS MRP_LUA_CLASS_NOFLAGS
+#define A_RAWGETTER MRP_LUA_CLASS_RAWGETTER
+#define A_RAWSETTER MRP_LUA_CLASS_RAWSETTER
+
+MRP_LUA_METHOD_LIST_TABLE(attribute_lua_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(attribute_lua_create));
+
+MRP_LUA_METHOD_LIST_TABLE(attribute_lua_overrides,
+ MRP_LUA_OVERRIDE_CALL (attribute_lua_create)
+ MRP_LUA_OVERRIDE_STRINGIFY(attribute_lua_stringify)
+ MRP_LUA_OVERRIDE_GETFIELD (attribute_lua_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (attribute_lua_setfield));
+
+/* "initialized" is a placeholder */
+
+MRP_LUA_MEMBER_LIST_TABLE(attribute_lua_members,
+ MRP_LUA_CLASS_BOOLEAN("initialized", A_OFFS(initialized), NULL, NULL, A_RDONLY));
+
+/* be careful! needs to be in the same order as the member list table */
+typedef enum {
+ ATTRIBUTE_MEMBER_INITIALIZED,
+} attribute_member_t;
+
+MRP_LUA_DEFINE_CLASS(attribute, lua, attribute_lua_t, attribute_lua_destroy,
+ attribute_lua_methods, attribute_lua_overrides,
+ attribute_lua_members, NULL, attribute_lua_changed, NULL, NULL,
+ MRP_LUA_CLASS_NOFLAGS);
+
+/* resource set */
+
+static inline resource_set_lua_t *resource_set_lua_check(lua_State *L, int idx)
+{
+ return (resource_set_lua_t *) mrp_lua_check_object(L, RESOURCE_SET_LUA_CLASS, idx);
+}
+
+static int resource_set_add_resource(lua_State *L)
+{
+ int narg;
+ resource_set_lua_t *rset;
+ resource_lua_t *resource;
+ const char *resource_name;
+ bool shared = FALSE;
+ bool mandatory = TRUE;
+ mrp_attr_t attribute_list[MAX_ATTRS], *attrs;
+
+ mrp_debug("> add_resource");
+
+ narg = lua_gettop(L);
+
+ if (narg != 2)
+ return luaL_error(L, "expecting one argument");
+
+ rset = resource_set_lua_check(L, 1);
+
+ if (!rset)
+ goto error;
+
+ /* the argument should be a table with at least "resource_name" index */
+
+ if (!lua_istable(L, -1))
+ return luaL_error(L, "argument error -- not a table");
+
+ lua_pushstring(L, "resource_name");
+ lua_gettable(L, -2);
+
+ if (!lua_isstring(L, -1))
+ return luaL_error(L, "'resource_name' is a mandatory field");
+
+ resource_name = lua_tostring(L, -1);
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "mandatory");
+ lua_gettable(L, -2);
+
+ if (lua_isboolean(L, -1)) {
+ mandatory = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+
+ lua_pushstring(L, "shared");
+ lua_gettable(L, -2);
+
+ if (lua_isboolean(L, -1)) {
+ shared = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+
+ /* create resource object and add it to the resource table in the resource
+ * set object */
+
+ resource = (resource_lua_t *) mrp_lua_create_object(L, RESOURCE_LUA_CLASS,
+ NULL, 0);
+
+ if (!resource)
+ goto error;
+
+ /* mrp_lua_object_ref_value(resource, L, 0); */
+
+ resource->mandatory = mandatory;
+ resource->shared = shared;
+ resource->acquired = FALSE;
+ resource->available = FALSE;
+ resource->resource_name = mrp_strdup(resource_name);
+
+ if (!resource->resource_name)
+ goto error;
+
+ resource->parent = rset;
+ resource->L = L;
+
+ resource->real_attributes = (attribute_lua_t *) mrp_lua_create_object(L,
+ ATTRIBUTE_LUA_CLASS, NULL, 0);
+
+ if (!resource->real_attributes)
+ goto error;
+
+ /* mrp_lua_object_ref_value(resource->real_attributes, L, 0); */
+
+ resource->real_attributes->L = L;
+ resource->real_attributes->parent = resource;
+ resource->real_attributes->resource_set = rset;
+ resource->real_attributes->initialized = TRUE;
+
+ attrs = mrp_resource_set_read_all_attributes(rset->resource_set,
+ resource->resource_name, MAX_ATTRS-1, attribute_list);
+
+ if (mrp_resource_set_add_resource(rset->resource_set,
+ resource->resource_name, shared, attrs, mandatory) < 0)
+ goto error;
+
+ /* add to resource map */
+
+ mrp_debug("inserted resource %s to %p", resource->resource_name, rset);
+ mrp_htbl_insert(rset->resources, resource->resource_name, resource);
+
+ return 0;
+
+error:
+ /* TODO: clean up the already allocated objects */
+
+ return luaL_error(L, "internal resource library error");
+}
+
+static int resource_set_acquire(lua_State *L)
+{
+ resource_set_lua_t *rset;
+
+ mrp_debug("acquire");
+
+ rset = resource_set_lua_check(L, 1);
+
+ if (!rset)
+ return luaL_error(L, "internal error");
+
+ if (!rset->committed) {
+
+ /* Application backend requires us to "commit" the resource set before
+ * we can use the resource set. It can be done only after all resources
+ * have been added to the resource set and all the attributes
+ * configured. */
+
+ if (mrp_application_class_add_resource_set(rset->application_class,
+ rset->zone, rset->resource_set, 0) < 0)
+ return luaL_error(L, "failed to commit the resource set");
+
+ rset->committed = TRUE;
+ }
+
+ mrp_resource_set_acquire(rset->resource_set, 0);
+
+ return 0;
+}
+
+static int resource_set_release(lua_State *L)
+{
+ resource_set_lua_t *rset;
+
+ mrp_debug("> release");
+
+ rset = resource_set_lua_check(L, 1);
+
+ if (!rset)
+ return luaL_error(L, "internal error");
+
+ if (!rset->committed) {
+
+ /* Committing the resource set here means that the resource set stays
+ * in released state but already receives events. */
+
+ if (mrp_application_class_add_resource_set(rset->application_class,
+ rset->zone, rset->resource_set, 0) < 0)
+ return luaL_error(L, "failed to commit the resource set");
+
+ rset->committed = TRUE;
+ }
+
+ mrp_resource_set_release(rset->resource_set, 0);
+
+ return 0;
+}
+
+
+void event_cb(uint32_t request_id, mrp_resource_set_t *resource_set, void *user_data)
+{
+ resource_set_lua_t *rset = (resource_set_lua_t *) user_data;
+ mrp_resource_mask_t grant, advice;
+ int top;
+
+ MRP_UNUSED(request_id);
+ MRP_UNUSED(resource_set);
+
+ mrp_debug("> event_cb");
+
+ top = lua_gettop(rset->L);
+
+ grant = mrp_get_resource_set_grant(rset->resource_set);
+ advice = mrp_get_resource_set_advice(rset->resource_set);
+
+ /* update resource set */
+ rset->acquired = !!grant;
+ rset->available = !!advice;
+
+ if (mrp_lua_object_deref_value(rset, rset->L, rset->callback, false)) {
+ mrp_lua_push_object(rset->L, rset);
+
+ if (lua_pcall(rset->L, 1, 0, 0) != 0)
+ mrp_log_error("failed to invoke Lua resource set callback: %s",
+ lua_tostring(rset->L, -1));
+ }
+
+ lua_settop(rset->L, top);
+}
+
+static void htbl_free_resource(void *key, void *object)
+{
+ resource_lua_t *res = (resource_lua_t *) object;
+ MRP_UNUSED(key);
+
+ mrp_lua_destroy_object(res->L, NULL, 0, res);
+#if 0
+
+ mrp_debug("lua-resource: htbl_free_resource %p, res: '%s'", res,
+ res->resource_name);
+
+ MRP_UNUSED(key);
+#endif
+}
+
+static int resource_set_lua_create(lua_State *L)
+{
+ char e[128] = "";
+ resource_set_lua_t *rset;
+ int narg;
+ mrp_htbl_config_t conf;
+
+ mrp_debug("create");
+
+ narg = lua_gettop(L);
+
+ rset = (resource_set_lua_t *) mrp_lua_create_object(L,
+ RESOURCE_SET_LUA_CLASS, NULL, 0);
+
+ if (!rset)
+ return luaL_error(L, "could not create Lua object");
+
+ rset->L = L;
+
+ /* user can affect these values */
+ rset->zone = mrp_strdup("default");
+ rset->application_class = NULL;
+ rset->autorelease = FALSE;
+ rset->dont_wait = FALSE;
+ rset->priority = 0;
+ rset->committed = FALSE;
+ rset->initialized = FALSE;
+
+ switch (narg) {
+ case 2:
+ /* argument table */
+ if (mrp_lua_init_members(rset, L, -2, e, sizeof(e)) != 1)
+ return luaL_error(L, "failed to initialize resource members (%s)",
+ e);
+ break;
+ default:
+ return luaL_error(L, "expecting a constructor argument, "
+ "got %d", narg);
+ }
+
+ if (rset->application_class == NULL)
+ return luaL_error(L, "application_class is a mandatory parameter");
+
+ if (rset->priority < 0)
+ rset->priority = 0;
+
+ /* initial state, these cannot be set by user */
+ rset->available = FALSE;
+ rset->acquired = FALSE;
+
+ /* initialize resource map */
+ conf.nbucket = 0;
+ conf.nentry = 10;
+ conf.comp = mrp_string_comp;
+ conf.hash = mrp_string_hash;
+ conf.free = htbl_free_resource;
+
+ rset->resources = mrp_htbl_create(&conf);
+ if (!rset->resources)
+ goto error;
+
+ /* do the actual resource work */
+
+ if (!client) {
+ /* create the resource client */
+
+ client = mrp_resource_client_create("lua", NULL);
+
+ if (!client)
+ goto error;
+ }
+
+ rset->resource_set = mrp_resource_set_create(client, rset->autorelease,
+ rset->dont_wait, rset->priority, event_cb, rset);
+
+ if (rset->resource_set)
+ n_sets++;
+ else
+ goto error;
+
+ rset->initialized = TRUE;
+
+ mrp_lua_push_object(L, rset);
+
+ return 1;
+
+error:
+ return luaL_error(L, "internal resource library error");
+}
+
+static int resource_set_get_id(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ resource_set_lua_t *rset;
+ MRP_UNUSED(L);
+ MRP_UNUSED(member);
+
+ mrp_debug("> resource_set_get_id");
+
+ if (!v)
+ return 0;
+
+ rset = (resource_set_lua_t *) data;
+
+ v->s32 = mrp_get_resource_set_id(rset->resource_set);
+
+ return 1;
+}
+
+static int resource_set_get_resources(void *data, lua_State *L, int member, mrp_lua_value_t *v)
+{
+ resource_set_lua_t *rset;
+ void *iter = NULL;
+ mrp_resource_t *resource;
+ mrp_resource_mask_t grant, advice;
+
+ MRP_UNUSED(member);
+ MRP_UNUSED(v);
+
+ mrp_debug("> resource_set_get_resources");
+
+ rset = (resource_set_lua_t *) data;
+
+ if (!rset)
+ return luaL_error(L, "internal error");
+
+ grant = mrp_get_resource_set_grant(rset->resource_set);
+ advice = mrp_get_resource_set_advice(rset->resource_set);
+
+ lua_newtable(L);
+
+ /* push all resource objects to a table and return it */
+
+ while ((resource = mrp_resource_set_iterate_resources(rset->resource_set, &iter))) {
+ const char *name = mrp_resource_get_name(resource);
+ mrp_resource_mask_t mask = mrp_resource_get_mask(resource);
+
+ /* fetch and update the resource object */
+
+ resource_lua_t *res =
+ (resource_lua_t *) mrp_htbl_lookup(rset->resources,
+ (void *) name);
+
+ if (!res) {
+ mrp_log_error("resources out of sync: %s not found", name);
+ continue;
+ }
+
+ /* mrp_lua_object_ref_value(res, L, 0); */
+
+ res->acquired = !!(mask & grant);
+ res->available = !!(mask & advice);
+
+ /* TODO: update attributes */
+
+ /* push the resource to the table */
+ lua_pushstring(L, res->resource_name);
+ mrp_lua_push_object(L, res);
+ lua_settable(L, -3);
+ }
+
+ return 1;
+}
+
+static int resource_set_lua_stringify(lua_State *L)
+{
+ resource_set_lua_t *rset;
+
+ mrp_debug("> stringify");
+
+ rset = resource_set_lua_check(L, 1);
+
+ if (!rset) {
+ lua_pushstring(L, "invalid resource set");
+ return 1;
+ }
+
+ lua_pushfstring(L, "resource set '%s', acquired: %s, available: %s",
+ rset->application_class,
+ rset->acquired ? "yes" : "no",
+ rset->available ? "yes" : "no");
+
+ return 1;
+}
+
+static void resource_set_lua_destroy(void *data)
+{
+ resource_set_lua_t *rset = (resource_set_lua_t *) data;
+
+ mrp_debug("lua destructor for rset %p", rset);
+
+ /* remove resources from the resource set -- they are finally cleaned from
+ * their own lua destructors */
+
+ if (rset->resource_set)
+ mrp_resource_set_destroy(rset->resource_set);
+
+ if (rset->resources) {
+ mrp_debug("deleting htbl at %p", rset->resources);
+ mrp_htbl_destroy(rset->resources, TRUE);
+ rset->resources = NULL;
+ }
+
+ mrp_free(rset->zone);
+ mrp_free(rset->application_class);
+
+ if (rset->initialized) {
+ n_sets--;
+
+ if (n_sets == 0) {
+ mrp_resource_client_destroy(client);
+ client = NULL;
+ }
+ }
+
+ return;
+}
+
+static void resource_set_lua_changed(void *data, lua_State *L, int member)
+{
+ resource_set_lua_t *rset = (resource_set_lua_t *) data;
+
+ MRP_UNUSED(L);
+ MRP_UNUSED(member);
+
+ mrp_debug("> changed");
+
+ switch (member) {
+ case RESOURCE_SET_MEMBER_CALLBACK:
+ /* no special handling needed */
+ break;
+ default:
+ /* trying to change readonly properties, should trigger an error */
+ mrp_log_error("Trying to change a readonly property for resource set %s",
+ rset->application_class);
+ break;
+ }
+
+ return;
+}
+
+/* resource */
+
+static inline resource_lua_t *resource_lua_check(lua_State *L, int idx)
+{
+ return (resource_lua_t *) mrp_lua_check_object(L, RESOURCE_LUA_CLASS, idx);
+}
+
+static int resource_lua_create(lua_State *L)
+{
+ mrp_debug("> resource_lua_create");
+
+ return luaL_error(L, "Resource objects are created with ResourceSet:addResource()");
+}
+
+static int resource_lua_stringify(lua_State *L)
+{
+ resource_lua_t *res;
+
+ mrp_debug("> stringify");
+
+ res = resource_lua_check(L, 1);
+
+ if (!res) {
+ lua_pushstring(L, "invalid resource set");
+ return 1;
+ }
+
+ lua_pushfstring(L, "resource '%s', acquired: %s, available: %s, mandatory: %s, shared: %s",
+ res->resource_name,
+ res->acquired ? "yes" : "no",
+ res->available ? "yes" : "no",
+ res->mandatory ? "yes" : "no",
+ res->shared ? "yes" : "no");
+
+ return 1;
+}
+
+static void resource_lua_destroy(void *data)
+{
+ resource_lua_t *res = (resource_lua_t *) data;
+
+ mrp_debug("lua destructor for resource %p (%s)", res, res->resource_name);
+
+ mrp_free(res->resource_name);
+
+ /* TODO: unref res->real_attributes ? */
+
+ mrp_lua_destroy_object(res->L, NULL, 0, res->real_attributes);
+
+ return;
+}
+
+static void resource_lua_changed(void *data, lua_State *L, int member)
+{
+ MRP_UNUSED(data);
+ MRP_UNUSED(L);
+ MRP_UNUSED(member);
+
+ mrp_debug("> resource_changed");
+}
+
+#if 0
+static int resource_get_attributes(void *data, lua_State *L, int member, mrp_lua_value_t *v)
+{
+ resource_lua_t *res = (resource_lua_t *) data;
+ resource_set_lua_t *rset = res->parent;
+ mrp_attr_t attribute_list[MAX_ATTRS], *attrs;
+
+ mrp_debug("> resource_get_attributes");
+
+ lua_newtable(L);
+
+ attrs = mrp_resource_set_read_all_attributes(rset->resource_set,
+ res->resource_name,
+ MAX_ATTRS-1,
+ attribute_list);
+
+ while (attrs->name != NULL) {
+
+ switch (attrs->type) {
+ case mqi_string:
+ lua_pushstring(L, attrs->name);
+ lua_pushstring(L, attrs->value.string);
+ lua_settable(L, -3);
+ break;
+ case mqi_integer:
+ lua_pushstring(L, attrs->name);
+ lua_pushinteger(L, attrs->value.integer);
+ lua_settable(L, -3);
+ break;
+ case mqi_unsignd:
+ if (attrs->value.unsignd > INT_MAX) {
+ /* too big! */
+ mrp_log_error("Sorry, we don't support big unsigned values right now");
+ }
+ else {
+ lua_pushstring(L, attrs->name);
+ lua_pushinteger(L, attrs->value.unsignd);
+ lua_settable(L, -3);
+ }
+ break;
+ case mqi_floating:
+ lua_pushstring(L, attrs->name);
+ lua_pushnumber(L, attrs->value.floating);
+ lua_settable(L, -3);
+ break;
+ default:
+ mrp_log_error("Unhandled attribute type");
+ break;
+ }
+
+ attrs++;
+ }
+
+ return 1;
+}
+#else
+static int resource_get_attributes(void *data, lua_State *L, int member, mrp_lua_value_t *v)
+{
+ resource_lua_t *res = (resource_lua_t *) data;
+
+ MRP_UNUSED(member);
+ MRP_UNUSED(v);
+
+ mrp_debug("> resource_get_attributes");
+
+ mrp_lua_push_object(L, res->real_attributes);
+
+ return 1;
+}
+#endif
+
+static int resource_set_attributes(void *data, lua_State *L, int member, mrp_lua_value_t *v)
+{
+ resource_lua_t *res = (resource_lua_t *) data;
+ resource_set_lua_t *rset = res->parent;
+ mrp_attr_t attribute_list[MAX_ATTRS], *attrs, *orig;
+
+ MRP_UNUSED(member);
+ MRP_UNUSED(v);
+
+ mrp_debug("> resource_set_attributes");
+
+ if (!lua_istable(L, -1))
+ return luaL_error(L, "argument error -- not a table");
+
+ mrp_resource_set_read_all_attributes(rset->resource_set,
+ res->resource_name, MAX_ATTRS-1, attribute_list);
+
+ attrs = orig = attribute_list;
+
+ while (attrs->name != NULL) {
+ /* get the attribute from the lua table by name */
+
+ lua_pushstring(L, attrs->name);
+ lua_gettable(L, -2);
+
+ /* update the attribute */
+
+ switch (attrs->type) {
+ case mqi_string:
+ if (lua_isstring(L, -1)) {
+ attrs->value.string = lua_tostring(L, -1);
+ mrp_debug("updated attr '%s' to '%s'", attrs->name, attrs->value.string);
+ }
+ break;
+ case mqi_integer:
+ if (lua_isnumber(L, -1)) {
+ attrs->value.integer = lua_tointeger(L, -1);
+ mrp_debug("updated attr '%s' to '%i'", attrs->name, attrs->value.integer);
+ }
+ break;
+ case mqi_unsignd:
+ if (lua_isnumber(L, -1)) {
+ int val = lua_tointeger(L, -1);
+ if (val >= 0) {
+ attrs->value.unsignd = val;
+ mrp_debug("updated attr '%s' to '%u'", attrs->name, attrs->value.unsignd);
+ }
+ }
+ break;
+ case mqi_floating:
+ if (lua_isnumber(L, -1)) {
+ attrs->value.floating = lua_tonumber(L, -1);
+ mrp_debug("updated attr '%s' to '%f'", attrs->name, attrs->value.floating);
+ }
+ break;
+ default:
+ break;
+ }
+
+ lua_pop(L, 1);
+ attrs++;
+ }
+
+ /* write the attributes back */
+ mrp_resource_set_write_attributes(rset->resource_set, res->resource_name, orig);
+
+ return 1;
+}
+
+static inline attribute_lua_t *attribute_lua_check(lua_State *L, int idx)
+{
+ return (attribute_lua_t *) mrp_lua_check_object(L, ATTRIBUTE_LUA_CLASS, idx);
+}
+
+static int attribute_lua_create(lua_State *L)
+{
+ mrp_debug("> attribute_create");
+
+ return luaL_error(L, "Attribute objects are created with ResourceSet:addResource()");
+}
+
+static void attribute_lua_destroy(void *data)
+{
+ attribute_lua_t *attribute = (attribute_lua_t *) data;
+
+ mrp_debug("lua destructor for attribute table %p", attribute);
+
+ return;
+}
+
+static int attribute_lua_stringify(lua_State *L)
+{
+ attribute_lua_t *attribute = attribute_lua_check(L, 1);
+ resource_lua_t *res = attribute->parent;
+ resource_set_lua_t *rset = res->parent;
+ mrp_attr_t attribute_list[MAX_ATTRS], *attrs;
+
+ int available = 4095;
+ char buf[available+1], *p;
+ char numbuf[16];
+ int vallen;
+
+ mrp_debug("> attribute_stringify");
+
+ memset(buf, 0, sizeof(buf));
+ p = buf;
+
+ mrp_resource_set_read_all_attributes(rset->resource_set,
+ res->resource_name, MAX_ATTRS-1, attribute_list);
+
+ attrs = attribute_list;
+
+ while (attrs->name != NULL) {
+
+ int keylen = strlen(attrs->name);
+
+ /* we need space for 2 + null */
+ available -= keylen + 3;
+
+ if (available < 0)
+ goto outofspace;
+
+ strncpy(p, attrs->name, keylen);
+ p += keylen;
+
+ /*
+ * we copy ": \0" and then proceed to only
+ * move the pointer by two, thus we can
+ * add one to the amount of available
+ * space.
+ */
+ strncpy(p, ": ", 3);
+ p += 2;
+ available += 1;
+
+ switch (attrs->type) {
+ case mqi_string:
+ vallen = strlen(attrs->value.string);
+ available -= vallen + 1;
+ if (available < 0)
+ goto outofspace;
+ strncpy(p, attrs->value.string, vallen);
+ p += vallen;
+ *p = '\n';
+ p += 1;
+ break;
+ case mqi_integer:
+ snprintf(numbuf, sizeof(numbuf), "%d",
+ (int) attrs->value.integer);
+ vallen = strlen(numbuf);
+ available -= vallen + 1;
+ if (available < 0)
+ goto outofspace;
+ strncpy(p, numbuf, vallen);
+ p += vallen;
+ *p = '\n';
+ p += 1;
+ break;
+ case mqi_unsignd:
+ snprintf(numbuf, sizeof(numbuf), "%u", attrs->value.unsignd);
+ vallen = strlen(numbuf);
+ available -= vallen + 1;
+ if (available < 0)
+ goto outofspace;
+ strncpy(p, numbuf, vallen);
+ p += vallen;
+ *p = '\n';
+ p += 1;
+ break;
+ case mqi_floating:
+ snprintf(numbuf, sizeof(numbuf), "%f", attrs->value.floating);
+ vallen = strlen(numbuf);
+ available -= vallen + 1;
+ if (available < 0)
+ goto outofspace;
+ strncpy(p, numbuf, vallen);
+ p += vallen;
+ *p = '\n';
+ p += 1;
+ break;
+ default:
+ break;
+ }
+
+ attrs++;
+ }
+ lua_pushstring(L, buf);
+
+ return 1;
+
+outofspace:
+ return luaL_error(L, "out of string buffer space");
+}
+
+static void attribute_lua_changed(void *data, lua_State *L, int member)
+{
+ MRP_UNUSED(data);
+ MRP_UNUSED(L);
+ MRP_UNUSED(member);
+
+ mrp_debug("> attribute_changed");
+ return;
+}
+
+static int attribute_lua_getfield(lua_State *L)
+{
+ attribute_lua_t *attribute = attribute_lua_check(L, 1);
+ resource_lua_t *res = attribute->parent;
+ resource_set_lua_t *rset = res->parent;
+ mrp_attr_t attribute_list[MAX_ATTRS], *attrs;
+ const char *key;
+
+ mrp_debug("> attribute_lua_getfield");
+
+ /* attributes are indexed by string */
+
+ if (lua_type(L, 2) != LUA_TSTRING)
+ return luaL_error(L, "invalid attribute index type (needs to be string)");
+
+ key = lua_tostring(L, 2);
+
+ attrs = mrp_resource_set_read_all_attributes(rset->resource_set,
+ res->resource_name, MAX_ATTRS-1, attribute_list);
+
+ if (!attrs)
+ return luaL_error(L, "internal resource library error");
+
+ while (attrs->name != NULL) {
+
+ if (strcmp(attrs->name, key) == 0) {
+
+ switch (attrs->type) {
+ case mqi_string:
+ lua_pushstring(L, attrs->value.string);
+ return 1;
+ case mqi_integer:
+ lua_pushinteger(L, attrs->value.integer);
+ return 1;
+ case mqi_unsignd:
+ if (attrs->value.unsignd > INT_MAX) {
+ /* too big! */
+ mrp_log_error("Sorry, we don't support big unsigned values right now");
+ return luaL_error(L, "too big value in attribute");
+ }
+ else {
+ lua_pushinteger(L, attrs->value.unsignd);
+ }
+ return 1;
+ case mqi_floating:
+ lua_pushnumber(L, attrs->value.floating);
+ return 1;
+ default:
+ mrp_log_error("Unhandled attribute type");
+ return 1;
+ }
+ }
+
+ attrs++;
+ }
+
+ return luaL_error(L, "trying to get a non-existing attribute");
+}
+
+static int attribute_lua_setfield(lua_State *L)
+{
+ attribute_lua_t *attribute = attribute_lua_check(L, 1);
+ resource_lua_t *res = attribute->parent;
+ resource_set_lua_t *rset = res->parent;
+ mrp_attr_t attribute_list[MAX_ATTRS], *attrs, *orig;
+ const char *key;
+ int new_type;
+
+ mrp_debug("> attribute_lua_setfield");
+
+ /* attributes are indexed by string */
+
+ if (lua_type(L, 2) != LUA_TSTRING)
+ return luaL_error(L, "invalid attribute index type (needs to be string)");
+
+ key = lua_tostring(L, 2);
+ new_type = lua_type(L, 3);
+
+ attrs = mrp_resource_set_read_all_attributes(rset->resource_set,
+ res->resource_name, MAX_ATTRS-1, attribute_list);
+
+ if (!attrs)
+ return luaL_error(L, "internal resource library error");
+
+ orig = attrs;
+
+ while (attrs->name != NULL) {
+
+ if (strcmp(attrs->name, key) == 0) {
+
+ switch (attrs->type) {
+ case mqi_string:
+ if (new_type != LUA_TSTRING)
+ return luaL_error(L, "type mismatch");
+ attrs->value.string = lua_tostring(L, 3);
+ break;
+ case mqi_integer:
+ {
+ int32_t i;
+ double dbl;
+
+ if (new_type != LUA_TNUMBER)
+ return luaL_error(L, "type mismatch");
+
+ if ((i = lua_tointeger(L, 3)) == (dbl = lua_tonumber(L, 3)))
+ attrs->value.integer = i;
+ else
+ return luaL_error(L, "type mismatch");
+
+ break;
+ }
+ case mqi_unsignd:
+ {
+ int32_t i;
+ double dbl;
+
+ if (new_type != LUA_TNUMBER)
+ return luaL_error(L, "type mismatch");
+
+ if ((i = lua_tointeger(L, 3)) == (dbl = lua_tonumber(L, 3)) && i >= 0)
+ attrs->value.unsignd = i;
+ else
+ return luaL_error(L, "type mismatch");
+
+ break;
+ }
+ case mqi_floating:
+ {
+ if (new_type != LUA_TNUMBER)
+ return luaL_error(L, "type mismatch");
+ attrs->value.floating = lua_tonumber(L, 3);
+ break;
+ }
+ default:
+ return luaL_error(L, "unhandled attribute type");
+ }
+ break; /* while */
+ }
+
+ attrs++;
+ }
+
+ mrp_resource_set_write_attributes(rset->resource_set, res->resource_name, orig);
+
+ return 1;
+}
+
+#if 0
+MURPHY_REGISTER_LUA_BINDINGS(murphy, RESOURCE_LUA_CLASS,
+ { "Resource", resource_lua_create });
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, RESOURCE_SET_LUA_CLASS,
+ { "ResourceSet", resource_set_lua_create });
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, ATTRIBUTE_LUA_CLASS,
+ { "Attribute", attribute_lua_create });
+#else
+
+static void register_murphy_bindings(void) MRP_INIT;
+
+static void register_murphy_bindings(void) {
+ static struct luaL_Reg resource_methods[] = {
+ { "Resource", resource_lua_create },
+ { NULL, NULL }
+ };
+
+ static mrp_lua_bindings_t resource_bindings = {
+ .meta = "murphy",
+ .methods = resource_methods,
+ .classdef = &resource_lua_class_def,
+ };
+
+ static struct luaL_Reg resource_set_methods[] = {
+ { "ResourceSet", resource_set_lua_create },
+ { NULL, NULL }
+ };
+
+ static mrp_lua_bindings_t resource_set_bindings = {
+ .meta = "murphy",
+ .methods = resource_set_methods,
+ .classdef = &resource_set_lua_class_def,
+ };
+
+ static struct luaL_Reg attribute_methods[] = {
+ { "Attribute", attribute_lua_create },
+ { NULL, NULL }
+ };
+
+ static mrp_lua_bindings_t attribute_bindings = {
+ .meta = "murphy",
+ .methods = attribute_methods,
+ .classdef = &attribute_lua_class_def,
+ };
+
+ mrp_list_init(&attribute_bindings.hook);
+ mrp_lua_register_murphy_bindings(&attribute_bindings);
+
+ mrp_list_init(&resource_bindings.hook);
+ mrp_lua_register_murphy_bindings(&resource_bindings);
+
+ mrp_list_init(&resource_set_bindings.hook);
+ mrp_lua_register_murphy_bindings(&resource_set_bindings);
+}
+#endif
+
+
+/*
+
+resourcehandler = function (rset)
+ if rset.resources.screen.acquired == true then
+ print("got it")
+ else
+ print("didn't get it")
+ end
+end
+
+resourcehandler = function (rset) print("bar") end
+
+rset = m:ResourceSet ( { zone = "driver", callback = resourceHandler, application_class = "player"Â } )
+
+rset:addResource({ resource_name = "audio_playback", mandatory = true })
+
+rset.resources.audio_playback.attributes.pid = "500"
+
+rset:acquire()
+
+rset:release()
+
+*/
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_MANAGER_API_H__
+#define __MURPHY_RESOURCE_MANAGER_API_H__
+
+#include <murphy/resource/common-api.h>
+
+uint32_t mrp_zone_get_id(mrp_zone_t *zone);
+const char *mrp_zone_get_name(mrp_zone_t *zone);
+mrp_attr_t *mrp_zone_read_attribute(mrp_zone_t *zone,
+ uint32_t attribute_index,
+ mrp_attr_t *buf);
+mrp_attr_t *mrp_zone_read_all_attributes(mrp_zone_t *zone,
+ uint32_t buflen,
+ mrp_attr_t *buf);
+
+
+const char *mrp_application_class_get_name(mrp_application_class_t *class);
+uint32_t mrp_application_class_get_priority(mrp_application_class_t *class);
+
+
+uint32_t mrp_resource_definition_create(const char *name,
+ bool shareable,
+ mrp_attr_def_t *attrdefs,
+ mrp_resource_mgr_ftbl_t *manager,
+ void *manager_data);
+void mrp_lua_resclass_create_from_c(uint32_t id);
+
+
+const char *mrp_resource_get_application_class(mrp_resource_t *resource);
+
+void mrp_resource_owner_recalc(uint32_t zoneid);
+
+#endif /* __MURPHY_RESOURCE_MANAGER_API_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_PROTOCOL_H__
+#define __MURPHY_RESOURCE_PROTOCOL_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <murphy/common/msg.h>
+
+#define RESPROTO_DEFAULT_ADDRESS "unxs:@murphy-resource-native"
+#define RESPROTO_DEFAULT_ADDRVAR "MURPHY_RESOURCE_ADDRESS"
+
+
+#define RESPROTO_BIT(n) ((uint32_t)1 << (n))
+
+#define RESPROTO_RSETFLAG_AUTORELEASE RESPROTO_BIT(0)
+#define RESPROTO_RSETFLAG_AUTOACQUIRE RESPROTO_BIT(1)
+#define RESPROTO_RSETFLAG_NOEVENTS RESPROTO_BIT(2)
+#define RESPROTO_RSETFLAG_DONTWAIT RESPROTO_BIT(3)
+
+#define RESPROTO_RESFLAG_MANDATORY RESPROTO_BIT(0)
+#define RESPROTO_RESFLAG_SHARED RESPROTO_BIT(1)
+
+#define RESPROTO_TAG(x) ((uint16_t)(x))
+
+#define RESPROTO_MESSAGE_END MRP_MSG_FIELD_END
+#define RESPROTO_SECTION_END RESPROTO_TAG(1)
+#define RESPROTO_ARRAY_DIMENSION RESPROTO_TAG(2)
+#define RESPROTO_SEQUENCE_NO RESPROTO_TAG(3)
+#define RESPROTO_REQUEST_TYPE RESPROTO_TAG(4)
+#define RESPROTO_REQUEST_STATUS RESPROTO_TAG(5)
+#define RESPROTO_RESOURCE_SET_ID RESPROTO_TAG(6)
+#define RESPROTO_RESOURCE_STATE RESPROTO_TAG(7)
+#define RESPROTO_RESOURCE_GRANT RESPROTO_TAG(8)
+#define RESPROTO_RESOURCE_ADVICE RESPROTO_TAG(9)
+#define RESPROTO_RESOURCE_ID RESPROTO_TAG(10)
+#define RESPROTO_RESOURCE_NAME RESPROTO_TAG(11)
+#define RESPROTO_RESOURCE_FLAGS RESPROTO_TAG(12)
+#define RESPROTO_RESOURCE_PRIORITY RESPROTO_TAG(13)
+#define RESPROTO_CLASS_NAME RESPROTO_TAG(14)
+#define RESPROTO_ZONE_NAME RESPROTO_TAG(15)
+#define RESPROTO_ATTRIBUTE_INDEX RESPROTO_TAG(16)
+#define RESPROTO_ATTRIBUTE_NAME RESPROTO_TAG(17)
+#define RESPROTO_ATTRIBUTE_VALUE RESPROTO_TAG(18)
+
+typedef enum {
+ RESPROTO_QUERY_RESOURCES,
+ RESPROTO_QUERY_CLASSES,
+ RESPROTO_QUERY_ZONES,
+ RESPROTO_CREATE_RESOURCE_SET,
+ RESPROTO_DESTROY_RESOURCE_SET,
+ RESPROTO_ACQUIRE_RESOURCE_SET,
+ RESPROTO_RELEASE_RESOURCE_SET,
+ RESPROTO_RESOURCES_EVENT,
+} mrp_resproto_request_t;
+
+typedef enum {
+ RESPROTO_RELEASE,
+ RESPROTO_ACQUIRE,
+} mrp_resproto_state_t;
+
+
+static inline const char *mrp_resource_get_default_address(void)
+{
+ const char *addr;
+
+ if ((addr = getenv(RESPROTO_DEFAULT_ADDRVAR)) == NULL)
+ return RESPROTO_DEFAULT_ADDRESS;
+ else
+ return addr;
+}
+
+#endif /* __MURPHY_RESOURCE_PROTOCOL_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include <resource/client-api.h>
+
+#include "resource-client.h"
+#include "resource-set.h"
+
+
+
+static MRP_LIST_HOOK(client_list);
+
+
+mrp_resource_client_t *mrp_resource_client_create(const char *name,
+ void *user_data)
+{
+ mrp_resource_client_t *client;
+ const char *dup_name;
+
+ MRP_ASSERT(name, "invalid argument");
+
+ if (!(client = mrp_allocz(sizeof(*client))) ||
+ !(dup_name = mrp_strdup(name)))
+ {
+ mrp_log_error("Memory alloc failure. Can't create client '%s'", name);
+ return NULL;
+ }
+
+ client->name = dup_name;
+ client->user_data = user_data;
+ mrp_list_init(&client->resource_sets);
+
+ mrp_list_append(&client_list, &client->list);
+
+ return client;
+}
+
+void mrp_resource_client_destroy(mrp_resource_client_t *client)
+{
+ mrp_list_hook_t *entry, *n;
+ mrp_resource_set_t *rset;
+
+ if (client) {
+ mrp_list_delete(&client->list);
+
+ mrp_list_foreach(&client->resource_sets, entry, n) {
+ rset = mrp_list_entry(entry, mrp_resource_set_t, client.list);
+ mrp_resource_set_destroy(rset);
+ }
+
+ mrp_free((void *) client->name);
+ mrp_free(client);
+ }
+}
+
+
+mrp_resource_set_t *mrp_resource_client_find_set(mrp_resource_client_t *client,
+ uint32_t resource_set_id)
+{
+ mrp_list_hook_t *entry, *n;
+ mrp_resource_set_t *rset;
+
+ if (client) {
+ mrp_list_foreach(&client->resource_sets, entry, n) {
+ rset = mrp_list_entry(entry, mrp_resource_set_t, client.list);
+
+ if (resource_set_id == rset->id)
+ return rset;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_CLIENT_H__
+#define __MURPHY_RESOURCE_CLIENT_H__
+
+#include <murphy/common/list.h>
+
+#include "data-types.h"
+
+
+struct mrp_resource_client_s {
+ mrp_list_hook_t list;
+ const char *name;
+ void *user_data;
+ mrp_list_hook_t resource_sets;
+};
+
+
+
+#endif /* __MURPHY_RESOURCE_CLIENT_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <lualib.h>
+#include <lauxlib.h>
+
+#include <murphy/common.h>
+#include <murphy/common/debug.h>
+#include <murphy/core/lua-bindings/murphy.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-utils/object.h>
+
+#include "resource-lua.h"
+#include "config-lua.h"
+#include "zone.h"
+#include "resource.h"
+#include "resource-set.h"
+#include "resource-owner.h"
+#include "application-class.h"
+#include "client-api.h"
+
+#define OWNERS_CLASS MRP_LUA_CLASS(resource, owners)
+#define SETREF_CLASS MRP_LUA_CLASS(resource, sets)
+
+#define OWNERREF_CLASSID MRP_LUA_CLASSID_ROOT "resource.ownerref"
+#define OWNERREF_USERDATA MRP_LUA_CLASSID_ROOT "resource.ownerref.userdata"
+
+typedef enum field_e field_t;
+typedef struct ownerref_s ownerref_t;
+
+enum field_e {
+ APPLICATION_CLASS = 1,
+ AUTO_RELEASE,
+ RESOURCE_SET,
+ ATTRIBUTES,
+ DONT_WAIT,
+ RESOURCE,
+ STATE,
+ ID
+};
+
+struct ownerref_s {
+ uint32_t zoneid;
+ uint32_t resid;
+};
+
+static void owners_class_create(lua_State *);
+static void ownerref_class_create(lua_State *);
+static void setref_class_create(lua_State *);
+
+static mrp_resource_ownersref_t *owners_get(lua_State *, uint32_t);
+static int owners_create(lua_State *);
+static int owners_getfield(lua_State *);
+static int owners_setfield(lua_State *);
+static void owners_destroy(void *);
+static mrp_resource_ownersref_t *owners_check(lua_State *, int);
+/* static mrp_resource_owners_s *to_owners(lua_State *, int); */
+
+static ownerref_t *ownerref_create(lua_State *, uint32_t, uint32_t);
+static int ownerref_getfield(lua_State *);
+static int ownerref_setfield(lua_State *);
+static mrp_resource_owner_t *ownerref_check(lua_State *, int);
+
+static int setref_getfield(lua_State *);
+static int setref_setfield(lua_State *);
+static void setref_destroy(void *);
+static mrp_resource_setref_t *setref_check(lua_State *, int);
+
+static void init_id_hash(void);
+static int add_to_id_hash(mrp_resource_setref_t *);
+static mrp_resource_setref_t *remove_from_id_hash(uint32_t);
+static mrp_resource_setref_t *find_in_id_hash(uint32_t);
+
+static field_t field_check(lua_State *, int, const char **);
+static field_t field_name_to_type(const char *, size_t);
+
+
+MRP_LUA_METHOD_LIST_TABLE (
+ owners_methods, /* methodlist name */
+ MRP_LUA_METHOD_CONSTRUCTOR (owners_create)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ setref_methods /* methodlist name */
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ owners_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_CALL (owners_create)
+ MRP_LUA_OVERRIDE_GETFIELD (owners_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (owners_setfield)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ ownerref_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (ownerref_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (ownerref_setfield)
+);
+
+MRP_LUA_METHOD_LIST_TABLE (
+ setref_overrides, /* methodlist name */
+ MRP_LUA_OVERRIDE_GETFIELD (setref_getfield)
+ MRP_LUA_OVERRIDE_SETFIELD (setref_setfield)
+);
+
+MRP_LUA_CLASS_DEF (
+ resource, /* main class name */
+ owners, /* constructor name */
+ mrp_resource_ownersref_t, /* userdata type */
+ owners_destroy, /* userdata destructor */
+ owners_methods, /* class methods */
+ owners_overrides /* class overrides */
+);
+
+MRP_LUA_CLASS_DEF (
+ resource, /* main class name */
+ sets, /* constructor name */
+ mrp_resource_setref_t, /* userdata type */
+ setref_destroy, /* userdata destructor */
+ setref_methods, /* class methods */
+ setref_overrides /* class overrides */
+);
+
+static mrp_resource_ownersref_t *resource_owners[MRP_ZONE_MAX];
+static mrp_htbl_t *id_hash;
+
+void mrp_resource_lua_init(lua_State *L)
+{
+ static bool initialised = false;
+
+ if (!initialised) {
+ owners_class_create(L);
+ ownerref_class_create(L);
+ setref_class_create(L);
+
+ init_id_hash();
+ }
+}
+
+bool mrp_resource_lua_veto(mrp_zone_t *zone,
+ mrp_resource_set_t *rset,
+ mrp_resource_owner_t *owners,
+ mrp_resource_mask_t grant,
+ mrp_resource_set_t *reqset)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_lua_resmethod_t *methods = mrp_lua_get_resource_methods();
+ mrp_funcarray_t *veto;
+ mrp_resource_setref_t *sref, *rref;
+ mrp_resource_ownersref_t *oref;
+ mrp_funcbridge_value_t args[16];
+ int i, top;
+ bool success;
+
+ if (L == NULL)
+ return true;
+
+ success = true;
+ top = lua_gettop(L);
+
+ if (zone && rset && owners && methods &&
+ (sref = find_in_id_hash(rset->id)) &&
+ (oref = owners_get(L, zone->id)))
+ {
+ rref = reqset ? find_in_id_hash(reqset->id) : NULL;
+ oref->owners = owners;
+
+ if ((veto = methods->veto)) {
+ args[i=0].string = zone->name;
+ args[++i].pointer = sref;
+ args[++i].integer = grant;
+ args[++i].pointer = oref;
+ args[++i].pointer = rref;
+
+ success = mrp_funcarray_call_from_c(L, veto, "sodoo", args);
+
+ goto out;
+ }
+ }
+
+ out:
+ lua_settop(L, top);
+
+ return success;
+}
+
+void mrp_resource_lua_set_owners(mrp_zone_t *zone,mrp_resource_owner_t *owners)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_resource_ownersref_t *ref;
+
+ if (L && zone && owners && (ref = owners_get(L, zone->id)))
+ ref->owners = owners;
+}
+
+void mrp_resource_lua_register_resource_set(mrp_resource_set_t *rset)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_resource_setref_t *ref;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ if ((ref = mrp_lua_create_object(L, SETREF_CLASS, NULL,rset->id))) {
+ ref->rset = rset;
+ add_to_id_hash(ref);
+ }
+}
+
+void mrp_resource_lua_unregister_resource_set(mrp_resource_set_t *rset)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_resource_setref_t *ref;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ if ((ref = remove_from_id_hash(rset->id))) {
+ MRP_ASSERT(rset == ref->rset, "confused with data structures");
+ mrp_lua_destroy_object(L, NULL,rset->id, ref);
+ ref->rset = NULL;
+ }
+}
+
+void mrp_resource_lua_add_resource_to_resource_set(mrp_resource_set_t *rset,
+ mrp_resource_t *res)
+{
+ lua_State *L = mrp_lua_get_lua_state();
+ mrp_resource_setref_t *ref;
+ mrp_resource_def_t *def;
+
+ MRP_ASSERT(rset && res, "invalid argument");
+
+ ref = find_in_id_hash(rset->id);
+ def = res->def;
+
+ if (ref && def) {
+ MRP_ASSERT(rset == ref->rset, "confused with data structures");
+
+ mrp_lua_push_object(L, ref);
+
+ lua_pushstring(L, def->name);
+ mrp_lua_resource_create(L, res);
+
+ lua_rawset(L, -3);
+ }
+}
+
+static void owners_class_create(lua_State *L)
+{
+ mrp_lua_create_object_class(L, OWNERS_CLASS);
+}
+
+static void ownerref_class_create(lua_State *L)
+{
+ luaL_newmetatable(L, OWNERREF_USERDATA);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, OWNERREF_CLASSID);
+ lua_pushliteral(L, "__index");
+ lua_pushvalue(L, -2);
+ lua_settable(L, -3); /* metatable.__index = metatable */
+ luaL_openlib(L, NULL, ownerref_overrides, 0);
+ lua_pop(L, 1);
+}
+
+static void setref_class_create(lua_State *L)
+{
+ mrp_lua_create_object_class(L, SETREF_CLASS);
+}
+
+static mrp_resource_ownersref_t *owners_get(lua_State *L, uint32_t zoneid)
+{
+ mrp_resource_ownersref_t *owner = NULL;
+ mrp_zone_t *zone;
+
+ if (zoneid < MRP_ZONE_MAX) {
+ if (!(owner = resource_owners[zoneid])) {
+ if ((zone = mrp_zone_find_by_id(zoneid))) {
+ owner = mrp_lua_create_object(L, OWNERS_CLASS, zone->name,0);
+ owner->zoneid = zoneid;
+ resource_owners[zoneid] = owner;
+ }
+ }
+ }
+
+ return owner;
+}
+
+static int owners_create(lua_State *L)
+{
+ luaL_error(L, "can't create resource owner from LUA");
+ return 0;
+}
+
+static int owners_getfield(lua_State *L)
+{
+ mrp_resource_ownersref_t *ref = owners_check(L, 1);
+ uint32_t resid;
+
+ MRP_LUA_ENTER;
+
+ switch (lua_type(L, 2)) {
+
+ case LUA_TSTRING:
+ if (mrp_lua_findtable(L, MRP_LUA_GLOBALTABLE, "resource.class", 0)) {
+ lua_pushnil(L);
+ break;
+ }
+ lua_pushvalue(L, 2);
+ lua_gettable(L, -2);
+ if (lua_isnil(L, -1))
+ break;
+ resid = mrp_lua_to_resource_id(L, -1);
+ goto create_reference;
+
+ case LUA_TNUMBER:
+ resid = lua_tointeger(L, 2) - 1;
+
+ create_reference:
+ if (resid >= MRP_RESOURCE_MAX || !ref->owners[resid].class)
+ lua_pushnil(L);
+ else
+ ownerref_create(L, ref->zoneid, resid);
+ break;
+
+ default:
+ lua_pushnil(L);
+ break;
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int owners_setfield(lua_State *L)
+{
+ MRP_LUA_ENTER;
+
+ luaL_error(L, "attempt to write read-only resource owners");
+
+ MRP_LUA_LEAVE(0);
+}
+
+static void owners_destroy(void *data)
+{
+ mrp_resource_ownersref_t *owners = (mrp_resource_ownersref_t *)data;
+
+ MRP_LUA_ENTER;
+
+ if (owners->zoneid < MRP_ZONE_MAX)
+ resource_owners[owners->zoneid] = NULL;
+
+ memset(owners, 0, sizeof(mrp_resource_ownersref_t));
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static mrp_resource_ownersref_t *owners_check(lua_State *L, int t)
+{
+ return (mrp_resource_ownersref_t*)mrp_lua_check_object(L, OWNERS_CLASS, t);
+}
+
+
+static ownerref_t *ownerref_create(lua_State *L,uint32_t zoneid,uint32_t resid)
+{
+ int table;
+ ownerref_t *or;
+
+ lua_createtable(L, 0, 0);
+ table = lua_gettop(L);
+
+ luaL_getmetatable(L, OWNERREF_CLASSID);
+ lua_setmetatable(L, table);
+
+ lua_pushliteral(L, "userdata");
+
+ or = (ownerref_t *)lua_newuserdata(L, sizeof(ownerref_t));
+ memset(or, 0, sizeof(ownerref_t));
+
+ luaL_getmetatable(L, OWNERREF_USERDATA);
+ lua_setmetatable(L, -2);
+
+ lua_rawset(L, table);
+
+ or->zoneid = zoneid;
+ or->resid = resid;
+
+ return or;
+}
+
+static int ownerref_getfield(lua_State *L)
+{
+ mrp_resource_owner_t *owner = ownerref_check(L, 1);
+ const char *name;
+ field_t field;
+
+ MRP_LUA_ENTER;
+
+ if (!owner || lua_type(L, 2) != LUA_TSTRING)
+ lua_pushnil(L);
+ else {
+ field = field_check(L, 2, &name);
+
+ switch (field) {
+ case APPLICATION_CLASS: lua_pushstring(L, owner->class->name); break;
+ case RESOURCE_SET: lua_pushinteger(L, owner->rset->id); break;
+ case RESOURCE: lua_pushnil(L); break;
+ default: lua_pushnil(L); break;
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int ownerref_setfield(lua_State *L)
+{
+ /* mrp_resource_owner_t *owner = ownerref_check(L, 1); */
+
+ MRP_UNUSED(L);
+
+ MRP_LUA_ENTER;
+
+ printf("*** ownerref setfield\n");
+
+ MRP_LUA_LEAVE(1);
+}
+
+static mrp_resource_owner_t *ownerref_check(lua_State *L, int t)
+{
+ ownerref_t *or;
+ mrp_resource_ownersref_t *ro;
+ mrp_resource_owner_t *owner = NULL;
+
+ t = (t < 0) ? lua_gettop(L) + t + 1 : t;
+
+ luaL_checktype(L, t, LUA_TTABLE);
+
+ lua_pushliteral(L, "userdata");
+ lua_rawget(L, t);
+
+ or = luaL_checkudata(L, -1, OWNERREF_USERDATA);
+ luaL_argcheck(L, or != NULL, t, "'resource owner' expected");
+
+ lua_pop(L, 1);
+
+ if ((ro = resource_owners[or->zoneid]))
+ owner = ro->owners + or->resid;
+
+ return owner;
+}
+
+static int setref_getfield(lua_State *L)
+{
+ mrp_resource_setref_t *ref = setref_check(L, 1);
+ mrp_resource_set_t *rset;
+ field_t field;
+ const char *state;
+
+ MRP_LUA_ENTER;
+
+ if (!ref || !(rset = ref->rset))
+ lua_pushnil(L);
+ else {
+ field = field_check(L, 2, NULL);
+
+ switch (field) {
+
+ case ID:
+ lua_pushinteger(L, rset->id);
+ break;
+
+ case STATE:
+ switch (rset->state) {
+ case mrp_resource_no_request: state = "no_request"; break;
+ case mrp_resource_release: state = "release"; break;
+ case mrp_resource_acquire: state = "acquire"; break;
+ default: state = "<invalid>"; break;
+ }
+ lua_pushstring(L, state);
+ break;
+
+ case DONT_WAIT:
+ lua_pushboolean(L, rset->dont_wait.current);
+ break;
+
+ case AUTO_RELEASE:
+ lua_pushboolean(L, rset->auto_release.current);
+ break;
+
+ case APPLICATION_CLASS:
+ lua_pushstring(L, rset->class.ptr->name);
+ break;
+
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+
+ MRP_LUA_LEAVE(1);
+}
+
+static int setref_setfield(lua_State *L)
+{
+ mrp_resource_setref_t *ref = setref_check(L, 1);
+ mrp_resource_set_t *rset;
+ field_t field;
+
+ MRP_LUA_ENTER;
+
+ if (ref && (rset = ref->rset)) {
+ field = field_check(L, 2, NULL);
+
+ switch (field) {
+
+ case DONT_WAIT:
+ rset->dont_wait.current = lua_toboolean(L, 3);
+ break;
+
+ case AUTO_RELEASE:
+ rset->auto_release.current = lua_toboolean(L, 3);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ MRP_LUA_LEAVE(0);
+}
+
+static void setref_destroy(void *data)
+{
+ mrp_resource_setref_t *ref = (mrp_resource_setref_t *)data;
+ mrp_resource_set_t *rset;
+
+ MRP_LUA_ENTER;
+
+ if (ref && (rset = ref->rset))
+ remove_from_id_hash(rset->id);
+
+ MRP_LUA_LEAVE_NOARG;
+}
+
+static mrp_resource_setref_t *setref_check(lua_State *L, int idx)
+{
+ return (mrp_resource_setref_t *)mrp_lua_check_object(L, SETREF_CLASS, idx);
+}
+
+
+static uint32_t ref_hash(const void *key)
+{
+ return (uint32_t)(key - NULL);
+}
+
+static int ref_comp(const void *key1, const void *key2)
+{
+ uint32_t k1 = key1 - NULL;
+ uint32_t k2 = key2 - NULL;
+
+ return (k1 == k2) ? 0 : ((k1 > k2) ? 1 : -1);
+}
+
+static void init_id_hash(void)
+{
+ mrp_htbl_config_t cfg;
+
+ cfg.nentry = 32;
+ cfg.comp = ref_comp;
+ cfg.hash = ref_hash;
+ cfg.free = NULL;
+ cfg.nbucket = cfg.nentry;
+
+ id_hash = mrp_htbl_create(&cfg);
+
+ MRP_ASSERT(id_hash, "failed to make id_hash for resource set refs");
+}
+
+static int add_to_id_hash(mrp_resource_setref_t *ref)
+{
+ mrp_resource_set_t *rset;
+
+ MRP_ASSERT(ref, "invalid argument");
+
+ rset = ref->rset;
+
+ MRP_ASSERT(rset, "confused with data structures");
+
+ if (!mrp_htbl_insert(id_hash, NULL + rset->id, ref))
+ return -1;
+
+ return 0;
+}
+
+static mrp_resource_setref_t *remove_from_id_hash(uint32_t id)
+{
+ return id_hash ? mrp_htbl_remove(id_hash, NULL + id, false) : NULL;
+}
+
+static mrp_resource_setref_t *find_in_id_hash(uint32_t id)
+{
+ return id_hash ? mrp_htbl_lookup(id_hash, NULL + id) : NULL;
+}
+
+
+static field_t field_check(lua_State *L, int idx, const char **ret_fldnam)
+{
+ const char *fldnam;
+ size_t fldnamlen;
+ field_t fldtyp;
+
+ if (!(fldnam = lua_tolstring(L, idx, &fldnamlen)))
+ fldtyp = 0;
+ else
+ fldtyp = field_name_to_type(fldnam, fldnamlen);
+
+ if (ret_fldnam)
+ *ret_fldnam = fldnam;
+
+ return fldtyp;
+}
+
+
+static field_t field_name_to_type(const char *name, size_t len)
+{
+ switch (len) {
+
+ case 2:
+ if (!strcmp(name, "id"))
+ return ID;
+ break;
+
+ case 5:
+ if (!strcmp(name, "state"))
+ return STATE;
+ break;
+
+ case 8:
+ if (!strcmp(name, "resource"))
+ return RESOURCE;
+ break;
+
+ case 9:
+ if (!strcmp(name, "dont_wait"))
+ return DONT_WAIT;
+ break;
+
+ case 10:
+ if (!strcmp(name, "attributes"))
+ return ATTRIBUTES;
+ break;
+
+ case 12:
+ if (!strcmp(name, "auto_release"))
+ return AUTO_RELEASE;
+ if (!strcmp(name, "resource_set"))
+ return RESOURCE_SET;
+ break;
+
+ case 17:
+ if (!strcmp(name, "application_class"))
+ return APPLICATION_CLASS;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_LUA_H__
+#define __MURPHY_RESOURCE_LUA_H__
+
+#include <lua.h>
+
+#include <murphy/common/hashtbl.h>
+
+#include "data-types.h"
+
+struct mrp_resource_ownersref_s {
+ uint32_t zoneid;
+ mrp_resource_owner_t *owners;
+};
+
+struct mrp_resource_setref_s {
+ mrp_resource_set_t *rset;
+};
+
+
+void mrp_resource_lua_init(lua_State *);
+
+bool mrp_resource_lua_veto(mrp_zone_t *, mrp_resource_set_t *,
+ mrp_resource_owner_t *, mrp_resource_mask_t,
+ mrp_resource_set_t *);
+void mrp_resource_lua_set_owners(mrp_zone_t *, mrp_resource_owner_t *);
+
+void mrp_resource_lua_register_resource_set(mrp_resource_set_t *);
+void mrp_resource_lua_unregister_resource_set(mrp_resource_set_t *);
+void mrp_resource_lua_add_resource_to_resource_set(mrp_resource_set_t *,
+ mrp_resource_t *);
+
+#endif /* __MURPHY_RESOURCE_LUA_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/log.h>
+
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/config-api.h>
+
+#include "resource-owner.h"
+#include "application-class.h"
+#include "resource-set.h"
+#include "resource.h"
+#include "zone.h"
+#include "resource-lua.h"
+
+#define NAME_LENGTH 24
+
+#define ZONE_ID_IDX 0
+#define ZONE_NAME_IDX 1
+#define CLASS_NAME_IDX 2
+#define RSET_ID_IDX 3
+#define FIRST_ATTRIBUTE_IDX 4
+
+typedef struct {
+ uint32_t zone_id;
+ const char *zone_name;
+ const char *class_name;
+ uint32_t rset_id;
+ mrp_attr_value_t attrs[MQI_COLUMN_MAX];
+} owner_row_t;
+
+static mrp_resource_owner_t resource_owners[MRP_ZONE_MAX * MRP_RESOURCE_MAX];
+static mqi_handle_t owner_tables[MRP_RESOURCE_MAX];
+
+static mrp_resource_owner_t *get_owner(uint32_t, uint32_t);
+static void reset_owners(uint32_t, mrp_resource_owner_t *);
+static bool grant_ownership(mrp_resource_owner_t *, mrp_zone_t *,
+ mrp_application_class_t *, mrp_resource_set_t *,
+ mrp_resource_t *);
+static bool advice_ownership(mrp_resource_owner_t *, mrp_zone_t *,
+ mrp_application_class_t *, mrp_resource_set_t *,
+ mrp_resource_t *);
+
+static void manager_start_transaction(mrp_zone_t *);
+static void manager_end_transaction(mrp_zone_t *);
+
+static void delete_resource_owner(mrp_zone_t *, mrp_resource_t *);
+static void insert_resource_owner(mrp_zone_t *, mrp_application_class_t *,
+ mrp_resource_set_t *, mrp_resource_t *);
+static void update_resource_owner(mrp_zone_t *, mrp_application_class_t *,
+ mrp_resource_set_t *, mrp_resource_t *);
+static void set_attr_descriptors(mqi_column_desc_t *, mrp_resource_t *);
+
+
+int mrp_resource_owner_create_database_table(mrp_resource_def_t *rdef)
+{
+ MQI_COLUMN_DEFINITION_LIST(base_coldefs,
+ MQI_COLUMN_DEFINITION( "zone_id" , MQI_UNSIGNED ),
+ MQI_COLUMN_DEFINITION( "zone_name" , MQI_VARCHAR(NAME_LENGTH) ),
+ MQI_COLUMN_DEFINITION( "application_class", MQI_VARCHAR(NAME_LENGTH) ),
+ MQI_COLUMN_DEFINITION( "resource_set_id" , MQI_UNSIGNED )
+ );
+
+ MQI_INDEX_DEFINITION(indexdef,
+ MQI_INDEX_COLUMN( "zone_id" )
+ );
+
+ static bool initialized = false;
+
+ char name[256];
+ mqi_column_def_t coldefs[MQI_COLUMN_MAX + 1];
+ mqi_column_def_t *col;
+ mrp_attr_def_t *atd;
+ mqi_handle_t table;
+ char c, *p;
+ size_t i,j;
+
+ if (!initialized) {
+ mqi_open();
+ for (i = 0; i < MRP_RESOURCE_MAX; i++)
+ owner_tables[i] = MQI_HANDLE_INVALID;
+ initialized = true;
+ }
+
+ MRP_ASSERT(sizeof(base_coldefs) < sizeof(coldefs),"too many base columns");
+ MRP_ASSERT(rdef, "invalid argument");
+ MRP_ASSERT(rdef->id < MRP_RESOURCE_MAX, "confused with data structures");
+ MRP_ASSERT(owner_tables[rdef->id] == MQI_HANDLE_INVALID,
+ "owner table already exist");
+
+ snprintf(name, sizeof(name), "%s_owner", rdef->name);
+ for (p = name; (c = *p); p++) {
+ if (!isascii(c) || (!isalnum(c) && c != '_'))
+ *p = '_';
+ }
+
+ j = MQI_DIMENSION(base_coldefs) - 1;
+ memcpy(coldefs, base_coldefs, j * sizeof(mqi_column_def_t));
+
+ for (i = 0; i < rdef->nattr && j < MQI_COLUMN_MAX; i++, j++) {
+ col = coldefs + j;
+ atd = rdef->attrdefs + i;
+
+ col->name = atd->name;
+ col->type = atd->type;
+ col->length = (col->type == mqi_string) ? NAME_LENGTH : 0;
+ col->flags = 0;
+ }
+
+ memset(coldefs + j, 0, sizeof(mqi_column_def_t));
+
+ table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
+
+ if (table == MQI_HANDLE_INVALID) {
+ mrp_log_error("Can't create table '%s': %s", name, strerror(errno));
+ return -1;
+ }
+
+ owner_tables[rdef->id] = table;
+
+ return 0;
+}
+
+void mrp_resource_owner_recalc(uint32_t zoneid)
+{
+ mrp_resource_owner_update_zone(zoneid, NULL, 0);
+}
+
+void mrp_resource_owner_update_zone(uint32_t zoneid,
+ mrp_resource_set_t *reqset,
+ uint32_t reqid)
+{
+ typedef struct {
+ uint32_t replyid;
+ mrp_resource_set_t *rset;
+ bool move;
+ } event_t;
+
+ mrp_resource_owner_t oldowners[MRP_RESOURCE_MAX];
+ mrp_resource_owner_t backup[MRP_RESOURCE_MAX];
+ mrp_zone_t *zone;
+ mrp_application_class_t *class;
+ mrp_resource_set_t *rset;
+ mrp_resource_t *res;
+ mrp_resource_def_t *rdef;
+ mrp_resource_mgr_ftbl_t *ftbl;
+ mrp_resource_owner_t *owner, *old, *owners;
+ mrp_resource_mask_t mask;
+ mrp_resource_mask_t mandatory;
+ mrp_resource_mask_t grant;
+ mrp_resource_mask_t advice;
+ void *clc, *rsc, *rc;
+ uint32_t rid;
+ uint32_t rcnt;
+ bool force_release;
+ bool changed;
+ bool move;
+ mrp_resource_event_t notify;
+ uint32_t replyid;
+ uint32_t nevent, maxev;
+ event_t *events, *ev, *lastev;
+
+ MRP_ASSERT(zoneid < MRP_ZONE_MAX, "invalid argument");
+
+ zone = mrp_zone_find_by_id(zoneid);
+
+ MRP_ASSERT(zone, "zone is not defined");
+
+ if (!(maxev = mrp_get_resource_set_count()))
+ return;
+
+ nevent = 0;
+ events = mrp_alloc(sizeof(event_t) * maxev);
+
+ MRP_ASSERT(events, "Memory alloc failure. Can't update zone");
+
+ reset_owners(zoneid, oldowners);
+ manager_start_transaction(zone);
+
+ rcnt = mrp_resource_definition_count();
+ clc = NULL;
+
+ while ((class = mrp_application_class_iterate_classes(&clc))) {
+ rsc = NULL;
+
+ while ((rset=mrp_application_class_iterate_rsets(class,zoneid,&rsc))) {
+ force_release = false;
+ mandatory = rset->resource.mask.mandatory;
+ grant = 0;
+ advice = 0;
+ rc = NULL;
+
+ switch (rset->state) {
+
+ case mrp_resource_acquire:
+ while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
+ rdef = res->def;
+ rid = rdef->id;
+ owner = get_owner(zoneid, rid);
+
+ backup[rid] = *owner;
+
+ if (grant_ownership(owner, zone, class, rset, res))
+ grant |= ((mrp_resource_mask_t)1 << rid);
+ else {
+ if (owner->rset != rset)
+ force_release |= owner->modal;
+ }
+ }
+ owners = get_owner(zoneid, 0);
+ if ((grant & mandatory) == mandatory &&
+ mrp_resource_lua_veto(zone, rset, owners, grant, reqset))
+ {
+ advice = grant;
+ }
+ else {
+ /* rollback, ie. restore the backed up state */
+ rc = NULL;
+ while ((res=mrp_resource_set_iterate_resources(rset,&rc))){
+ rdef = res->def;
+ rid = rdef->id;
+ mask = (mrp_resource_mask_t)1 << rid;
+ owner = get_owner(zoneid, rid);
+ *owner = backup[rid];
+
+ if ((grant & mask)) {
+ if ((ftbl = rdef->manager.ftbl) && ftbl->free)
+ ftbl->free(zone, res, rdef->manager.userdata);
+ }
+
+ if (advice_ownership(owner, zone, class, rset, res))
+ advice |= mask;
+ }
+
+ grant = 0;
+
+ if ((advice & mandatory) != mandatory)
+ advice = 0;
+
+ mrp_resource_lua_set_owners(zone, owners);
+ }
+ break;
+
+ case mrp_resource_release:
+ while ((res = mrp_resource_set_iterate_resources(rset, &rc))) {
+ rdef = res->def;
+ rid = rdef->id;
+ owner = get_owner(zoneid, rid);
+
+ if (advice_ownership(owner, zone, class, rset, res))
+ advice |= ((mrp_resource_mask_t)1 << rid);
+ }
+ if ((advice & mandatory) != mandatory)
+ advice = 0;
+ break;
+
+ default:
+ break;
+ }
+
+ changed = false;
+ move = false;
+ notify = 0;
+ replyid = (reqset == rset && reqid == rset->request.id) ? reqid:0;
+
+
+ if (force_release) {
+ move = (rset->state != mrp_resource_release);
+ notify = move ? MRP_RESOURCE_EVENT_RELEASE : 0;
+ changed = move || rset->resource.mask.grant;
+ rset->state = mrp_resource_release;
+ rset->resource.mask.grant = 0;
+ }
+ else {
+ if (grant == rset->resource.mask.grant) {
+ if (rset->state == mrp_resource_acquire &&
+ !grant && rset->dont_wait.current)
+ {
+ rset->state = mrp_resource_release;
+ rset->dont_wait.current = rset->dont_wait.client;
+
+ notify = MRP_RESOURCE_EVENT_RELEASE;
+ move = true;
+ }
+ }
+ else {
+ rset->resource.mask.grant = grant;
+ changed = true;
+
+ if (rset->state != mrp_resource_release &&
+ !grant && rset->auto_release.current)
+ {
+ rset->state = mrp_resource_release;
+ rset->auto_release.current = rset->auto_release.client;
+
+ notify = MRP_RESOURCE_EVENT_RELEASE;
+ move = true;
+ }
+ }
+ }
+
+ if (notify) {
+ mrp_resource_set_notify(rset, notify);
+ }
+
+ if (advice != rset->resource.mask.advice) {
+ rset->resource.mask.advice = advice;
+ changed = true;
+ }
+
+ if (replyid || changed) {
+ ev = events + nevent++;
+
+ ev->replyid = replyid;
+ ev->rset = rset;
+ ev->move = move;
+ }
+ } /* while rset */
+ } /* while class */
+
+ manager_end_transaction(zone);
+
+ for (lastev = (ev = events) + nevent; ev < lastev; ev++) {
+ rset = ev->rset;
+
+ if (ev->move)
+ mrp_application_class_move_resource_set(rset);
+
+ mrp_resource_set_updated(rset);
+
+ /* first we send out the revoke/deny events
+ * followed by the grants (in the next for loop)
+ */
+ if (rset->event && !rset->resource.mask.grant)
+ rset->event(ev->replyid, rset, rset->user_data);
+ }
+
+ for (lastev = (ev = events) + nevent; ev < lastev; ev++) {
+ rset = ev->rset;
+
+ if (rset->event && rset->resource.mask.grant)
+ rset->event(ev->replyid, rset, rset->user_data);
+ }
+
+ mrp_free(events);
+
+ for (rid = 0; rid < rcnt; rid++) {
+ owner = get_owner(zoneid, rid);
+ old = oldowners + rid;
+
+ if (owner->class != old->class ||
+ owner->rset != old->rset ||
+ owner->res != old->res )
+ {
+ if (!owner->res)
+ delete_resource_owner(zone,old->res);
+ else if (!old->res)
+ insert_resource_owner(zone,owner->class,owner->rset,owner->res);
+ else
+ update_resource_owner(zone,owner->class,owner->rset,owner->res);
+ }
+ }
+}
+
+int mrp_resource_owner_print(char *buf, int len)
+{
+#define PRINT(fmt, args...) if (p<e) { p += snprintf(p, e-p, fmt , ##args); }
+
+ mrp_zone_t *zone;
+ mrp_resource_owner_t *owner;
+ mrp_application_class_t *class;
+ mrp_resource_set_t *rset;
+ mrp_resource_t *res;
+ mrp_resource_def_t *rdef;
+ uint32_t rcnt, rid;
+ uint32_t zcnt, zid;
+ char *p, *e;
+
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(buf, "invalid argument");
+
+ rcnt = mrp_resource_definition_count();
+ zcnt = mrp_zone_count();
+
+ e = (p = buf) + len;
+
+ PRINT("Resource owners:\n");
+
+ for (zid = 0; zid < zcnt; zid++) {
+ zone = mrp_zone_find_by_id(zid);
+
+ if (!zone) {
+ PRINT(" Zone %u:\n", zid);
+ }
+ else {
+ PRINT(" Zone %s:", zone->name);
+ p += mrp_zone_attribute_print(zone, p, e-p);
+ PRINT("\n");
+ }
+
+ for (rid = 0; rid < rcnt; rid++) {
+ if (!(rdef = mrp_resource_definition_find_by_id(rid)))
+ continue;
+
+ PRINT(" %-15s: ", rdef->name);
+
+ owner = get_owner(zid, rid);
+
+ if (!(class = owner->class) ||
+ !(rset = owner->rset ) ||
+ !(res = owner->res ) )
+ {
+ PRINT("<nobody>");
+ }
+ else {
+ MRP_ASSERT(rdef == res->def, "confused with data structures");
+
+ PRINT("%-15s", class->name);
+
+ p += mrp_resource_attribute_print(res, p, e-p);
+ }
+
+ PRINT("\n");
+ }
+ }
+
+ return p - buf;
+
+#undef PRINT
+}
+
+
+static mrp_resource_owner_t *get_owner(uint32_t zone, uint32_t resid)
+{
+ MRP_ASSERT(zone < MRP_ZONE_MAX && resid < MRP_RESOURCE_MAX,
+ "invalid argument");
+
+ return resource_owners + (zone * MRP_RESOURCE_MAX + resid);
+}
+
+static void reset_owners(uint32_t zone, mrp_resource_owner_t *oldowners)
+{
+ mrp_resource_owner_t *owners = get_owner(zone, 0);
+ size_t size = sizeof(mrp_resource_owner_t) * MRP_RESOURCE_MAX;
+ size_t i;
+
+ if (oldowners)
+ memcpy(oldowners, owners, size);
+
+ memset(owners, 0, size);
+
+ for (i = 0; i < MRP_RESOURCE_MAX; i++)
+ owners[i].share = true;
+}
+
+static bool grant_ownership(mrp_resource_owner_t *owner,
+ mrp_zone_t *zone,
+ mrp_application_class_t *class,
+ mrp_resource_set_t *rset,
+ mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef = res->def;
+ mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
+ bool set_owner = false;
+
+ /*
+ if (forbid_grant())
+ return false;
+ */
+
+ if (owner->modal)
+ return false;
+
+ do { /* not a loop */
+ if (!owner->class && !owner->rset) {
+ /* nobody owns this, so grab it */
+ set_owner = true;
+ break;
+ }
+
+ if (owner->class == class && owner->rset == rset) {
+ /* we happen to already own it */
+ break;
+ }
+
+ if (rdef->shareable && owner->share) {
+ /* OK, someone else owns it but
+ the owner is ready to share it with us */
+ break;
+ }
+
+ return false;
+
+ } while(0);
+
+ if (ftbl && ftbl->allocate) {
+ if (!ftbl->allocate(zone, res, rdef->manager.userdata))
+ return false;
+ }
+
+ if (set_owner) {
+ owner->class = class;
+ owner->rset = rset;
+ owner->res = res;
+ owner->modal = class->modal;
+ }
+
+ owner->share = class->share && res->shared;
+
+ return true;
+}
+
+static bool advice_ownership(mrp_resource_owner_t *owner,
+ mrp_zone_t *zone,
+ mrp_application_class_t *class,
+ mrp_resource_set_t *rset,
+ mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef = res->def;
+ mrp_resource_mgr_ftbl_t *ftbl = rdef->manager.ftbl;
+
+ (void)zone;
+
+ /*
+ if (forbid_grant())
+ return false;
+ */
+
+ if (owner->modal)
+ return false;
+
+ do { /* not a loop */
+ if (!owner->class && !owner->rset)
+ /* nobody owns this */
+ break;
+
+ if (owner->share)
+ /* someone else owns it but it can be shared */
+ break;
+
+ if (owner->class == class) {
+ if (owner->rset->class.priority == rset->class.priority &&
+ class->order == MRP_RESOURCE_ORDER_LIFO)
+ /* same class and resource goes to the last one who asks it */
+ break;
+ }
+
+ return false;
+
+ } while(0);
+
+ if (ftbl && ftbl->advice) {
+ if (!ftbl->advice(zone, res, rdef->manager.userdata))
+ return false;
+ }
+
+ return true;
+}
+
+static void manager_start_transaction(mrp_zone_t *zone)
+{
+ mrp_resource_def_t *rdef;
+ mrp_resource_mgr_ftbl_t *ftbl;
+ void *cursor = NULL;
+
+ while ((rdef = mrp_resource_definition_iterate_manager(&cursor))) {
+ ftbl = rdef->manager.ftbl;
+
+ MRP_ASSERT(ftbl, "confused with data structures");
+
+ if (ftbl->init)
+ ftbl->init(zone, rdef->manager.userdata);
+ }
+}
+
+static void manager_end_transaction(mrp_zone_t *zone)
+{
+ mrp_resource_def_t *rdef;
+ mrp_resource_mgr_ftbl_t *ftbl;
+ void *cursor = NULL;
+
+ while ((rdef = mrp_resource_definition_iterate_manager(&cursor))) {
+ ftbl = rdef->manager.ftbl;
+
+ MRP_ASSERT(ftbl, "confused with data structures");
+
+ if (ftbl->commit)
+ ftbl->commit(zone, rdef->manager.userdata);
+ }
+}
+
+
+static void delete_resource_owner(mrp_zone_t *zone, mrp_resource_t *res)
+{
+ static uint32_t zone_id;
+
+ MQI_WHERE_CLAUSE(where,
+ MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
+ );
+
+ mrp_resource_def_t *rdef;
+ int n;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ rdef = res->def;
+ zone_id = zone->id;
+
+ if ((n = MQI_DELETE(owner_tables[rdef->id], where)) != 1)
+ mrp_log_error("Could not delete resource owner");
+}
+
+static void insert_resource_owner(mrp_zone_t *zone,
+ mrp_application_class_t *class,
+ mrp_resource_set_t *rset,
+ mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef = res->def;
+ uint32_t i;
+ int n;
+ owner_row_t row;
+ owner_row_t *rows[2];
+ mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+ MRP_ASSERT(FIRST_ATTRIBUTE_IDX + rdef->nattr <= MQI_COLUMN_MAX,
+ "too many attributes for a table");
+
+ row.zone_id = zone->id;
+ row.zone_name = zone->name;
+ row.class_name = class->name;
+ row.rset_id = rset->id;
+ memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
+
+ i = 0;
+ cdsc[i].cindex = ZONE_ID_IDX;
+ cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_id);
+
+ i++;
+ cdsc[i].cindex = ZONE_NAME_IDX;
+ cdsc[i].offset = MQI_OFFSET(owner_row_t, zone_name);
+
+ i++;
+ cdsc[i].cindex = CLASS_NAME_IDX;
+ cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
+
+ i++;
+ cdsc[i].cindex = RSET_ID_IDX;
+ cdsc[i].offset = MQI_OFFSET(owner_row_t, rset_id);
+
+ set_attr_descriptors(cdsc + (i+1), res);
+
+ rows[0] = &row;
+ rows[1] = NULL;
+
+ if ((n = MQI_INSERT_INTO(owner_tables[rdef->id], cdsc, rows)) != 1)
+ mrp_log_error("can't insert row into owner table");
+}
+
+static void update_resource_owner(mrp_zone_t *zone,
+ mrp_application_class_t *class,
+ mrp_resource_set_t *rset,
+ mrp_resource_t *res)
+{
+ static uint32_t zone_id;
+
+ MQI_WHERE_CLAUSE(where,
+ MQI_EQUAL( MQI_COLUMN(0), MQI_UNSIGNED_VAR(zone_id) )
+ );
+
+ mrp_resource_def_t *rdef = res->def;
+ uint32_t i;
+ int n;
+ owner_row_t row;
+ mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+ zone_id = zone->id;
+
+ MRP_ASSERT(1 + rdef->nattr <= MQI_COLUMN_MAX,
+ "too many attributes for a table");
+
+ row.class_name = class->name;
+ row.rset_id = rset->id;
+ memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
+
+ i = 0;
+ cdsc[i].cindex = CLASS_NAME_IDX;
+ cdsc[i].offset = MQI_OFFSET(owner_row_t, class_name);
+
+ i++;
+ cdsc[i].cindex = RSET_ID_IDX;
+ cdsc[i].offset = MQI_OFFSET(owner_row_t, rset_id);
+
+ set_attr_descriptors(cdsc + (i+1), res);
+
+
+ if ((n = MQI_UPDATE(owner_tables[rdef->id], cdsc, &row, where)) != 1)
+ mrp_log_error("can't update row in owner table");
+}
+
+
+static void set_attr_descriptors(mqi_column_desc_t *cdsc, mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef = res->def;
+ uint32_t i,j;
+ int o;
+
+ for (i = j = 0; j < rdef->nattr; j++) {
+ switch (rdef->attrdefs[j].type) {
+ case mqi_string: o = MQI_OFFSET(owner_row_t,attrs[j].string); break;
+ case mqi_integer: o = MQI_OFFSET(owner_row_t,attrs[j].integer); break;
+ case mqi_unsignd: o = MQI_OFFSET(owner_row_t,attrs[j].unsignd); break;
+ case mqi_floating: o = MQI_OFFSET(owner_row_t,attrs[j].floating);break;
+ default: /* skip this */ continue;
+ }
+
+ cdsc[i].cindex = FIRST_ATTRIBUTE_IDX + j;
+ cdsc[i].offset = o;
+ i++;
+ }
+
+ cdsc[i].cindex = -1;
+ cdsc[i].offset = 1;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_OWNER_H__
+#define __MURPHY_RESOURCE_OWNER_H__
+
+#include "data-types.h"
+
+
+
+struct mrp_resource_owner_s {
+ mrp_application_class_t *class; /**< owner application class */
+ mrp_resource_set_t *rset; /**< owner resource set */
+ mrp_resource_t *res; /**< owner resource */
+ /* the following fields are for
+ internal use only. They used during
+ update and have no meaningful values
+ outside of the update procedure */
+ bool modal;
+ bool share;
+ bool release;
+};
+
+
+int mrp_resource_owner_create_database_table(mrp_resource_def_t *);
+void mrp_resource_owner_update_zone(uint32_t, mrp_resource_set_t *, uint32_t);
+
+
+#endif /* __MURPHY_RESOURCE_OWNER_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/hashtbl.h>
+#include <murphy/common/utils.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mainloop.h>
+
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/common-api.h>
+
+#include "resource-set.h"
+#include "application-class.h"
+#include "resource.h"
+#include "resource-client.h"
+#include "resource-owner.h"
+#include "resource-lua.h"
+
+
+#define STAMP_MAX ((uint32_t)1 << MRP_KEY_STAMP_BITS)
+#define PRIORITY_MAX ((uint32_t)1 << MRP_KEY_PRIORITY_BITS)
+
+
+static MRP_LIST_HOOK(resource_set_list);
+static uint32_t resource_set_count;
+static mrp_htbl_t *id_hash;
+
+static int add_to_id_hash(mrp_resource_set_t *);
+static void remove_from_id_hash(mrp_resource_set_t *);
+
+static mrp_resource_t *find_resource_by_name(mrp_resource_set_t *,const char*);
+#if 0
+static mrp_resource_t *find_resource_by_id(mrp_resource_set_t *, uint32_t);
+#endif
+
+static uint32_t get_request_stamp(void);
+static const char *state_str(mrp_resource_state_t);
+static void send_rset_event(mrp_resource_set_t *rset,
+ mrp_resource_event_t ev);
+
+uint32_t mrp_get_resource_set_count(void)
+{
+ return resource_set_count;
+}
+
+mrp_resource_set_t *mrp_resource_set_create(mrp_resource_client_t *client,
+ bool auto_release,
+ bool dont_wait,
+ uint32_t priority,
+ mrp_resource_event_cb_t event_cb,
+ void *user_data)
+{
+ static uint32_t our_id;
+
+ mrp_resource_set_t *rset;
+
+ MRP_ASSERT(client, "invalid argument");
+
+ if (priority >= PRIORITY_MAX)
+ priority = PRIORITY_MAX - 1;
+
+ if (!(rset = mrp_allocz(sizeof(mrp_resource_set_t))))
+ mrp_log_error("Memory alloc failure. Can't create resource set");
+ else {
+ rset->id = ++our_id;
+
+ rset->dont_wait.current = dont_wait;
+ rset->dont_wait.client = dont_wait;
+
+ rset->auto_release.current = auto_release;
+ rset->auto_release.client = auto_release;
+
+ mrp_list_init(&rset->resource.list);
+ rset->resource.share = false;
+
+ mrp_list_append(&client->resource_sets, &rset->client.list);
+ rset->client.ptr = client;
+ rset->client.reqno = MRP_RESOURCE_REQNO_INVALID;
+
+ mrp_list_init(&rset->class.list);
+ rset->class.priority = priority;
+
+ mrp_list_append(&resource_set_list, &rset->list);
+
+ rset->event = event_cb;
+ rset->user_data = user_data;
+
+ resource_set_count++;
+
+ add_to_id_hash(rset);
+ mrp_resource_lua_register_resource_set(rset);
+
+ send_rset_event(rset, MRP_RESOURCE_EVENT_CREATED);
+ }
+
+ return rset;
+}
+
+void mrp_resource_set_destroy(mrp_resource_set_t *rset)
+{
+ mrp_resource_state_t state;
+ mrp_list_hook_t *entry, *n;
+ mrp_resource_t *res;
+
+ if (rset) {
+ state = rset->state;
+
+ rset->event = NULL; /* make sure nothing is sent any more */
+
+ send_rset_event(rset, MRP_RESOURCE_EVENT_DESTROYED);
+
+ mrp_resource_lua_unregister_resource_set(rset);
+ remove_from_id_hash(rset);
+
+ if (state == mrp_resource_acquire)
+ mrp_resource_set_release(rset, MRP_RESOURCE_REQNO_INVALID);
+
+ mrp_list_foreach(&rset->resource.list, entry, n) {
+ res = mrp_list_entry(entry, mrp_resource_t, list);
+ mrp_resource_notify(res, rset, MRP_RESOURCE_EVENT_DESTROYED);
+ mrp_resource_destroy(res);
+ }
+
+ mrp_list_delete(&rset->list);
+ mrp_list_delete(&rset->client.list);
+ mrp_list_delete(&rset->class.list);
+
+ mrp_free(rset);
+
+ if (resource_set_count > 0)
+ resource_set_count--;
+ }
+}
+
+mrp_resource_set_t *mrp_resource_set_find_by_id(uint32_t id)
+{
+ return id_hash ? mrp_htbl_lookup(id_hash, NULL + id) : NULL;
+}
+
+uint32_t mrp_get_resource_set_id(mrp_resource_set_t *rset)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ return rset->id;
+}
+
+mrp_resource_state_t mrp_get_resource_set_state(mrp_resource_set_t *rset)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ return rset->state;
+}
+
+mrp_resource_mask_t mrp_get_resource_set_grant(mrp_resource_set_t *rset)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ return rset->resource.mask.grant;
+}
+
+mrp_resource_mask_t mrp_get_resource_set_advice(mrp_resource_set_t *rset)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ return rset->resource.mask.advice;
+}
+
+mrp_resource_client_t *mrp_get_resource_set_client(mrp_resource_set_t *rset)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ return rset->client.ptr;
+}
+
+mrp_resource_t *mrp_resource_set_find_resource(uint32_t rsetid,
+ const char *resnam)
+{
+ mrp_resource_set_t *rset;
+ mrp_resource_t *res;
+
+ MRP_ASSERT(resnam, "invalid argument");
+
+ if (!(rset = mrp_resource_set_find_by_id(rsetid)))
+ res = NULL;
+ else
+ res = find_resource_by_name(rset, resnam);
+
+ return res;
+}
+
+
+mrp_resource_t *mrp_resource_set_iterate_resources(mrp_resource_set_t *rset,
+ void **cursor)
+{
+ mrp_list_hook_t *list, *entry;
+
+ MRP_ASSERT(rset && cursor, "invalid argument");
+
+ list = &rset->resource.list;
+ entry = (*cursor == NULL) ? list->next : (mrp_list_hook_t *)*cursor;
+
+ if (entry == list)
+ return NULL;
+
+ *cursor = entry->next;
+
+ return mrp_list_entry(entry, mrp_resource_t, list);
+}
+
+
+int mrp_resource_set_add_resource(mrp_resource_set_t *rset,
+ const char *name,
+ bool shared,
+ mrp_attr_t *attrs,
+ bool mandatory)
+{
+ uint32_t mask;
+ mrp_resource_t *res;
+ uint32_t rsetid;
+ bool autorel;
+
+ MRP_ASSERT(rset && name, "invalid argument");
+
+ rsetid = rset->id;
+ autorel = rset->auto_release.client;
+
+ if (!(res = mrp_resource_create(name, rsetid, autorel, shared, attrs))) {
+ mrp_log_error("Can't add resource '%s' name to resource set %u",
+ name, rset->id);
+ return -1;
+ }
+
+ mask = mrp_resource_get_mask(res);
+
+ rset->resource.mask.all |= mask;
+ rset->resource.mask.mandatory |= mandatory ? mask : 0;
+ rset->resource.share |= mrp_resource_is_shared(res);
+
+
+ mrp_list_append(&rset->resource.list, &res->list);
+
+ mrp_resource_lua_add_resource_to_resource_set(rset, res);
+
+ return 0;
+}
+
+mrp_attr_t *mrp_resource_set_read_attribute(mrp_resource_set_t *rset,
+ const char *resnam,
+ uint32_t attridx,
+ mrp_attr_t *buf)
+{
+ mrp_resource_t *res;
+
+ MRP_ASSERT(rset && resnam, "invalid argument");
+
+ if (!(res = find_resource_by_name(rset, resnam)))
+ return NULL;
+
+ return mrp_resource_read_attribute(res, attridx, buf);
+}
+
+mrp_attr_t *mrp_resource_set_read_all_attributes(mrp_resource_set_t *rset,
+ const char *resnam,
+ uint32_t buflen,
+ mrp_attr_t *buf)
+{
+ mrp_resource_t *res;
+
+ MRP_ASSERT(rset && resnam, "invalid argument");
+
+ if (!(res = find_resource_by_name(rset, resnam)))
+ return NULL;
+
+ return mrp_resource_read_all_attributes(res, buflen, buf);
+}
+
+int mrp_resource_set_write_attributes(mrp_resource_set_t *rset,
+ const char *resnam,
+ mrp_attr_t *attrs)
+{
+ mrp_resource_t *res;
+
+ MRP_ASSERT(rset && resnam && attrs, "invalid argument");
+
+ if (!(res = find_resource_by_name(rset, resnam)))
+ return -1;
+
+ if (mrp_resource_write_attributes(res, attrs) < 0)
+ return -1;
+
+ return 0;
+}
+
+void mrp_resource_set_acquire(mrp_resource_set_t *rset, uint32_t reqid)
+{
+ mrp_resource_state_t old_state;
+ mqi_handle_t trh;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ mrp_debug("acquiring resource set #%d", rset->id);
+
+ old_state = rset->state;
+ rset->state = mrp_resource_acquire;
+
+ if (rset->class.ptr) {
+ rset->request.id = reqid;
+ rset->request.stamp = get_request_stamp();
+
+ mrp_application_class_move_resource_set(rset);
+
+ if (old_state != mrp_resource_acquire)
+ mrp_resource_set_notify(rset, MRP_RESOURCE_EVENT_ACQUIRE);
+
+ trh = mqi_begin_transaction();
+ mrp_resource_owner_update_zone(rset->zone, rset, reqid);
+ mqi_commit_transaction(trh);
+ }
+}
+
+void mrp_resource_set_release(mrp_resource_set_t *rset, uint32_t reqid)
+{
+ mqi_handle_t trh;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ mrp_debug("releasing resource set #%d", rset->id);
+
+ if (!rset->class.ptr)
+ rset->state = mrp_resource_release;
+ else {
+ if (rset->state == mrp_resource_release) {
+ if (rset->event)
+ rset->event(reqid, rset, rset->user_data);
+ }
+ else {
+ rset->state = mrp_resource_release;
+ rset->request.id = reqid;
+ rset->request.stamp = get_request_stamp();
+
+ mrp_application_class_move_resource_set(rset);
+
+ mrp_resource_set_notify(rset, MRP_RESOURCE_EVENT_RELEASE);
+
+ trh = mqi_begin_transaction();
+ mrp_resource_owner_update_zone(rset->zone, rset, reqid);
+ mqi_commit_transaction(trh);
+ }
+ }
+}
+
+void mrp_resource_set_updated(mrp_resource_set_t *rset)
+{
+ mrp_resource_t *res;
+ mrp_resource_def_t *def;
+ mrp_list_hook_t *resen, *n;
+ mrp_resource_mask_t mask;
+ bool grant;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ mrp_debug("resource set got #%d updated", rset->id);
+
+ mrp_list_foreach(&rset->resource.list, resen, n) {
+ res = mrp_list_entry(resen, mrp_resource_t, list);
+ def = res->def;
+
+ mask = ((mrp_resource_mask_t)1) << def->id;
+ grant = (mask & rset->resource.mask.grant) ? true : false;
+
+ mrp_debug(" %s now %sgranted", def->name, grant ? "" : "not ");
+
+ mrp_resource_user_update(res, rset->state, grant);
+ }
+}
+
+
+MRP_REGISTER_EVENTS(resource_events,
+ MRP_EVENT(MURPHY_RESOURCE_EVENT_CREATED , MRP_RESOURCE_EVENT_CREATED ),
+ MRP_EVENT(MURPHY_RESOURCE_EVENT_DESTROYED, MRP_RESOURCE_EVENT_DESTROYED),
+ MRP_EVENT(MURPHY_RESOURCE_EVENT_ACQUIRE , MRP_RESOURCE_EVENT_ACQUIRE ),
+ MRP_EVENT(MURPHY_RESOURCE_EVENT_RELEASE , MRP_RESOURCE_EVENT_RELEASE ));
+
+
+
+static void send_rset_event(mrp_resource_set_t *rset, mrp_resource_event_t ev)
+{
+ mrp_event_bus_t *bus = MRP_GLOBAL_BUS;
+ uint32_t id = resource_events[ev].id;
+ int flags = MRP_EVENT_SYNCHRONOUS;;
+ uint16_t tag = MRP_RESOURCE_TAG_RSET_ID;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ /* The resource set id is enough information, because the full resource
+ * set can be found by making a query with the id. */
+
+
+ mrp_debug("emit event %d for rset %u", id, rset->id);
+
+ switch (ev) {
+ case MRP_RESOURCE_EVENT_CREATED:
+ mrp_event_emit_msg(bus, id, flags, MRP_MSG_TAG_UINT32(tag, rset->id));
+ break;
+ case MRP_RESOURCE_EVENT_ACQUIRE:
+ mrp_event_emit_msg(bus, id, flags, MRP_MSG_TAG_UINT32(tag, rset->id));
+ break;
+ case MRP_RESOURCE_EVENT_RELEASE:
+ mrp_event_emit_msg(bus, id, flags, MRP_MSG_TAG_UINT32(tag, rset->id));
+ break;
+ case MRP_RESOURCE_EVENT_DESTROYED:
+ mrp_event_emit_msg(bus, id, flags, MRP_MSG_TAG_UINT32(tag, rset->id));
+ break;
+ default:
+ break;
+ }
+}
+
+void mrp_resource_set_notify(mrp_resource_set_t *rset, mrp_resource_event_t ev)
+{
+ mrp_resource_t *res;
+ void *cursor = NULL;
+
+ MRP_ASSERT(rset, "invalid argument");
+
+ send_rset_event(rset, ev);
+
+ while ((res = mrp_resource_set_iterate_resources(rset, &cursor)))
+ mrp_resource_notify(res, rset, ev);
+}
+
+void mrp_resource_set_request_auto_release(mrp_resource_set_t *rset,
+ bool auto_release)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ rset->auto_release.current = auto_release;
+}
+
+void mrp_resource_set_request_dont_wait(mrp_resource_set_t *rset,
+ bool dont_wait)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ rset->dont_wait.current = dont_wait;
+}
+
+int mrp_resource_set_print(mrp_resource_set_t *rset, size_t indent,
+ char *buf, int len)
+{
+#define PRINT(fmt, args...) if (p<e) { p += snprintf(p, e-p, fmt , ##args); }
+
+ mrp_resource_t *res;
+ mrp_list_hook_t *resen, *n;
+ uint32_t mandatory;
+ char gap[] = " ";
+ char *p, *e;
+
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(rset && indent < sizeof(gap)-1 && buf,
+ "invalid argument");
+
+ gap[indent] = '\0';
+
+ e = (p = buf) + len;
+
+ mandatory = rset->resource.mask.mandatory;
+
+ PRINT("%s%3u - 0x%02x/0x%02x 0x%02x/0x%02x 0x%08x %d %s%s%s %s\n",
+ gap, rset->id,
+ rset->resource.mask.all, mandatory,
+ rset->resource.mask.grant, rset->resource.mask.advice,
+ mrp_application_class_get_sorting_key(rset), rset->class.priority,
+ rset->resource.share ? "shared ":"exclusive",
+ rset->auto_release.client ? ",autorelease" : "",
+ rset->dont_wait.client ? ",dontwait" : "",
+ state_str(rset->state));
+
+ mrp_list_foreach(&rset->resource.list, resen, n) {
+ res = mrp_list_entry(resen, mrp_resource_t, list);
+ p += mrp_resource_print(res, mandatory, indent+6, p, e-p);
+ }
+
+ if (p >= e) {
+ if (len >= 5) {
+ e[-5] = e[-4] = e[-3] = '.';
+ e[-2] = '\n';
+ e[-1] = '\0';
+ }
+ }
+
+ return p - buf;
+
+#undef PRINT
+}
+
+static uint32_t rset_hash(const void *key)
+{
+ return (uint32_t)(key - NULL);
+}
+
+static int rset_comp(const void *key1, const void *key2)
+{
+ uint32_t k1 = key1 - NULL;
+ uint32_t k2 = key2 - NULL;
+
+ return (k1 == k2) ? 0 : ((k1 > k2) ? 1 : -1);
+}
+
+static void init_id_hash(void)
+{
+ mrp_htbl_config_t cfg;
+
+ if (!id_hash) {
+ cfg.nentry = 32;
+ cfg.comp = rset_comp;
+ cfg.hash = rset_hash;
+ cfg.free = NULL;
+ cfg.nbucket = cfg.nentry;
+
+ id_hash = mrp_htbl_create(&cfg);
+
+ MRP_ASSERT(id_hash, "failed to make id_hash for resource sets");
+ }
+}
+
+static int add_to_id_hash(mrp_resource_set_t *rset)
+{
+ MRP_ASSERT(rset, "invalid argument");
+
+ init_id_hash();
+
+ if (!mrp_htbl_insert(id_hash, NULL + rset->id, rset))
+ return -1;
+
+ return 0;
+}
+
+static void remove_from_id_hash(mrp_resource_set_t *rset)
+{
+ mrp_resource_set_t *deleted;
+
+ if (id_hash && rset) {
+ deleted = mrp_htbl_remove(id_hash, NULL + rset->id, false);
+
+ MRP_ASSERT(!deleted || deleted == rset, "confused with data "
+ "structures when deleting resource-set from id hash");
+
+ /* in case we were not compiled with debug enabled */
+ if (deleted != rset) {
+ mrp_log_error("confused with data structures when deleting "
+ "resource-set '%u' from id hash", rset->id);
+ }
+ }
+}
+
+static mrp_resource_t *find_resource_by_name(mrp_resource_set_t *rset,
+ const char *name)
+{
+ mrp_list_hook_t *entry, *n;
+ mrp_resource_t *res;
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(rset && name, "invalid_argument");
+
+ mrp_list_foreach(&rset->resource.list, entry, n) {
+ res = mrp_list_entry(entry, mrp_resource_t, list);
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+
+ if (!strcasecmp(name, rdef->name))
+ return res;
+ }
+
+ return NULL;
+}
+
+#if 0
+static mrp_resource_t *find_resource_by_id(mrp_resource_set_t *rset,
+ uint32_t id)
+{
+ mrp_list_hook_t *entry, *n;
+ mrp_resource_t *res;
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(rset, "invalid_argument");
+
+ mrp_list_foreach(&rset->resource.list, entry, n) {
+ res = mrp_list_entry(entry, mrp_resource_t, list);
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+
+ if (id == rdef->id)
+ return res;
+ }
+
+ return NULL;
+}
+#endif
+
+static uint32_t get_request_stamp(void)
+{
+ static uint32_t stamp;
+
+ mrp_list_hook_t *entry, *n;
+ mrp_resource_set_t *rset;
+ uint32_t min;
+
+ if ((min = stamp) >= STAMP_MAX) {
+ mrp_log_info("rebasing resource set stamps");
+
+ mrp_list_foreach(&resource_set_list, entry, n) {
+ rset = mrp_list_entry(entry, mrp_resource_set_t, list);
+ if (rset->request.stamp < min)
+ min = rset->request.stamp;
+ }
+
+ stamp -= min;
+
+ mrp_list_foreach(&resource_set_list, entry, n) {
+ rset = mrp_list_entry(entry, mrp_resource_set_t, list);
+ rset->request.stamp -= min;
+ }
+ }
+
+ MRP_ASSERT(stamp < STAMP_MAX, "Request stamp overflow");
+
+ return stamp++;
+}
+
+static const char *state_str(mrp_resource_state_t state)
+{
+ switch(state) {
+ case mrp_resource_no_request: return "no-request";
+ case mrp_resource_release: return "release";
+ case mrp_resource_acquire: return "acquire";
+ default: return "< ??? >";
+ }
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_SET_H__
+#define __MURPHY_RESOURCE_SET_H__
+
+#include <murphy/common/list.h>
+
+#include "data-types.h"
+
+/* event protocol */
+
+#define MURPHY_RESOURCE_EVENT_CREATED "resource_set_created"
+#define MURPHY_RESOURCE_EVENT_DESTROYED "resource_set_destroyed"
+#define MURPHY_RESOURCE_EVENT_ACQUIRE "resource_set_acquire"
+#define MURPHY_RESOURCE_EVENT_RELEASE "resource_set_release"
+
+#define MRP_RESOURCE_TAG_RSET_ID ((uint16_t) 1)
+
+struct mrp_resource_set_s {
+ mrp_list_hook_t list;
+ uint32_t id;
+ mrp_resource_state_t state;
+ struct {
+ bool current;
+ bool client;
+ } auto_release;
+ struct {
+ bool current;
+ bool client;
+ } dont_wait;
+ struct {
+ struct {
+ mrp_resource_mask_t all;
+ mrp_resource_mask_t mandatory;
+ mrp_resource_mask_t grant;
+ mrp_resource_mask_t advice;
+ } mask;
+ mrp_list_hook_t list;
+ bool share;
+ } resource;
+ struct {
+ mrp_list_hook_t list;
+ mrp_resource_client_t *ptr;
+ uint32_t reqno;
+ } client;
+ struct {
+ mrp_list_hook_t list;
+ mrp_application_class_t *ptr;
+ uint32_t priority;
+ } class;
+ uint32_t zone;
+ struct {
+ uint32_t id;
+ uint32_t stamp;
+ } request;
+ mrp_resource_event_cb_t event;
+ void *user_data;
+};
+
+
+mrp_resource_set_t *mrp_resource_set_find_by_id(uint32_t);
+mrp_resource_t *mrp_resource_set_find_resource(uint32_t, const char *);
+uint32_t mrp_get_resource_set_count(void);
+void mrp_resource_set_updated(mrp_resource_set_t *);
+void mrp_resource_set_notify(mrp_resource_set_t *,
+ mrp_resource_event_t);
+void mrp_resource_set_request_auto_release(mrp_resource_set_t *,
+ bool);
+void mrp_resource_set_request_dont_wait(mrp_resource_set_t *,
+ bool);
+int mrp_resource_set_print(mrp_resource_set_t *, size_t,
+ char *, int);
+
+
+#endif /* __MURPHY_RESOURCE_SET_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include <murphy-db/mqi.h>
+
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/manager-api.h>
+
+#include "resource.h"
+#include "resource-owner.h"
+#include "resource-set.h"
+#include "application-class.h"
+#include "zone.h"
+
+
+#define RESOURCE_MAX (sizeof(mrp_resource_mask_t) * 8)
+#define ATTRIBUTE_MAX (sizeof(mrp_attribute_mask_t) * 8)
+#define NAME_LENGTH 24
+
+#define RSETID_IDX 0
+#define AUTOREL_IDX 1
+#define STATE_IDX 2
+#define GRANT_IDX 3
+#define FIRST_ATTRIBUTE_IDX 4
+
+
+#define VALID_TYPE(t) ((t) == mqi_string || \
+ (t) == mqi_integer || \
+ (t) == mqi_unsignd || \
+ (t) == mqi_floating )
+
+typedef struct {
+ uint32_t rsetid;
+ int32_t autorel;
+ int32_t state;
+ int32_t grant;
+ mrp_attr_value_t attrs[MQI_COLUMN_MAX];
+} user_row_t;
+
+
+static uint32_t resource_def_count;
+static mrp_resource_def_t *resource_def_table[RESOURCE_MAX];
+static MRP_LIST_HOOK(manager_list);
+static mqi_handle_t resource_user_table[RESOURCE_MAX];
+
+static uint32_t add_resource_definition(const char *, bool, uint32_t,
+ mrp_resource_mgr_ftbl_t *, void *);
+
+#if 0
+static uint32_t find_resource_attribute_id(mrp_resource_t *, const char *);
+
+static mqi_data_type_t get_resource_attribute_value_type(mrp_resource_t *,
+ uint32_t);
+
+static mrp_attr_value_t *get_resource_attribute_default_value(mrp_resource_t*,
+ uint32_t);
+#endif
+
+static int resource_user_create_table(mrp_resource_def_t *);
+static void resource_user_insert(mrp_resource_t *, bool);
+static void resource_user_delete(mrp_resource_t *);
+
+static void set_attr_descriptors(mqi_column_desc_t *, mrp_resource_t *);
+
+
+
+uint32_t mrp_resource_definition_create(const char *name, bool shareable,
+ mrp_attr_def_t *attrdefs,
+ mrp_resource_mgr_ftbl_t *manager,
+ void *mgrdata)
+{
+ uint32_t nattr;
+ uint32_t id;
+ mrp_resource_def_t *def;
+
+ MRP_ASSERT(name, "invalid argument");
+
+ if (mrp_resource_definition_find_by_name(name)) {
+ mrp_log_error("attmpt to redefine resource '%s'", name);
+ return MRP_RESOURCE_ID_INVALID;
+ }
+
+ for (nattr = 0; attrdefs && attrdefs[nattr].name; nattr++)
+ ;
+
+ id = add_resource_definition(name, shareable, nattr, manager, mgrdata);
+
+ if (id != MRP_RESOURCE_ID_INVALID) {
+ def = mrp_resource_definition_find_by_id(id);
+
+ MRP_ASSERT(def, "got confused with data structures");
+
+ if (mrp_attribute_copy_definitions(attrdefs, def->attrdefs) < 0)
+ return MRP_RESOURCE_ID_INVALID;
+
+ resource_user_create_table(def);
+ mrp_resource_owner_create_database_table(def);
+ }
+
+ return id;
+}
+
+uint32_t mrp_resource_definition_count(void)
+{
+ return resource_def_count;
+}
+
+mrp_resource_def_t *mrp_resource_definition_find_by_name(const char *name)
+{
+ mrp_resource_def_t *def;
+ uint32_t i;
+
+ for (i = 0; i < resource_def_count; i++) {
+ def = resource_def_table[i];
+
+ if (def && !strcasecmp(name, def->name))
+ return def;
+ }
+
+ return NULL;
+}
+
+uint32_t mrp_resource_definition_get_resource_id_by_name(const char *name)
+{
+ mrp_resource_def_t *def = mrp_resource_definition_find_by_name(name);
+
+ if (!def) {
+ return MRP_RESOURCE_ID_INVALID;
+ }
+
+ return def->id;
+}
+
+mrp_resource_def_t *mrp_resource_definition_find_by_id(uint32_t id)
+{
+ if (id < resource_def_count)
+ return resource_def_table[id];
+
+ return NULL;
+}
+
+mrp_resource_def_t *mrp_resource_definition_iterate_manager(void **cursor)
+{
+ mrp_list_hook_t *entry;
+
+ MRP_ASSERT(cursor, "invalid argument");
+
+ entry = (*cursor == NULL) ? manager_list.next : (mrp_list_hook_t *)*cursor;
+
+ if (entry == &manager_list)
+ return NULL;
+
+ *cursor = entry->next;
+
+ return mrp_list_entry(entry, mrp_resource_def_t, manager.list);
+}
+
+const char **mrp_resource_definition_get_all_names(uint32_t buflen,
+ const char **buf)
+{
+ uint32_t i;
+
+ MRP_ASSERT(!buf || (buf && buflen > 1), "invlaid argument");
+
+ if (buf) {
+ if (buflen < resource_def_count + 1)
+ return NULL;
+ }
+ else {
+ buflen = resource_def_count + 1;
+ if (!(buf = mrp_allocz(sizeof(const char *) * buflen))) {
+ mrp_log_error("Memory alloc failure. Can't get resource names");
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < resource_def_count; i++)
+ buf[i] = resource_def_table[i]->name;
+
+ buf[i] = NULL;
+
+ return buf;
+}
+
+mrp_attr_t *mrp_resource_definition_read_all_attributes(uint32_t resid,
+ uint32_t buflen,
+ mrp_attr_t *buf)
+{
+ mrp_resource_def_t *rdef = mrp_resource_definition_find_by_id(resid);
+ mrp_attr_t *retval;
+
+
+ if (!rdef)
+ retval = mrp_attribute_get_all_values(buflen, buf, 0, NULL, 0);
+ else {
+ retval = mrp_attribute_get_all_values(buflen, buf, rdef->nattr,
+ rdef->attrdefs, 0);
+ }
+
+ if (!retval) {
+ mrp_log_error("Memory alloc failure. Can't get all "
+ "attributes of resource definition");
+ }
+
+ return retval;
+}
+
+
+
+mrp_resource_t *mrp_resource_create(const char *name,
+ uint32_t rsetid,
+ bool autorel,
+ bool shared,
+ mrp_attr_t *attrs)
+{
+ mrp_resource_t *res = NULL;
+ mrp_resource_def_t *rdef;
+ size_t base_size;
+ size_t attr_size;
+ size_t total_size;
+ int sts;
+
+ MRP_ASSERT(name, "invalid argument");
+
+ if (!(rdef = mrp_resource_definition_find_by_name(name))) {
+ mrp_log_warning("Can't find resource definition '%s'. "
+ "No resource created", name);
+ }
+ else {
+ base_size = sizeof(mrp_resource_t);
+ attr_size = sizeof(mrp_attr_value_t) * rdef->nattr;
+ total_size = base_size + attr_size;
+
+ if (!(res = mrp_allocz(total_size))) {
+ mrp_log_error("Memory alloc failure. Can't create "
+ "resource '%s'", name);
+ }
+ else {
+ mrp_list_init(&res->list);
+
+ res->rsetid = rsetid;
+ res->def = rdef;
+ res->shared = rdef->shareable ? shared : false;
+
+ sts = mrp_attribute_set_values(attrs, rdef->nattr,
+ rdef->attrdefs, res->attrs);
+ if (sts < 0) {
+ mrp_log_error("Memory alloc failure. No '%s' "
+ "resource created", name);
+ return NULL;
+ }
+
+ resource_user_insert(res, autorel);
+ }
+ }
+
+ return res;
+}
+
+void mrp_resource_destroy(mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef;
+ mqi_data_type_t type;
+ uint32_t id;
+
+ if (res) {
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "invalid_argument");
+
+ resource_user_delete(res);
+
+ mrp_list_delete(&res->list);
+
+ for (id = 0; id < rdef->nattr; id++) {
+ type = rdef->attrdefs[id].type;
+
+ if (type == mqi_string)
+ mrp_free((void *)res->attrs[id].string);
+ }
+
+ mrp_free(res);
+ }
+}
+
+uint32_t mrp_resource_get_id(mrp_resource_t *res)
+{
+ mrp_resource_def_t *def;
+
+ if (res) {
+ def = res->def;
+ MRP_ASSERT(def, "confused with internal data structures");
+ return def->id;
+ }
+
+ return MRP_RESOURCE_ID_INVALID;
+}
+
+const char *mrp_resource_get_name(mrp_resource_t *res)
+{
+ mrp_resource_def_t *def;
+
+ if (res) {
+ def = res->def;
+
+ MRP_ASSERT(def && def->name, "confused with internal data structures");
+
+ return def->name;
+ }
+
+ return "<unknown resource>";
+}
+
+mrp_resource_mask_t mrp_resource_get_mask(mrp_resource_t *res)
+{
+ mrp_resource_def_t *def;
+ mrp_resource_mask_t mask = 0;
+
+ if (res) {
+ def = res->def;
+
+ MRP_ASSERT(def, "confused with internal data structures");
+
+ mask = (mrp_resource_mask_t)1 << def->id;
+ }
+
+ return mask;
+}
+
+bool mrp_resource_is_shared(mrp_resource_t *res)
+{
+ if (res)
+ return res->shared;
+
+ return false;
+}
+
+mrp_attr_t *mrp_resource_read_attribute(mrp_resource_t *res,
+ uint32_t idx,
+ mrp_attr_t *value)
+{
+ mrp_attr_t *retval;
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+
+ retval = mrp_attribute_get_value(idx, value, rdef->nattr,
+ rdef->attrdefs, res->attrs);
+
+ if (!retval) {
+ mrp_log_error("Memory alloc failure. Can't get "
+ "resource '%s' attribute %u", rdef->name, idx);
+ }
+
+ return retval;
+}
+
+
+mrp_attr_t *mrp_resource_read_all_attributes(mrp_resource_t *res,
+ uint32_t nvalue,
+ mrp_attr_t *values)
+{
+ mrp_attr_t *retval;
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+
+ retval = mrp_attribute_get_all_values(nvalue, values, rdef->nattr,
+ rdef->attrdefs, res->attrs);
+
+ if (!retval) {
+ mrp_log_error("Memory alloc failure. Can't get all "
+ "attributes of resource '%s'", rdef->name);
+ }
+
+ return retval;
+}
+
+int mrp_resource_write_attributes(mrp_resource_t *res, mrp_attr_t *values)
+{
+ int sts;
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(res && values, "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+
+ sts = mrp_attribute_set_values(values, rdef->nattr,
+ rdef->attrdefs, res->attrs);
+
+ if (sts < 0) {
+ mrp_log_error("Memory alloc failure. Can't set attributes "
+ "of resource '%s'", rdef->name);
+ }
+
+ return sts;
+}
+
+const char *mrp_resource_get_application_class(mrp_resource_t *res)
+{
+ mrp_resource_set_t *rset;
+ mrp_application_class_t *class;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ if (!(rset = mrp_resource_set_find_by_id(res->rsetid)))
+ return NULL;
+
+ if (!(class = rset->class.ptr))
+ return NULL;
+
+ return class->name;
+}
+
+void mrp_resource_notify(mrp_resource_t *res,
+ mrp_resource_set_t *rset,
+ mrp_resource_event_t event)
+{
+ mrp_resource_def_t *rdef;
+ mrp_resource_mgr_ftbl_t *ftbl;
+ mrp_manager_notify_func_t notify;
+ mrp_application_class_t *class;
+ mrp_zone_t *zone;
+
+ MRP_ASSERT(res && rset, "inavlid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+
+ if ((ftbl = rdef->manager.ftbl) &&
+ (notify = ftbl->notify) &&
+ (zone = mrp_zone_find_by_id(rset->zone)) &&
+ (class = rset->class.ptr))
+ {
+ notify(event, zone, class, res, rdef->manager.userdata);
+ }
+}
+
+int mrp_resource_print(mrp_resource_t *res, uint32_t mandatory,
+ size_t indent, char *buf, int len)
+{
+#define PRINT(fmt, args...) if (p<e) { p += snprintf(p, e-p, fmt , ##args); }
+
+ mrp_resource_def_t *rdef;
+ char gap[] = " ";
+ char *p, *e;
+ uint32_t m;
+
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(res && indent < sizeof(gap)-1 && buf,
+ "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "Confused with data structures");
+
+ gap[indent] = '\0';
+
+ e = (p = buf) + len;
+ m = ((mrp_resource_mask_t)1 << rdef->id);
+
+ PRINT("%s%s: 0x%02x %s %s", gap, rdef->name, m,
+ (m & mandatory) ? "mandatory":"optional ",
+ res->shared ? "shared ":"exlusive");
+
+ p += mrp_resource_attribute_print(res, p, e-p);
+
+ PRINT("\n");
+
+
+ return p - buf;
+
+#undef PRINT
+}
+
+int mrp_resource_attribute_print(mrp_resource_t *res, char *buf, int len)
+{
+ mrp_resource_def_t *rdef;
+
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(res && buf, "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "Confused with data structures");
+
+ return mrp_attribute_print(rdef->nattr,rdef->attrdefs,res->attrs, buf,len);
+}
+
+
+static uint32_t add_resource_definition(const char *name,
+ bool shareable,
+ uint32_t nattr,
+ mrp_resource_mgr_ftbl_t *mgrftbl,
+ void *mgrdata)
+{
+ mrp_resource_def_t *def;
+ const char *dup_name;
+ size_t size;
+ uint32_t id;
+
+ MRP_ASSERT(name && nattr < ATTRIBUTE_MAX, "invalid argument");
+
+ if (resource_def_count >= RESOURCE_MAX) {
+ mrp_log_error("Resource table overflow. Can't add resource '%s'",name);
+ return MRP_RESOURCE_ID_INVALID;
+ }
+
+ size = sizeof(mrp_resource_def_t) + sizeof(mrp_attr_def_t) * nattr;
+
+ if (!(def = mrp_allocz(size)) || !(dup_name = mrp_strdup(name))) {
+ mrp_log_error("Memory alloc failure. Can't add resource '%s'", name);
+ return MRP_RESOURCE_ID_INVALID;
+ }
+
+ id = resource_def_count++;
+
+ def->id = id;
+ def->name = dup_name;
+ def->shareable = shareable;
+ def->nattr = nattr;
+
+ if (mgrftbl) {
+ def->manager.ftbl = mrp_alloc(sizeof(mrp_resource_mgr_ftbl_t));
+ def->manager.userdata = mgrdata;
+
+ if (def->manager.ftbl)
+ memcpy(def->manager.ftbl, mgrftbl,sizeof(mrp_resource_mgr_ftbl_t));
+ else {
+ mrp_log_error("Memory alloc failure. No manager for resource '%s'",
+ name);
+ }
+ }
+
+ resource_def_table[id] = def;
+
+ if (!mgrftbl)
+ mrp_list_init(&def->manager.list);
+ else
+ mrp_list_append(&manager_list, &def->manager.list);
+
+ return id;
+}
+
+
+#if 0
+static uint32_t find_resource_attribute_id(mrp_resource_t *res,
+ const char *attrnam)
+{
+ mrp_resource_def_t *rdef;
+ mrp_attr_def_t *adef;
+ uint32_t id;
+
+ if (res && (rdef = res->def) && attrnam) {
+ for (id = 0; id < rdef->nattr; id++) {
+ adef = rdef->attrdefs + id;
+
+ if (!strcasecmp(attrnam, adef->name))
+ return id;
+ }
+ }
+
+ return MRP_RESOURCE_ID_INVALID;
+}
+
+static mqi_data_type_t
+get_resource_attribute_value_type(mrp_resource_t *res, uint32_t id)
+{
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+ MRP_ASSERT(id < rdef->nattr, "invalid argument");
+
+ return rdef->attrdefs[id].type;
+}
+
+static mrp_attr_value_t *
+get_resource_attribute_default_value(mrp_resource_t *res, uint32_t id)
+{
+ mrp_resource_def_t *rdef;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ rdef = res->def;
+
+ MRP_ASSERT(rdef, "confused with data structures");
+ MRP_ASSERT(id < rdef->nattr, "invalid argument");
+
+ return &rdef->attrdefs[id].value;
+}
+#endif
+
+
+static int resource_user_create_table(mrp_resource_def_t *rdef)
+{
+ MQI_COLUMN_DEFINITION_LIST(base_coldefs,
+ MQI_COLUMN_DEFINITION( "rsetid" , MQI_UNSIGNED ),
+ MQI_COLUMN_DEFINITION( "autorel", MQI_INTEGER ),
+ MQI_COLUMN_DEFINITION( "state" , MQI_INTEGER ),
+ MQI_COLUMN_DEFINITION( "grant" , MQI_INTEGER )
+ );
+
+ MQI_INDEX_DEFINITION(indexdef,
+ MQI_INDEX_COLUMN( "rsetid" )
+ );
+
+ static bool initialized = false;
+
+ char name[256];
+ mqi_column_def_t coldefs[MQI_COLUMN_MAX + 1];
+ mqi_column_def_t *col;
+ mrp_attr_def_t *atd;
+ mqi_handle_t table;
+ char c, *p;
+ size_t i,j;
+
+ if (!initialized) {
+ mqi_open();
+ for (i = 0; i < RESOURCE_MAX; i++)
+ resource_user_table[i] = MQI_HANDLE_INVALID;
+ initialized = true;
+ }
+
+ MRP_ASSERT(sizeof(base_coldefs) < sizeof(coldefs),"too many base columns");
+ MRP_ASSERT(rdef, "invalid argument");
+ MRP_ASSERT(rdef->id < RESOURCE_MAX, "confused with data structures");
+ MRP_ASSERT(resource_user_table[rdef->id] == MQI_HANDLE_INVALID,
+ "resource user table already exist");
+
+ snprintf(name, sizeof(name), "%s_users", rdef->name);
+ for (p = name; (c = *p); p++) {
+ if (!isascii(c) || (!isalnum(c) && c != '_'))
+ *p = '_';
+ }
+
+ j = MQI_DIMENSION(base_coldefs) - 1;
+ memcpy(coldefs, base_coldefs, j * sizeof(mqi_column_def_t));
+
+ for (i = 0; i < rdef->nattr && j < MQI_COLUMN_MAX; i++, j++) {
+ col = coldefs + j;
+ atd = rdef->attrdefs + i;
+
+ col->name = atd->name;
+ col->type = atd->type;
+ col->length = (col->type == mqi_string) ? NAME_LENGTH : 0;
+ col->flags = 0;
+ }
+
+ memset(coldefs + j, 0, sizeof(mqi_column_def_t));
+
+ table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
+
+ if (table == MQI_HANDLE_INVALID) {
+ mrp_log_error("Can't create table '%s': %s", name, strerror(errno));
+ return -1;
+ }
+
+ resource_user_table[rdef->id] = table;
+
+ return 0;
+}
+
+static void resource_user_insert(mrp_resource_t *res, bool autorel)
+{
+ mrp_resource_def_t *rdef = res->def;
+ uint32_t i;
+ int n;
+ user_row_t row;
+ user_row_t *rows[2];
+ mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+ MRP_ASSERT(FIRST_ATTRIBUTE_IDX + rdef->nattr <= MQI_COLUMN_MAX,
+ "too many attributes for a table");
+
+ row.rsetid = res->rsetid;
+ row.autorel = autorel;
+ row.grant = 0;
+ row.state = mrp_resource_no_request;
+ memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
+
+ i = 0;
+ cdsc[i].cindex = RSETID_IDX;
+ cdsc[i].offset = MQI_OFFSET(user_row_t, rsetid);
+
+ i++;
+ cdsc[i].cindex = AUTOREL_IDX;
+ cdsc[i].offset = MQI_OFFSET(user_row_t, autorel);
+
+ i++;
+ cdsc[i].cindex = STATE_IDX;
+ cdsc[i].offset = MQI_OFFSET(user_row_t, state);
+
+ i++;
+ cdsc[i].cindex = GRANT_IDX;
+ cdsc[i].offset = MQI_OFFSET(user_row_t, grant);
+
+ set_attr_descriptors(cdsc + (i+1), res);
+
+ rows[0] = &row;
+ rows[1] = NULL;
+
+ if ((n = MQI_INSERT_INTO(resource_user_table[rdef->id], cdsc, rows)) != 1)
+ mrp_log_error("can't insert row into resource user table");
+}
+
+static void resource_user_delete(mrp_resource_t *res)
+{
+ static uint32_t rsetid;
+
+ MQI_WHERE_CLAUSE(where,
+ MQI_EQUAL( MQI_COLUMN(RSETID_IDX), MQI_UNSIGNED_VAR(rsetid) )
+ );
+
+ mrp_resource_def_t *rdef;
+ int n;
+
+ MRP_ASSERT(res, "invalid argument");
+
+ rdef = res->def;
+ rsetid = res->rsetid;
+
+ if ((n = MQI_DELETE(resource_user_table[rdef->id], where)) != 1)
+ mrp_log_error("Could not delete resource user");
+}
+
+void mrp_resource_user_update(mrp_resource_t *res, int state, bool grant)
+{
+ static uint32_t rsetid;
+
+ MQI_WHERE_CLAUSE(where,
+ MQI_EQUAL( MQI_COLUMN(RSETID_IDX), MQI_UNSIGNED_VAR(rsetid) )
+ );
+
+ mrp_resource_def_t *rdef = res->def;
+ uint32_t i;
+ int n;
+ user_row_t row;
+ mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+ rsetid = res->rsetid;
+
+ MRP_ASSERT(1 + rdef->nattr <= MQI_COLUMN_MAX,
+ "too many attributes for a table");
+
+ row.state = state;
+ row.grant = grant;
+ memcpy(row.attrs, res->attrs, rdef->nattr * sizeof(mrp_attr_value_t));
+
+ i = 0;
+ cdsc[i].cindex = STATE_IDX;
+ cdsc[i].offset = MQI_OFFSET(user_row_t, state);
+
+ i++;
+ cdsc[i].cindex = GRANT_IDX;
+ cdsc[i].offset = MQI_OFFSET(user_row_t, grant);
+
+ set_attr_descriptors(cdsc + (i+1), res);
+
+ if ((n = MQI_UPDATE(resource_user_table[rdef->id], cdsc,&row, where)) != 1)
+ mrp_log_error("can't update row in resource user table");
+}
+
+static void set_attr_descriptors(mqi_column_desc_t *cdsc, mrp_resource_t *res)
+{
+ mrp_resource_def_t *rdef = res->def;
+ uint32_t i,j;
+ int o;
+
+ for (i = j = 0; j < rdef->nattr; j++) {
+ switch (rdef->attrdefs[j].type) {
+ case mqi_string: o = MQI_OFFSET(user_row_t,attrs[j].string); break;
+ case mqi_integer: o = MQI_OFFSET(user_row_t,attrs[j].integer); break;
+ case mqi_unsignd: o = MQI_OFFSET(user_row_t,attrs[j].unsignd); break;
+ case mqi_floating: o = MQI_OFFSET(user_row_t,attrs[j].floating);break;
+ default: /* skip this */ continue;
+ }
+
+ cdsc[i].cindex = FIRST_ATTRIBUTE_IDX + j;
+ cdsc[i].offset = o;
+ i++;
+ }
+
+ cdsc[i].cindex = -1;
+ cdsc[i].offset = 1;
+}
+
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_RESOURCE_H__
+#define __MURPHY_RESOURCE_H__
+
+#include <murphy/common/list.h>
+
+#include "attribute.h"
+
+
+struct mrp_resource_def_s {
+ uint32_t id;
+ const char *name;
+ bool shareable;
+ struct {
+ mrp_list_hook_t list;
+ mrp_resource_mgr_ftbl_t *ftbl;
+ void *userdata;
+ } manager;
+ uint32_t nattr;
+ mrp_attr_def_t attrdefs[0];
+};
+
+struct mrp_resource_s {
+ mrp_list_hook_t list;
+ uint32_t rsetid;
+ mrp_resource_def_t *def;
+ bool shared;
+ mrp_attr_value_t attrs[0];
+};
+
+
+
+uint32_t mrp_resource_definition_count(void);
+mrp_resource_def_t *mrp_resource_definition_find_by_name(const char *);
+mrp_resource_def_t *mrp_resource_definition_find_by_id(uint32_t);
+mrp_resource_def_t *mrp_resource_definition_iterate_manager(void **);
+
+
+mrp_resource_t *mrp_resource_create(const char *, uint32_t, bool,
+ bool, mrp_attr_t *);
+void mrp_resource_destroy(mrp_resource_t *);
+
+void mrp_resource_notify(mrp_resource_t *, mrp_resource_set_t *,
+ mrp_resource_event_t);
+
+int mrp_resource_print(mrp_resource_t*, uint32_t,
+ size_t, char *, int);
+int mrp_resource_attribute_print(mrp_resource_t *, char *,int);
+
+void mrp_resource_user_update(mrp_resource_t *, int, bool);
+
+#endif /* __MURPHY_RESOURCE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include <murphy/common/mm.h>
+#include <murphy/common/log.h>
+
+#include <murphy/resource/manager-api.h>
+#include <murphy/resource/client-api.h>
+#include <murphy/resource/config-api.h>
+
+#include <murphy-db/mqi.h>
+
+#include "zone.h"
+
+
+#define ATTRIBUTE_MAX 32
+#define NAME_LENGTH 24
+
+#define VALID_TYPE(t) ((t) == mqi_string || \
+ (t) == mqi_integer || \
+ (t) == mqi_unsignd || \
+ (t) == mqi_floating )
+
+
+#define ZONE_ID_IDX 0
+#define ZONE_NAME_IDX 1
+#define FIRST_ATTRIBUTE_IDX 2
+
+typedef struct {
+ uint32_t zone_id;
+ const char *zone_name;
+ mrp_attr_value_t attrs[MQI_COLUMN_MAX];
+} zone_row_t;
+
+static mrp_zone_def_t *zone_def;
+static uint32_t zone_count;
+static mrp_zone_t *zone_table[MRP_ZONE_MAX];
+static mqi_handle_t db_table = MQI_HANDLE_INVALID;
+
+static mqi_handle_t create_zone_table(mrp_zone_def_t *);
+static void insert_into_zone_table(mrp_zone_t *);
+static void set_attr_descriptors(mqi_column_desc_t *);
+
+
+int mrp_zone_definition_create(mrp_attr_def_t *attrdefs)
+{
+ uint32_t nattr;
+ size_t size;
+ mrp_zone_def_t *def;
+
+ for (nattr = 0; attrdefs && attrdefs[nattr].name; nattr++)
+ ;
+
+ size = sizeof(mrp_zone_def_t) + sizeof(mrp_attr_def_t) * nattr;
+
+ if (!(def = mrp_allocz(size))) {
+ mrp_log_error("Memory alloc failure. Can't create zone definition");
+ return -1;
+ }
+
+ def->nattr = nattr;
+ zone_def = def;
+
+ if (mrp_attribute_copy_definitions(attrdefs, def->attrdefs) < 0)
+ return -1;
+
+ db_table = create_zone_table(def);
+
+ return 0;
+}
+
+uint32_t mrp_zone_count(void)
+{
+ return zone_count;
+}
+
+uint32_t mrp_zone_create(const char *name, mrp_attr_t *attrs)
+{
+ size_t size;
+ mrp_zone_t *zone;
+ const char *dup_name;
+ int sts;
+
+ MRP_ASSERT(name, "invalid argument");
+
+ if (!zone_def) {
+ mrp_log_error("Zone definition must preceed zone creation. "
+ "can't create zone '%s'", name);
+ return MRP_ZONE_ID_INVALID;
+ }
+
+ if (zone_count >= MRP_ZONE_MAX) {
+ mrp_log_error("Zone table overflow. Can't create zone '%s'", name);
+ return MRP_ZONE_ID_INVALID;
+ }
+
+ size = sizeof(mrp_zone_t) + sizeof(mrp_attr_def_t) * zone_def->nattr;
+
+ if (!(zone = mrp_allocz(size)) || !(dup_name = mrp_strdup(name))) {
+ mrp_log_error("Memory alloc failure. Can't create zone '%s'", name);
+ return MRP_ZONE_ID_INVALID;
+ }
+
+
+ zone->id = zone_count++;
+ zone->name = dup_name;
+
+ sts = mrp_attribute_set_values(attrs, zone_def->nattr,
+ zone_def->attrdefs, zone->attrs);
+ if (sts < 0) {
+ mrp_log_error("Memory alloc failure. Can't create zone '%s'", name);
+ return MRP_ZONE_ID_INVALID;
+ }
+
+ insert_into_zone_table(zone);
+
+ zone_table[zone->id] = zone;
+
+ return zone->id;
+}
+
+mrp_zone_t *mrp_zone_find_by_id(uint32_t id)
+{
+ if (id < zone_count)
+ return zone_table[id];
+
+ return NULL;
+}
+
+mrp_zone_t *mrp_zone_find_by_name(const char *name)
+{
+ mrp_zone_t *zone;
+ uint32_t id;
+
+ for (id = 0; id < zone_count; id++) {
+ zone = zone_table[id];
+
+ if (!strcasecmp(name, zone->name))
+ return zone;
+ }
+
+ return NULL;
+}
+
+uint32_t mrp_zone_get_id(mrp_zone_t *zone)
+{
+ if (!zone)
+ return MRP_ZONE_ID_INVALID;
+
+ return zone->id;
+}
+
+const char *mrp_zone_get_name(mrp_zone_t *zone)
+{
+ if (!zone || !zone->name)
+ return "<unknown zone>";
+
+ return zone->name;
+}
+
+const char **mrp_zone_get_all_names(uint32_t buflen, const char **buf)
+{
+ uint32_t i;
+
+ MRP_ASSERT(!buf || (buf && buflen > 1), "invlaid argument");
+
+ if (buf) {
+ if (buflen < zone_count + 1)
+ return NULL;
+ }
+ else {
+ buflen = zone_count + 1;
+ if (!(buf = mrp_allocz(sizeof(const char *) * buflen))) {
+ mrp_log_error("Memory alloc failure. Can't get all zone names");
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < zone_count; i++)
+ buf[i] = zone_table[i]->name;
+
+ buf[i] = NULL;
+
+ return buf;
+}
+
+
+mrp_attr_t *mrp_zone_read_attribute(mrp_zone_t *zone,
+ uint32_t idx,
+ mrp_attr_t *value)
+{
+ mrp_attr_t *retval;
+
+ MRP_ASSERT(zone, "invalid argument");
+ MRP_ASSERT(zone_def, "no zone definition");
+
+ retval = mrp_attribute_get_value(idx, value, zone_def->nattr,
+ zone_def->attrdefs, zone->attrs);
+
+ if (!retval) {
+ mrp_log_error("Memory alloc failure. Can't get "
+ "zone '%s' attribute %u", zone->name, idx);
+ }
+
+ return retval;
+}
+
+mrp_attr_t *mrp_zone_read_all_attributes(mrp_zone_t *zone,
+ uint32_t nvalue,
+ mrp_attr_t *values)
+{
+ mrp_attr_t *retval;
+
+ MRP_ASSERT(zone, "invalid argument");
+
+ retval = mrp_attribute_get_all_values(nvalue, values, zone_def->nattr,
+ zone_def->attrdefs, zone->attrs);
+
+ if (!retval) {
+ mrp_log_error("Memory alloc failure. Can't get all"
+ "attributes of zone '%s'", zone->name);
+ }
+
+ return retval;
+}
+
+int mrp_zone_attribute_print(mrp_zone_t *zone, char *buf, int len)
+{
+ if (len <= 0)
+ return 0;
+
+ MRP_ASSERT(zone && buf, "invalid argument");
+
+ return mrp_attribute_print(zone_def->nattr, zone_def->attrdefs,
+ zone->attrs, buf,len);
+}
+
+
+static mqi_handle_t create_zone_table(mrp_zone_def_t *zdef)
+{
+ MQI_COLUMN_DEFINITION_LIST(base_coldefs,
+ MQI_COLUMN_DEFINITION( "zone_id" , MQI_UNSIGNED ),
+ MQI_COLUMN_DEFINITION( "zone_name" , MQI_VARCHAR(NAME_LENGTH) )
+ );
+
+ MQI_INDEX_DEFINITION(indexdef,
+ MQI_INDEX_COLUMN("zone_id")
+ );
+
+ char *name;
+ mqi_column_def_t coldefs[MQI_COLUMN_MAX + 1];
+ mqi_column_def_t *col;
+ mrp_attr_def_t *atd;
+ mqi_handle_t table;
+ size_t i,j;
+
+ MRP_ASSERT(zdef, "invalid argument");
+ MRP_ASSERT(zdef->nattr < MQI_COLUMN_MAX, "too many zone attributes");
+ MRP_ASSERT(db_table == MQI_HANDLE_INVALID,
+ "multiple definition of zone data table");
+
+ mqi_open();
+
+ name = "zones";
+
+ j = MQI_DIMENSION(base_coldefs) - 1;
+ memcpy(coldefs, base_coldefs, j * sizeof(mqi_column_def_t));
+
+ for (i = 0; i < zdef->nattr && j < MQI_COLUMN_MAX; i++, j++) {
+ col = coldefs + j;
+ atd = zdef->attrdefs + i;
+
+ col->name = atd->name;
+ col->type = atd->type;
+ col->length = (col->type == mqi_string) ? NAME_LENGTH : 0;
+ col->flags = 0;
+ }
+
+ memset(coldefs + j, 0, sizeof(mqi_column_def_t));
+
+ table = MQI_CREATE_TABLE(name, MQI_TEMPORARY, coldefs, indexdef);
+
+ if (table == MQI_HANDLE_INVALID)
+ mrp_log_error("Can't create table '%s': %s", name, strerror(errno));
+
+ return table;
+}
+
+static void insert_into_zone_table(mrp_zone_t *zone)
+{
+ uint32_t i;
+ int n;
+ zone_row_t row;
+ zone_row_t *rows[2];
+ mqi_column_desc_t cdsc[FIRST_ATTRIBUTE_IDX + MQI_COLUMN_MAX + 1];
+
+ MRP_ASSERT(zone_def, "no zone definition");
+ MRP_ASSERT(db_table != MQI_HANDLE_INVALID, "no zone table");
+ MRP_ASSERT(FIRST_ATTRIBUTE_IDX + zone_def->nattr <= MQI_COLUMN_MAX,
+ "too many attributes for a table");
+
+ row.zone_id = zone->id;
+ row.zone_name = zone->name;
+ memcpy(row.attrs, zone->attrs, zone_def->nattr * sizeof(mrp_attr_value_t));
+
+ i = 0;
+ cdsc[i].cindex = ZONE_ID_IDX;
+ cdsc[i].offset = MQI_OFFSET(zone_row_t, zone_id);
+
+ i++;
+ cdsc[i].cindex = ZONE_NAME_IDX;
+ cdsc[i].offset = MQI_OFFSET(zone_row_t, zone_name);
+
+ set_attr_descriptors(cdsc + (i+1));
+
+ rows[0] = &row;
+ rows[1] = NULL;
+
+ if ((n = MQI_INSERT_INTO(db_table, cdsc, rows)) != 1)
+ mrp_log_error("can't insert row into zone table");
+}
+
+static void set_attr_descriptors(mqi_column_desc_t *cdsc)
+{
+ uint32_t i,j;
+ int o;
+
+ for (i = j = 0; j < zone_def->nattr; j++) {
+ switch (zone_def->attrdefs[j].type) {
+ case mqi_string: o = MQI_OFFSET(zone_row_t,attrs[j].string); break;
+ case mqi_integer: o = MQI_OFFSET(zone_row_t,attrs[j].integer); break;
+ case mqi_unsignd: o = MQI_OFFSET(zone_row_t,attrs[j].unsignd); break;
+ case mqi_floating: o = MQI_OFFSET(zone_row_t,attrs[j].floating); break;
+ default: /* skip this */ continue;
+ }
+
+ cdsc[i].cindex = FIRST_ATTRIBUTE_IDX + j;
+ cdsc[i].offset = o;
+ i++;
+ }
+
+ cdsc[i].cindex = -1;
+ cdsc[i].offset = 1;
+}
+
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_ZONE_H__
+#define __MURPHY_ZONE_H__
+
+#include "attribute.h"
+
+struct mrp_zone_def_s {
+ uint32_t nattr;
+ mrp_attr_def_t attrdefs[0];
+};
+
+struct mrp_zone_s {
+ uint32_t id;
+ const char *name;
+ mrp_attr_value_t attrs[0];
+};
+
+
+uint32_t mrp_zone_count(void);
+mrp_zone_t *mrp_zone_find_by_id(uint32_t);
+mrp_zone_t *mrp_zone_find_by_name(const char *);
+
+int mrp_zone_attribute_print(mrp_zone_t *, char *, int);
+
+
+#endif /* __MURPHY_ZONE_H__ */
+
+/*
+ * Local Variables:
+ * c-basic-offset: 4
+ * indent-tabs-mode: nil
+ * End:
+ *
+ */
--- /dev/null
+AM_CFLAGS = $(WARNING_CFLAGS) -I$(top_builddir)
+noinst_PROGRAMS = collect-symbols
+
+collect_symbols_SOURCES = collect-symbols.c
+collect_symbols_CFLAGS = $(AM_CFLAGS)
+collect_symbols_LDADD =
+
+collect-symbols$(EXEEXT): $(collect_symbols_SOURCES)
+ $(CC_FOR_BUILD) $(collect_symbols_CFLAGS) -o $@ $< \
+ $(collect_symbols_LDADD)
--- /dev/null
+/*
+ * Copyright (c) 2012, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <unistd.h>
+#include <regex.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#define RD 0
+#define WR 1
+
+typedef enum {
+ TOKEN_ERROR = -1,
+ TOKEN_LINEMARKER, /* a preprocessor line marker */
+ TOKEN_BLOCK, /* a block enclosed in {}/()/[] */
+ TOKEN_WORD, /* a word */
+ TOKEN_DQUOTED, /* a double-quoted sequence */
+ TOKEN_SQUOTED, /* a single-quoted sequence */
+ TOKEN_ASSIGN, /* '=' */
+ TOKEN_SEMICOLON, /* ';' */
+ TOKEN_COLON, /* ',' */
+ TOKEN_OTHER, /* any other token */
+} token_type_t;
+
+
+typedef struct {
+ token_type_t type; /* token type */
+ char *value; /* token value */
+} token_t;
+
+
+#define READBUF_SIZE ( 8 * 1024)
+#define RINGBUF_SIZE (16 * 1024)
+#define MAX_TOKEN (512)
+#define MAX_TOKENS (64)
+
+typedef struct {
+ int fd; /* file descriptor to read */
+ char buf[READBUF_SIZE]; /* data buffer */
+ int len; /* amount of data in buffer */
+ int rd; /* data buffer read offset */
+ int nxt; /* pushed back data if non-zero */
+} input_t;
+
+typedef struct {
+ char buf[RINGBUF_SIZE]; /* data buffer */
+ int wr; /* write offset */
+} ringbuf_t;
+
+typedef struct {
+ char *preproc; /* preprocessor to use */
+ char *pattern; /* symbol pattern */
+ char **files; /* files to parse for symbols */
+ int nfile; /* number of files */
+ char *cflags; /* compiler flags */
+ char *output; /* output path */
+ int gnuld; /* generate GNU ld script */
+ int verbose; /* verbosity */
+} config_t;
+
+typedef struct {
+ char **syms;
+ int nsym;
+} symtab_t;
+
+
+static int verbosity = 1;
+
+
+static void fatal_error(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+
+static void verbose_message(int level, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (verbosity >= level) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+}
+
+
+static char *unshave(char *cmd)
+{
+#define SHAVE "shave cc "
+ char *shave;
+
+ shave = strstr(cmd, SHAVE);
+
+ if (shave == NULL)
+ return cmd;
+ else
+ return shave + sizeof(SHAVE) - 1;
+#undef SHAVE
+}
+
+
+static void print_usage(const char *argv0, int exit_code, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fmt && *fmt) {
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+ }
+
+ printf("usage: %s [options]\n\n"
+ "The possible options are:\n"
+ " -P --preproc <preprocessor> preprocessor to use [gcc]\n"
+ " -c, --compiler-flags <flags> flags to pass to compiler\n"
+ " -p, --pattern <pattern> symbol regexp pattern\n"
+ " -o, --output <path> write output to the given file\n"
+ " -g, --gnu-ld <script> generate GNU ld linker script\n"
+ " -v, --verbose increase verbosity\n"
+ " -q, --quiet decrease verbosity\n"
+ " -h, --help show this help on usage\n",
+ argv0);
+
+ if (exit_code < 0)
+ return;
+ else
+ exit(exit_code);
+}
+
+
+static void set_defaults(config_t *c)
+{
+ memset(c, 0, sizeof(*c));
+ c->preproc = "gcc";
+ c->pattern = "^mrp_|^_mrp";
+}
+
+
+static void parse_cmdline(config_t *cfg, int argc, char **argv)
+{
+# define OPTIONS "P:c:p:o:gvqh"
+ struct option options[] = {
+ { "preprocessor" , required_argument, NULL, 'P' },
+ { "compiler-flags", required_argument, NULL, 'c' },
+ { "pattern" , required_argument, NULL, 'p' },
+ { "output" , required_argument, NULL, 'o' },
+ { "gnu-ld" , no_argument , NULL, 'g' },
+ { "verbose" , no_argument , NULL, 'v' },
+ { "quiet" , no_argument , NULL, 'q' },
+ { "help" , no_argument , NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+ };
+
+ int opt;
+
+ set_defaults(cfg);
+
+ while ((opt = getopt_long(argc, argv, OPTIONS, options, NULL)) != -1) {
+ switch (opt) {
+ case 'P':
+ cfg->preproc = unshave(optarg);
+ break;
+
+ case 'c':
+ cfg->cflags = optarg;
+ break;
+
+ case 'p':
+ cfg->pattern = optarg;
+ break;
+
+ case 'o':
+ cfg->output = optarg;
+ break;
+
+ case 'g':
+ cfg->gnuld = 1;
+ break;
+
+ case 'v':
+ verbosity++;
+ break;
+
+ case 'q':
+ verbosity--;
+ break;
+
+ case 'h':
+ print_usage(argv[0], -1, "");
+ exit(0);
+ break;
+
+ default:
+ print_usage(argv[0], EINVAL, "invalid option '%s'\n",
+ argv[optind]);
+ }
+ }
+
+ cfg->files = argv + optind;
+ cfg->nfile = argc - optind;
+}
+
+
+static int preprocess_file(const char *preproc, const char *file,
+ const char *cflags, pid_t *pid)
+{
+ char cmd[4096], *argv[32];
+ int fd[2], argc, i;
+
+ /*
+ * preprocess the given file
+ *
+ * Fork off a process for preprocessing the given file with the
+ * configured compiler flags. Return the reading end of the pipe
+ * the preprocessor is writing to.
+ */
+
+ if (pipe(fd) != 0)
+ fatal_error("failed to create pipe (%d: %s).", errno, strerror(errno));
+
+ *pid = fork();
+
+ switch (*pid) {
+ case -1:
+ fatal_error("failed to for preprocessor (%d: %s).",
+ errno, strerror(errno));
+ break;
+
+ case 0: /* child: exec preprocessor */
+ close(fd[RD]);
+
+ /*
+ * Notes:
+ * Currently we execute the preprocessor by starting a shell
+ * and feeding it our constructed preprocessor command using
+ * the '-c' option. If we need to pass options to the pre-
+ * processor we need to protect those from expansion by the
+ * intermediate shell. This causes some level of pain if we
+ * also have a script that gets its arguments somewhere else,
+ * eg. from a Makefile, and passes those forward to us. This
+ * is exactly how we are executed during Murphy builds.
+ *
+ * To reduce the pain perhaps we should leave the shell out,
+ * search $PATH ourselves for the preprocessor and just exec
+ * it directly here.
+ */
+
+ argc = 0;
+ argv[argc++] = "/bin/sh";
+ argv[argc++] = "-c";
+
+ if (cflags != NULL)
+ snprintf(cmd, sizeof(cmd), "%s %s -E %s", preproc, cflags, file);
+ else
+ snprintf(cmd, sizeof(cmd), "%s -E %s", preproc, file);
+
+ argv[argc++] = cmd;
+ argv[argc] = NULL;
+
+ for (i = 0; i < argc; i++) {
+ verbose_message(3, "shell arg #%d: '%s'\n", i, argv[i]);
+ }
+
+ if (dup2(fd[WR], fileno(stdout)) < 0)
+ fatal_error("failed to redirect stdout (%d: %s)",
+ errno, strerror(errno));
+
+ if (execv("/bin/sh", argv) != 0)
+ fatal_error("failed to exec command '%s' (%d: %s)", cmd,
+ errno, strerror(errno));
+ break;
+
+ default: /* parent: return fd to read preprocessed data from */
+ close(fd[WR]);
+ return fd[RD];
+ }
+
+ return -1; /* never reached */
+}
+
+
+static void input_init(input_t *in, int fd)
+{
+ memset(in, 0, sizeof(*in));
+
+ in->fd = fd;
+}
+
+
+static char input_read(input_t *in)
+{
+ char ch;
+
+ /*
+ * read the next input character
+ *
+ * If there is an pushed back character deliver (and clear) than one.
+ * Otherwise refill the input buffer if needed and return the next
+ * character from it.
+ */
+
+ if (in->nxt != 0) {
+ ch = in->nxt;
+ in->nxt = 0;
+ }
+ else {
+ if (in->len <= in->rd) {
+ in->len = read(in->fd, in->buf, sizeof(in->buf));
+
+ if (in->len > 0) {
+ in->rd = 1;
+ ch = in->buf[0];
+ }
+ else
+ ch = 0;
+ }
+ else
+ return ch = in->buf[in->rd++];
+ }
+
+ return ch;
+}
+
+
+static int input_pushback(input_t *in, char ch)
+{
+ /*
+ * push back a character to the input stream
+ *
+ * Note that you can only push back a single character. Trying to
+ * push back more than one will fail with an error.
+ */
+
+ if (in->nxt == 0) {
+ in->nxt = ch;
+
+ return 0;
+ }
+ else {
+ errno = EBUSY;
+
+ return -1;
+ }
+}
+
+
+static void input_discard_whitespace(input_t *in)
+{
+ char ch;
+
+ /*
+ * discard consecutive whitespace (including newline)
+ */
+
+ while ((ch = input_read(in)) == ' ' || ch == '\t' || ch == '\n')
+ ;
+
+ input_pushback(in, ch);
+}
+
+
+#if 0
+static void input_discard_line(input_t *in)
+{
+ int ch;
+
+ /*
+ * discard input till a newline
+ */
+
+ while ((ch = input_read(in)) != '\n' && ch != 0)
+ ;
+}
+#endif
+
+
+static int input_discard_quoted(input_t *in, char quote)
+{
+ char ch;
+
+ /*
+ * discard a block of quoted input
+ */
+
+ while ((ch = input_read(in)) != quote && ch != 0) {
+ if (ch == '\\')
+ input_read(in);
+ }
+
+ if (ch != quote) {
+ errno = EINVAL;
+ return -1;
+ }
+ else
+ return 0;
+}
+
+
+static int input_discard_block(input_t *in, char beg)
+{
+ char end, ch, quote;
+ int level;
+
+ /*
+ * discard a block enclosed in {}, [], or ()
+ */
+
+ switch (beg) {
+ case '{': end = '}'; break;
+ case '[': end = ']'; break;
+ case '(': end = ')'; break;
+ default: return 0;
+ }
+
+ level = 1;
+ while (level > 0) {
+ switch ((ch = input_read(in))) {
+ case '"':
+ case '\'':
+ quote = ch;
+ if (input_discard_quoted(in, quote) != 0)
+ return -1;
+ break;
+
+ default:
+ if (ch == end)
+ level--;
+ else if (ch == beg)
+ level++;
+ }
+ }
+
+ if (level == 0)
+ return 0;
+ else {
+ errno = EINVAL;
+ return -1;
+ }
+}
+
+
+static void ringbuf_init(ringbuf_t *rb)
+{
+ memset(rb->buf, 0, sizeof(rb->buf));
+ rb->wr = 0;
+}
+
+
+static char *ringbuf_save(ringbuf_t *rb, char *token, int len)
+{
+ char *t, *s, *d;
+ int n, o, i;
+
+ /*
+ * save the given token in the token ring buffer
+ */
+
+ verbose_message(2, "saving '%s'...\n", token);
+
+ if (len < 0)
+ len = strlen(token);
+
+ n = sizeof(rb->buf) - 1 - rb->wr;
+
+ if (n < len + 1) {
+ t = rb->buf;
+ n = sizeof(rb->buf) - 1;
+ o = 0;
+ }
+ else {
+ t = rb->buf + rb->wr;
+ o = rb->wr;
+ }
+
+ if (n >= len + 1) {
+ s = token;
+ d = t;
+
+ for (i = 0; i < len; i++, o++)
+ *d++ = *s++;
+
+ *d = '\0';
+ rb->wr = o + 1;
+
+ return t;
+ }
+ else {
+ errno = ENOSPC;
+ return NULL;
+ }
+}
+
+
+static char *input_collect_word(input_t *in, ringbuf_t *rb)
+{
+#define WORD_CHAR(c) \
+ (('a' <= (c) && (c) <= 'z') || \
+ ('A' <= (c) && (c) <= 'Z') || \
+ ('0' <= (c) && (c) <= '9') || \
+ ((c) == '_' || (c) == '$'))
+
+ char buf[MAX_TOKEN], ch;
+ int n;
+
+ /*
+ * collect and save the next word (consecutive sequence) of input
+ */
+
+ for (n = 0; n < (int)sizeof(buf) - 1; n++) {
+ ch = input_read(in);
+
+ if (WORD_CHAR(ch))
+ buf[n] = ch;
+ else {
+ buf[n] = '\0';
+ input_pushback(in, ch);
+
+ return ringbuf_save(rb, buf, n);
+ }
+ }
+
+ errno = ENOSPC;
+ return NULL;
+}
+
+
+static char *input_parse_linemarker(input_t *in, char *buf, size_t size)
+{
+ char ch;
+ int i;
+
+ while((ch = input_read(in)) != '"' && ch != '\n' && ch)
+ ;
+
+ if (ch != '"')
+ return NULL;
+
+ for (i = 0; i < (int)size - 1; i++) {
+ buf[i] = ch = input_read(in);
+
+ if (ch == '"') {
+ buf[i] = '\0';
+
+ while ((ch = input_read(in)) != '\n' && ch)
+ ;
+
+ return buf;
+ }
+ }
+
+ return NULL;
+}
+
+
+static int same_file(const char *path1, const char *path2)
+{
+ struct stat st1, st2;
+
+ if (stat(path1, &st1) != 0 || stat(path2, &st2) != 0)
+ return 0;
+ else
+ return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
+}
+
+
+static int collect_tokens(input_t *in, ringbuf_t *rb, token_t *tokens,
+ int ntoken)
+{
+ char ch, *v, path[1024];
+ int n, has_paren;
+
+ /*
+ * collect a sequence of tokens that forms (or looks like) a logical unit
+ */
+
+ n = 0;
+ has_paren = 0;
+ while (n < ntoken) {
+ switch ((ch = input_read(in))) {
+ /* always treat a semicolon here as a sequence terminator */
+ case ';':
+ tokens[n].type = TOKEN_SEMICOLON;
+ tokens[n].value = ringbuf_save(rb, ";", 1);
+ return n + 1;
+
+ /* extract path name from preprocessor line-markers */
+ case '#':
+ v = input_parse_linemarker(in, path, sizeof(path));
+ if (v != NULL) {
+ tokens[n].type = TOKEN_LINEMARKER;
+ tokens[n].value = ringbuf_save(rb, v, -1);
+ if (n == 0)
+ return n + 1;
+ else
+ return -1;
+ }
+ break;
+
+ /* discard whitespace (including trailing newlines) */
+ case ' ':
+ case '\t':
+ input_discard_whitespace(in);
+ break;
+
+ /* ignore newlines */
+ case '\n':
+ break;
+
+ /* collate/collapse blocks to a block indicator token */
+ case '{':
+ case '(':
+ case '[':
+ if (input_discard_block(in, ch) != 0)
+ return -1;
+ else {
+ /* filter out __attribute__ ((.*)) token pairs */
+ if (ch == '(' && n > 0 &&
+ tokens[n-1].type == TOKEN_WORD &&
+ !strcmp(tokens[n-1].value, "__attribute__")) {
+ n--;
+ verbose_message(2, "filtered __attribute__...\n");
+ continue;
+ }
+
+ v = (ch == '{' ? "{" : (ch == '[' ? "[" : "("));
+ tokens[n].type = TOKEN_BLOCK;
+ tokens[n].value = ringbuf_save(rb, v, 1);
+ n++;
+
+ if (v[0] == '(')
+ has_paren = 1;
+ else {
+ /*
+ * if this sequence includes both '(...)' and '{...}'
+ * we assume this to be a function definition so we
+ * don't wait for a semicolon but terminate sequence
+ * here
+ */
+ if (v[0] == '{')
+ if (has_paren)
+ return n;
+ }
+ }
+ break;
+
+ /* end of file terminates the current sequence */
+ case 0:
+ return n;
+
+ /* collect and save the next word */
+ case 'a'...'z':
+ case 'A'...'Z':
+ case '_':
+ case '$':
+ case '0'...'9':
+ input_pushback(in, ch);
+ v = input_collect_word(in, rb);
+
+ if (v != NULL) {
+ if (!strcmp(v, "__extension__"))
+ break;
+ tokens[n].type = TOKEN_WORD;
+ tokens[n].value = v;
+ n++;
+ }
+ else
+ return -1;
+ break;
+
+ case '=':
+ tokens[n].type = TOKEN_ASSIGN;
+ tokens[n].value = ringbuf_save(rb, "=", 1);
+ n++;
+ break;
+
+ /* ignore asterisks */
+ case '*':
+ break;
+
+ /* the rest we print for debugging */
+ default:
+ printf("%c", ch);
+ }
+ }
+
+ errno = EOVERFLOW;
+ return -1;
+}
+
+
+static char *symbol_from_tokens(token_t *tokens, int ntoken)
+{
+#define MATCHING_TOKEN(_n, _type, _val) \
+ (tokens[(_n)].type == TOKEN_##_type && \
+ (!*_val || !strcmp(_val, tokens[(_n)].value)))
+
+ int last, has_paren, has_curly, has_bracket, has_assign;
+ int i;
+
+ /*
+ * extract the symbol from a sequence of tokens
+ */
+
+ if (verbosity > 2) {
+ for (i = 0; i < ntoken; i++)
+ verbose_message(3, "0x%x: '%s'\n", tokens[i].type, tokens[i].value);
+ verbose_message(3, "--\n");
+ }
+
+ has_paren = has_curly = has_bracket = has_assign = 0;
+ for (i = 0; i < ntoken; i++) {
+ if (MATCHING_TOKEN(i, BLOCK , "(")) has_paren = 1;
+ else if (MATCHING_TOKEN(i, BLOCK , "{")) has_curly = 1;
+ else if (MATCHING_TOKEN(i, BLOCK , "[")) has_bracket = 1;
+ else if (MATCHING_TOKEN(i, ASSIGN, "" )) has_assign = 1 + i;
+ }
+
+ last = ntoken - 1;
+
+ if (tokens[0].type != TOKEN_WORD) {
+ verbose_message(2, "ignoring sequence starting with non-word\n");
+ return NULL;
+ }
+
+ /* ignore typedefs and everything static */
+ if (MATCHING_TOKEN(0, WORD, "typedef") ||
+ MATCHING_TOKEN(0, WORD, "static")) {
+ verbose_message(2, "ignoring typedef or static sequence\n");
+ return NULL;
+ }
+
+ /* ignore forward declarations */
+ if (ntoken == 3 &&
+ (MATCHING_TOKEN(0, WORD, "struct") ||
+ MATCHING_TOKEN(0, WORD, "union" ) ||
+ MATCHING_TOKEN(0, WORD, "enum" )) &&
+ MATCHING_TOKEN(1, WORD, "") &&
+ MATCHING_TOKEN(2, SEMICOLON, "")) {
+ verbose_message(2, "ignoring forward declaration sequence\n");
+ return NULL;
+ }
+
+ /* take care of function prototypes */
+ if (last > 2) {
+ if (MATCHING_TOKEN(last , SEMICOLON, "" ) &&
+ MATCHING_TOKEN(last-1, BLOCK , "(") &&
+ MATCHING_TOKEN(last-2, WORD , "" ))
+ return tokens[last-2].value;
+ }
+
+ /* take care of global variables with assignments */
+ if (last > 1 && has_assign) {
+ i = has_assign - 1;
+ if (i > 0 && MATCHING_TOKEN(i-1, WORD, ""))
+ return tokens[i-1].value;
+ if (i > 1 &&
+ MATCHING_TOKEN(i-1, BLOCK, "[") &&
+ MATCHING_TOKEN(i-2, WORD , ""))
+ return tokens[i-2].value;
+ }
+
+ /* take care of global variables */
+ if (last > 1 && !has_paren && !has_curly) {
+ if (MATCHING_TOKEN(last , SEMICOLON, "") &&
+ MATCHING_TOKEN(last-1, WORD , ""))
+ return tokens[last-1].value;
+ }
+
+ verbose_message(2, "ignoring other non-matching token sequence\n");
+
+ return NULL;
+}
+
+
+static void symtab_init(symtab_t *st)
+{
+ st->syms = NULL;
+ st->nsym = 0;
+}
+
+
+static void symtab_add(symtab_t *st, char *sym)
+{
+ int i;
+
+ for (i = 0; i < st->nsym; i++)
+ if (!strcmp(st->syms[i], sym))
+ return;
+
+ st->syms = realloc(st->syms, (st->nsym + 1) * sizeof(st->syms[0]));
+
+ if (st->syms != NULL) {
+ st->syms[st->nsym] = strdup(sym);
+
+ if (st->syms[st->nsym] != NULL) {
+ st->nsym++;
+
+ return;
+ }
+
+ fatal_error("failed to save symbol '%s'", sym);
+ }
+
+ fatal_error("failed to allocate new symbol table entry");
+}
+
+
+static void symtab_reset(symtab_t *st)
+{
+ int i;
+
+ for (i = 0; i < st->nsym; i++)
+ free(st->syms[i]);
+
+ free(st->syms);
+
+ st->syms = NULL;
+ st->nsym = 0;
+}
+
+
+static void symtab_dump(symtab_t *st, int gnuld, FILE *out)
+{
+ int i;
+
+ if (!gnuld) {
+ for (i = 0; i < st->nsym; i++)
+ fprintf(out, "%s\n", st->syms[i]);
+ }
+ else {
+ fprintf(out, "{\n");
+ if (st->nsym > 0) {
+ fprintf(out, " global:\n");
+ for (i = 0; i < st->nsym; i++)
+ fprintf(out, " %s;\n", st->syms[i]);
+ }
+ fprintf(out, " local:\n");
+ fprintf(out, " *;\n");
+ fprintf(out, "};\n");
+ }
+}
+
+
+static void extract_symbols(const char *preproc, const char *path,
+ const char *cflags, symtab_t *st, regex_t *re)
+{
+ input_t in;
+ ringbuf_t rb;
+ int fd;
+ pid_t pp_pid;
+ token_t tokens[MAX_TOKENS];
+ int ntoken;
+ char *sym;
+ int pp_status, foreign;
+
+ fd = preprocess_file(preproc, path, cflags, &pp_pid);
+
+ input_init(&in, fd);
+ ringbuf_init(&rb);
+ foreign = 0;
+
+ while ((ntoken = collect_tokens(&in, &rb, tokens, MAX_TOKENS)) > 0) {
+ if (tokens[0].type == TOKEN_LINEMARKER) {
+ foreign = !same_file(path, tokens[0].value);
+
+ verbose_message(2, "input switched to %s file '%s'...\n",
+ foreign ? "foreign" : "input", tokens[0].value);
+
+ continue;
+ }
+
+ if (foreign) {
+ verbose_message(2, "ignoring token stream from foreign file...\n");
+ continue;
+ }
+
+ sym = symbol_from_tokens(tokens, ntoken);
+
+ if (sym != NULL) {
+ if (re == NULL || regexec(re, sym, 0, NULL, 0) == 0)
+ symtab_add(st, sym);
+ else
+ verbose_message(2, "filtered non-matching '%s'...\n", sym);
+ }
+ }
+
+ close(fd);
+ waitpid(pp_pid, &pp_status, 0);
+
+ if (WIFEXITED(pp_status) && WEXITSTATUS(pp_status) != 0)
+ fatal_error("preprocessing of '%s' failed\n", path);
+}
+
+
+int main(int argc, char *argv[])
+{
+ config_t cfg;
+ symtab_t st;
+ regex_t rebuf, *re;
+ char regerr[1024];
+ FILE *out;
+ int err, i;
+
+ if (getenv("__COLLECT_SYMBOLS_DEBUG") != NULL) {
+ verbosity = 3;
+ for (i = 0; i < argc; i++) {
+ verbose_message(0, "argv[%d]: '%s'\n", i, argv[i]);
+ }
+ }
+
+ symtab_init(&st);
+ parse_cmdline(&cfg, argc, argv);
+
+ verbose_message(1, "using preprocessor '%s', cflags '%s'\n", cfg.preproc,
+ cfg.cflags ? cfg.cflags : "");
+
+ if (cfg.pattern != NULL) {
+ err = regcomp(&rebuf, cfg.pattern, REG_EXTENDED);
+
+ if (err != 0) {
+ regerror(err, &rebuf, regerr, sizeof(regerr));
+ fatal_error("invalid pattern '%s' (error: %s)\n", cfg.pattern,
+ regerr);
+ }
+
+ re = &rebuf;
+ }
+ else
+ re = NULL;
+
+ for (i = 0; i < cfg.nfile; i++)
+ extract_symbols(cfg.preproc, cfg.files[i], cfg.cflags, &st, re);
+
+ if (cfg.output != NULL) {
+ out = fopen(cfg.output, "w");
+
+ if (out == NULL)
+ fatal_error("failed to open '%s' (%d: %s)", cfg.output,
+ errno, strerror(errno));
+ }
+ else
+ out = stdout;
+
+ symtab_dump(&st, cfg.gnuld, out);
+
+ if (re != NULL)
+ regfree(re);
+
+ symtab_reset(&st);
+
+ if (out != stdout)
+ fclose(out);
+
+ return 0;
+}