author | Sebastian Harl <sh@tokkee.org> | |
Wed, 5 Dec 2012 15:10:48 +0000 (16:10 +0100) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Wed, 5 Dec 2012 15:10:48 +0000 (16:10 +0100) |
This is the initial version of 'syscollector', a system management / inventory
collection service. The current version provides a configurable plugin
infrastructure to query host / service objects.
The system is made up of the following components:
- libsyscollector: the library providing the core functionality to manage
plugins and the data store
* core/object: base "class" for objects being stored in any part of the
library; an object contains information about a reference count and the
actual data being stored (in derived "classes"); it may be used to ease
memory management when using instances in different parts of the
software
* core/plugin: infrastructure for registering and calling plugin
callbacks; each registered plugin has its own context which stores
information that should be available to different parts of the software
without having the plugin to care much about it; for now, configuration,
init, shutdown and collector (backend) callbacks are supported
* core/store: implementation of the system object store; currently, host
and service entries are supported; each host includes a list of services
assigned to it; the store objects are derived from the core object
implementation
* utils/llist: a linked list implementation, currently supporting sorted
insertions, searching and shifting objects and iterators; the list
stores core object instances
* utils/string: collection of string utilities; currently a wrapper around
strerror_r() is available only
* utils/time: implementation of a custom time type which is a single
integer value storing the time since the epoch in nanoseconds
resolution; this is provided for simplified time calculations and
comparisons
* utils/unixsock: helper functions to connect to and communicate with a
UNIX domain socket
- backends: plugins implementing collector callbacks
* collectd: this backend retrieves host and service information from
collectd, the system statistics collection daemon, through its unixsock
interface
- daemon: 'syscollectord' is a daemon based on libsyscollector which
periodically queries all backends
- liboconfig: the configuration parsing library used by collectd; this is
used by 'syscollectord' for parsing its configuration and it's used by
libsyscollector to pass on configuration to the plugins
collection service. The current version provides a configurable plugin
infrastructure to query host / service objects.
The system is made up of the following components:
- libsyscollector: the library providing the core functionality to manage
plugins and the data store
* core/object: base "class" for objects being stored in any part of the
library; an object contains information about a reference count and the
actual data being stored (in derived "classes"); it may be used to ease
memory management when using instances in different parts of the
software
* core/plugin: infrastructure for registering and calling plugin
callbacks; each registered plugin has its own context which stores
information that should be available to different parts of the software
without having the plugin to care much about it; for now, configuration,
init, shutdown and collector (backend) callbacks are supported
* core/store: implementation of the system object store; currently, host
and service entries are supported; each host includes a list of services
assigned to it; the store objects are derived from the core object
implementation
* utils/llist: a linked list implementation, currently supporting sorted
insertions, searching and shifting objects and iterators; the list
stores core object instances
* utils/string: collection of string utilities; currently a wrapper around
strerror_r() is available only
* utils/time: implementation of a custom time type which is a single
integer value storing the time since the epoch in nanoseconds
resolution; this is provided for simplified time calculations and
comparisons
* utils/unixsock: helper functions to connect to and communicate with a
UNIX domain socket
- backends: plugins implementing collector callbacks
* collectd: this backend retrieves host and service information from
collectd, the system statistics collection daemon, through its unixsock
interface
- daemon: 'syscollectord' is a daemon based on libsyscollector which
periodically queries all backends
- liboconfig: the configuration parsing library used by collectd; this is
used by 'syscollectord' for parsing its configuration and it's used by
libsyscollector to pass on configuration to the plugins
42 files changed:
.gitignore | [new file with mode: 0644] | patch | blob |
COPYING | [new file with mode: 0644] | patch | blob |
INSTALL | [new file with mode: 0644] | patch | blob |
Makefile.am | [new file with mode: 0644] | patch | blob |
README | [new file with mode: 0644] | patch | blob |
THANKS | [new file with mode: 0644] | patch | blob |
autogen.sh | [new file with mode: 0755] | patch | blob |
configure.ac | [new file with mode: 0644] | patch | blob |
doc/Makefile.am | [new file with mode: 0644] | patch | blob |
doc/syscollectord.1.txt | [new file with mode: 0644] | patch | blob |
m4/sc_plugin.m4 | [new file with mode: 0644] | patch | blob |
src/Makefile.am | [new file with mode: 0644] | patch | blob |
src/backend/collectd.c | [new file with mode: 0644] | patch | blob |
src/core/object.c | [new file with mode: 0644] | patch | blob |
src/core/plugin.c | [new file with mode: 0644] | patch | blob |
src/core/store.c | [new file with mode: 0644] | patch | blob |
src/daemon/config.c | [new file with mode: 0644] | patch | blob |
src/daemon/syscollectord.c | [new file with mode: 0644] | patch | blob |
src/daemon/syscollectord.conf | [new file with mode: 0644] | patch | blob |
src/include/core/object.h | [new file with mode: 0644] | patch | blob |
src/include/core/plugin.h | [new file with mode: 0644] | patch | blob |
src/include/core/store.h | [new file with mode: 0644] | patch | blob |
src/include/syscollector.h.in | [new file with mode: 0644] | patch | blob |
src/include/utils/llist.h | [new file with mode: 0644] | patch | blob |
src/include/utils/string.h | [new file with mode: 0644] | patch | blob |
src/include/utils/time.h | [new file with mode: 0644] | patch | blob |
src/include/utils/unixsock.h | [new file with mode: 0644] | patch | blob |
src/liboconfig/COPYING | [new file with mode: 0644] | patch | blob |
src/liboconfig/Makefile.am | [new file with mode: 0644] | patch | blob |
src/liboconfig/aux_types.h | [new file with mode: 0644] | patch | blob |
src/liboconfig/oconfig.c | [new file with mode: 0644] | patch | blob |
src/liboconfig/oconfig.h | [new file with mode: 0644] | patch | blob |
src/liboconfig/parser.y | [new file with mode: 0644] | patch | blob |
src/liboconfig/scanner.l | [new file with mode: 0644] | patch | blob |
src/liboconfig/utils.c | [new file with mode: 0644] | patch | blob |
src/liboconfig/utils.h | [new file with mode: 0644] | patch | blob |
src/syscollector.c | [new file with mode: 0644] | patch | blob |
src/utils/llist.c | [new file with mode: 0644] | patch | blob |
src/utils/string.c | [new file with mode: 0644] | patch | blob |
src/utils/time.c | [new file with mode: 0644] | patch | blob |
src/utils/unixsock.c | [new file with mode: 0644] | patch | blob |
version-gen.sh | [new file with mode: 0755] | patch | blob |
diff --git a/.gitignore b/.gitignore
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# build system stuff
+m4/argz.m4
+m4/libtool.m4
+m4/ltdl.m4
+m4/ltoptions.m4
+m4/ltsugar.m4
+m4/ltversion.m4
+m4/lt~obsolete.m4
+.deps
+Makefile
+Makefile.in
+aclocal.m4
+autom4te.cache
+compile
+config.*
+configure
+depcomp
+install-sh
+missing
+stamp-h1
+version
+ylwrap
+
+# ltdl stuff
+libltdl
+libtool
+ltmain.sh
+
+# build output
+src/liboconfig/parser.c
+src/liboconfig/parser.h
+src/liboconfig/scanner.c
+.dirstamp
+.libs
+*.la
+*.lo
+*.o
+syscollectord
+syscollectord.1
+syscollector.h
+
diff --git a/COPYING b/COPYING
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,24 @@
+Copyright (c) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/INSTALL b/INSTALL
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,236 @@
+Installation Instructions
+*************************
+
+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
+Software Foundation, Inc.
+
+This file is free documentation; the Free Software Foundation gives
+unlimited permission to copy, distribute and modify it.
+
+Basic Installation
+==================
+
+These are generic installation instructions.
+
+ The `configure' shell script attempts to guess correct values for
+various system-dependent variables used during compilation. It uses
+those values to create a `Makefile' in each directory of the package.
+It may also create one or more `.h' files containing system-dependent
+definitions. Finally, it creates a shell script `config.status' that
+you can run in the future to recreate the current configuration, and a
+file `config.log' containing compiler output (useful mainly for
+debugging `configure').
+
+ It can also use an optional file (typically called `config.cache'
+and enabled with `--cache-file=config.cache' or simply `-C') that saves
+the results of its tests to speed up reconfiguring. (Caching is
+disabled by default to prevent problems with accidental use of stale
+cache files.)
+
+ If you need to do unusual things to compile the package, please try
+to figure out how `configure' could check whether to do them, and mail
+diffs or instructions to the address given in the `README' so they can
+be considered for the next release. If you are using the cache, and at
+some point `config.cache' contains results you don't want to keep, you
+may remove or edit it.
+
+ The file `configure.ac' (or `configure.in') is used to create
+`configure' by a program called `autoconf'. You only need
+`configure.ac' if you want to change it or regenerate `configure' using
+a newer version of `autoconf'.
+
+The simplest way to compile this package is:
+
+ 1. `cd' to the directory containing the package's source code and type
+ `./configure' to configure the package for your system. If you're
+ using `csh' on an old version of System V, you might need to type
+ `sh ./configure' instead to prevent `csh' from trying to execute
+ `configure' itself.
+
+ Running `configure' takes awhile. While running, it prints some
+ messages telling which features it is checking for.
+
+ 2. Type `make' to compile the package.
+
+ 3. Optionally, type `make check' to run any self-tests that come with
+ the package.
+
+ 4. Type `make install' to install the programs and any data files and
+ documentation.
+
+ 5. You can remove the program binaries and object files from the
+ source code directory by typing `make clean'. To also remove the
+ files that `configure' created (so you can compile the package for
+ a different kind of computer), type `make distclean'. There is
+ also a `make maintainer-clean' target, but that is intended mainly
+ for the package's developers. If you use it, you may have to get
+ all sorts of other programs in order to regenerate files that came
+ with the distribution.
+
+Compilers and Options
+=====================
+
+Some systems require unusual options for compilation or linking that the
+`configure' script does not know about. Run `./configure --help' for
+details on some of the pertinent environment variables.
+
+ You can give `configure' initial values for configuration parameters
+by setting variables in the command line or in the environment. Here
+is an example:
+
+ ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
+
+ *Note Defining Variables::, for more details.
+
+Compiling For Multiple Architectures
+====================================
+
+You can compile the package for more than one kind of computer at the
+same time, by placing the object files for each architecture in their
+own directory. To do this, you must use a version of `make' that
+supports the `VPATH' variable, such as GNU `make'. `cd' to the
+directory where you want the object files and executables to go and run
+the `configure' script. `configure' automatically checks for the
+source code in the directory that `configure' is in and in `..'.
+
+ If you have to use a `make' that does not support the `VPATH'
+variable, you have to compile the package for one architecture at a
+time in the source code directory. After you have installed the
+package for one architecture, use `make distclean' before reconfiguring
+for another architecture.
+
+Installation Names
+==================
+
+By default, `make install' installs the package's commands under
+`/usr/local/bin', include files under `/usr/local/include', etc. You
+can specify an installation prefix other than `/usr/local' by giving
+`configure' the option `--prefix=PREFIX'.
+
+ You can specify separate installation prefixes for
+architecture-specific files and architecture-independent files. If you
+pass the option `--exec-prefix=PREFIX' to `configure', the package uses
+PREFIX as the prefix for installing programs and libraries.
+Documentation and other data files still use the regular prefix.
+
+ In addition, if you use an unusual directory layout you can give
+options like `--bindir=DIR' to specify different values for particular
+kinds of files. Run `configure --help' for a list of the directories
+you can set and what kinds of files go in them.
+
+ If the package supports it, you can cause programs to be installed
+with an extra prefix or suffix on their names by giving `configure' the
+option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
+
+Optional Features
+=================
+
+Some packages pay attention to `--enable-FEATURE' options to
+`configure', where FEATURE indicates an optional part of the package.
+They may also pay attention to `--with-PACKAGE' options, where PACKAGE
+is something like `gnu-as' or `x' (for the X Window System). The
+`README' should mention any `--enable-' and `--with-' options that the
+package recognizes.
+
+ For packages that use the X Window System, `configure' can usually
+find the X include and library files automatically, but if it doesn't,
+you can use the `configure' options `--x-includes=DIR' and
+`--x-libraries=DIR' to specify their locations.
+
+Specifying the System Type
+==========================
+
+There may be some features `configure' cannot figure out automatically,
+but needs to determine by the type of machine the package will run on.
+Usually, assuming the package is built to be run on the _same_
+architectures, `configure' can figure that out, but if it prints a
+message saying it cannot guess the machine type, give it the
+`--build=TYPE' option. TYPE can either be a short name for the system
+type, such as `sun4', or a canonical name which has the form:
+
+ CPU-COMPANY-SYSTEM
+
+where SYSTEM can have one of these forms:
+
+ OS KERNEL-OS
+
+ See the file `config.sub' for the possible values of each field. If
+`config.sub' isn't included in this package, then this package doesn't
+need to know the machine type.
+
+ If you are _building_ compiler tools for cross-compiling, you should
+use the option `--target=TYPE' to select the type of system they will
+produce code for.
+
+ If you want to _use_ a cross compiler, that generates code for a
+platform different from the build platform, you should specify the
+"host" platform (i.e., that on which the generated programs will
+eventually be run) with `--host=TYPE'.
+
+Sharing Defaults
+================
+
+If you want to set default values for `configure' scripts to share, you
+can create a site shell script called `config.site' that gives default
+values for variables like `CC', `cache_file', and `prefix'.
+`configure' looks for `PREFIX/share/config.site' if it exists, then
+`PREFIX/etc/config.site' if it exists. Or, you can set the
+`CONFIG_SITE' environment variable to the location of the site script.
+A warning: not all `configure' scripts look for a site script.
+
+Defining Variables
+==================
+
+Variables not defined in a site shell script can be set in the
+environment passed to `configure'. However, some packages may run
+configure again during the build, and the customized values of these
+variables may be lost. In order to avoid this problem, you should set
+them in the `configure' command line, using `VAR=value'. For example:
+
+ ./configure CC=/usr/local2/bin/gcc
+
+causes the specified `gcc' to be used as the C compiler (unless it is
+overridden in the site shell script). Here is a another example:
+
+ /bin/bash ./configure CONFIG_SHELL=/bin/bash
+
+Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
+configuration-related scripts to be executed by `/bin/bash'.
+
+`configure' Invocation
+======================
+
+`configure' recognizes the following options to control how it operates.
+
+`--help'
+`-h'
+ Print a summary of the options to `configure', and exit.
+
+`--version'
+`-V'
+ Print the version of Autoconf used to generate the `configure'
+ script, and exit.
+
+`--cache-file=FILE'
+ Enable the cache: use and save the results of the tests in FILE,
+ traditionally `config.cache'. FILE defaults to `/dev/null' to
+ disable caching.
+
+`--config-cache'
+`-C'
+ Alias for `--cache-file=config.cache'.
+
+`--quiet'
+`--silent'
+`-q'
+ Do not print messages saying which checks are being made. To
+ suppress all normal output, redirect it to `/dev/null' (any error
+ messages will still be shown).
+
+`--srcdir=DIR'
+ Look for the package's source code in directory DIR. Usually
+ `configure' can determine that directory automatically.
+
+`configure' also accepts some other, not widely useful, options. Run
+`configure --help' for more details.
+
diff --git a/Makefile.am b/Makefile.am
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,14 @@
+ACLOCAL_AMFLAGS = -I m4
+
+SUBDIRS = src
+if BUILD_DOCUMENTATION
+SUBDIRS += doc
+endif
+
+EXTRA_DIST = autogen.sh version-gen.sh
+
+version: FORCE
+ @# As a side-effect, this updates version.
+ @echo Building $(PACKAGE_NAME) version $$( cd .. && ./version-gen.sh )
+.PHONY: FORCE
+
diff --git a/README b/README
--- /dev/null
+++ b/README
@@ -0,0 +1,45 @@
+ syscollector -- a system management / inventory collection service
+ ====================================================================
+
+ “System Collector” (syscollector) is a multi-backend system management and
+ inventory collection service. It may be used to collect information from
+ various backends (inventory services, monitoring services, etc.) and
+ provides a unique interface to access the information independent of the
+ active backends. This is done by mapping the backend objects to generic
+ objects and correlating the attributes to create a single hierarchy of your
+ infrastructure.
+
+ This is free and open source software, licensed under the 2-clause BSD
+ license. See COPYING for details.
+
+Prerequisites
+-------------
+
+ To compile the syscollector package from source you need:
+
+ * A build environment: autotools, libtool, C compiler, ...
+
+ * A POSIX + Single UNIX Specification compatible C library.
+
+ * asciidoc, xsltproc:
+ The AsciiDoc text document format is used to write the manpages.
+
+Configuring / Compiling / Installing
+------------------------------------
+
+ To configure, build and install syscollector with the default settings, run
+ `./configure && make && make install'. For detailed, generic instructions
+ see INSTALL. For a complete list of configure options and their description,
+ run `./configure --help'.
+
+ By default, syscollector will be installed into `/opt/syscollector'. You can
+ adjust this setting by specifying the `--prefix' configure option - see
+ INSTALL for details. If you pass DESTDIR=<path> to `make install', <path>
+ will be prefixed to all installation directories. This might be useful when
+ creating packages for syscollector.
+
+Author
+------
+
+ Sebastian "tokkee" Harl <sh@tokkee.org>
+
diff --git a/THANKS b/THANKS
--- /dev/null
+++ b/THANKS
@@ -0,0 +1,4 @@
+Special thanks goes to Florian 'octo' Forster <octo@collectd.org> and
+everybody involved in collectd <http://collectd.org>. The project has greatly
+influenced the architectural design of syscollector.
+
diff --git a/autogen.sh b/autogen.sh
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,17 @@
+#! /bin/sh
+
+libtoolize=libtoolize
+
+if which glibtoolize > /dev/null 2>&1; then
+ libtoolize=glibtoolize
+fi
+
+set -ex
+
+aclocal -I m4 --force --warnings=all
+$libtoolize --automake --copy --force
+aclocal -I m4
+autoconf --force --warnings=all
+autoheader --force --warnings=all
+automake --add-missing --copy --foreign --warnings=all
+
diff --git a/configure.ac b/configure.ac
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,247 @@
+dnl Process this file with autoconf to produce a configure script.
+dnl
+dnl This is the syscollector configure script.
+dnl
+dnl Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+dnl All rights reserved.
+dnl
+dnl Redistribution and use in source and binary forms, with or without
+dnl modification, are permitted provided that the following conditions
+dnl are met:
+dnl 1. Redistributions of source code must retain the above copyright
+dnl notice, this list of conditions and the following disclaimer.
+dnl 2. Redistributions in binary form must reproduce the above copyright
+dnl notice, this list of conditions and the following disclaimer in the
+dnl documentation and/or other materials provided with the distribution.
+dnl
+dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+dnl ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+dnl TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+dnl PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+dnl CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+dnl EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+dnl PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+dnl OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+dnl WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+dnl OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+dnl ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+AC_INIT([system collector],[m4_esyscmd(./version-gen.sh)],
+ [sh@tokkee.org],
+ [syscollector],
+ [http://git.tokkee.org/?p=syscollector.git])
+PACKAGE_MAINTAINER="Sebastian 'tokkee' Harl <sh@tokkee.org>"
+AC_DEFINE_UNQUOTED([PACKAGE_MAINTAINER], ["$PACKAGE_MAINTAINER"],
+ [Define to the name of the maintainer of this package.])
+AC_CONFIG_SRCDIR([src/syscollector.c])
+AC_CONFIG_HEADERS([src/config.h])
+AC_PREFIX_DEFAULT([/opt/syscollector])
+
+AM_INIT_AUTOMAKE([foreign -Wall])
+
+AC_LANG(C)
+
+AC_SYS_LARGEFILE
+
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+AC_PROG_LN_S
+AC_PROG_MAKE_SET
+
+AM_PROG_CC_C_O
+AM_PROG_LEX
+AC_PROG_YACC
+
+m4_ifdef([LT_INIT],
+ [
+ LT_CONFIG_LTDL_DIR([libltdl])
+ LT_INIT([dlopen])
+ LTDL_INIT([convenience])
+ ],
+ # else
+ # (older libtools)
+ [
+ AC_CONFIG_SUBDIRS(libltdl)
+ AC_LIBLTDL_CONVENIENCE
+ AC_SUBST(LTDLINCL)
+ AC_SUBST(LIBLTDL)
+ AC_LIBTOOL_DLOPEN
+ ]
+)
+
+test_cc_flags() {
+ AC_LANG_CONFTEST([AC_LANG_PROGRAM([[ ]], [[ ]])])
+ $CC -c conftest.c $CFLAGS $@ > /dev/null 2> /dev/null
+ ret=$?
+ rm -f conftest.o
+ return $ret
+}
+
+m4_divert_once([HELP_ENABLE], [
+Build options:])
+
+dnl Optionally stick to standard C99 and POSIX:2001 as close as possible.
+AC_ARG_ENABLE([standards],
+ AS_HELP_STRING([--enable-standards],
+ [C99 / POSIX standards compliance mode @<:@default=no@:>@]),
+ [enable_standards="$enableval"],
+ [enable_standards="no"])
+
+if test "x$enable_standards" = "xyes"; then
+ AC_DEFINE([_ISOC99_SOURCE], 1,
+ [Define to enforce ISO/IEC 9899:1999 (C99) compliance.])
+ AC_DEFINE([_POSIX_C_SOURCE], 200112L,
+ [Define to enforce IEEE 1003.1-2001 (POSIX:2001) compliance.])
+ AC_DEFINE([_XOPEN_SOURCE], 600,
+ [Define to enforce X/Open 6 (XSI) compliance.])
+ AC_DEFINE([_REENTRANT], 1,
+ [Define to enable reentrant interfaces.])
+ AC_DEFINE([_THREAD_SAFE], 1,
+ [Define to enable reentrant interfaces.])
+
+ for flag in -std=c99 -pedantic; do
+ AC_MSG_CHECKING([whether $CC accepts $flag])
+
+ if test_cc_flags $flag; then
+ CFLAGS="$CFLAGS $flag"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ done
+fi
+
+dnl Hardening (see e.g. http://wiki.debian.org/Hardening for a motivation).
+AC_DEFINE([_FORTIFY_SOURCE], 2,
+ [Define to enable protection against static sized buffer overflows.])
+AC_ARG_ENABLE([hardening],
+ AS_HELP_STRING([--disable-hardening],
+ [hardening options @<:@default=yes@:>@]),
+ [enable_hardening="$enableval"],
+ [enable_hardening="yes"])
+
+if test "x$enable_hardening" = "xyes"; then
+ hardening=0
+ hardening_tests=0
+ for flag in -Wformat -Wformat-security; do
+ hardening_tests=$(($hardening_tests + 1))
+ AC_MSG_CHECKING([whether $CC accepts $flag])
+
+ if test_cc_flags $flag; then
+ CFLAGS="$CFLAGS $flag"
+ hardening=$(($hardening + 1))
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ done
+ if test $hardening -ne $hardening_tests; then
+ AC_MSG_WARN(
+ [Some hardening options are not supported by your compiler!])
+ fi
+fi
+
+dnl Strict checking for potential problems.
+AC_ARG_ENABLE([strict-checks],
+ AS_HELP_STRING([--disable-strict-checks],
+ [strict compiler checks @<:@default=yes@:>@]),
+ [enable_strict_checks="$enableval"],
+ [enable_strict_checks="yes"])
+
+STRICT_CFLAGS=""
+for flag in -Wall -Werror; do
+ AC_MSG_CHECKING([whether $CC accepts $flag])
+
+ if test_cc_flags $flag; then
+ STRICT_CFLAGS="$STRICT_CFLAGS $flag"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+done
+
+if test "x$enable_strict_checks" = "xyes"; then
+ for flag in -Wextra \
+ -Wbad-function-cast \
+ -Wcast-align \
+ -Wcast-qual \
+ -Wconversion \
+ -Wdeclaration-after-statement \
+ -Wmissing-prototypes \
+ -Wpointer-arith \
+ -Wshadow \
+ -Wstrict-prototypes \
+ -Wunreachable-code; do
+ AC_MSG_CHECKING([whether $CC accepts $flag])
+
+ if test_cc_flags $flag; then
+ STRICT_CFLAGS="$STRICT_CFLAGS $flag"
+ AC_MSG_RESULT([yes])
+ else
+ AC_MSG_RESULT([no])
+ fi
+ done
+fi
+AC_SUBST([STRICT_CFLAGS])
+
+AC_CHECK_HEADERS(libgen.h)
+
+dnl Check for dependencies.
+build_documentation="yes"
+
+have_xsltproc="yes"
+AC_PATH_PROG([XSLTPROC], [xsltproc])
+if test "x$XSLTPROC" = "x"; then
+ have_xsltproc="no"
+ build_documentation="no (missing xsltproc)"
+fi
+
+have_a2x="yes"
+AC_PATH_PROG([A2X], [a2x])
+if test "x$A2X" = "x"; then
+ have_a2x="no"
+ build_documentation="no (missing a2x)"
+fi
+AC_SUBST([A2X])
+
+m4_divert_once([HELP_ENABLE], [
+Backends:])
+
+AC_SC_PLUGIN_INIT
+AC_SC_PLUGIN([collectd], [yes],
+ [backend accessing the system statistics collection daemon])
+
+AM_CONDITIONAL([BUILD_DOCUMENTATION], test "x$build_documentation" = "xyes")
+
+AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile
+ src/liboconfig/Makefile])
+AC_OUTPUT
+
+BUILD_DATE="`date --utc '+%F %T'` (UTC)"
+
+AC_MSG_RESULT()
+AC_MSG_RESULT([$PACKAGE_NAME has been configured successfully.])
+AC_MSG_RESULT()
+AC_MSG_RESULT([Run 'make' to compile the software and use 'make install' to])
+AC_MSG_RESULT([install the package into $prefix.])
+AC_MSG_RESULT()
+AC_MSG_RESULT([Configuration summary:])
+AC_MSG_RESULT()
+AC_MSG_RESULT([ package version: $PACKAGE_VERSION])
+AC_MSG_RESULT([ build date: $BUILD_DATE])
+AC_MSG_RESULT()
+AC_MSG_RESULT([ Tools:])
+AC_MSG_RESULT([ AsciiDoc (a2x): . . . . . . $have_a2x])
+AC_MSG_RESULT([ xsltproc: . . . . . . . . . $have_xsltproc])
+AC_MSG_RESULT()
+AC_MSG_RESULT([ Features:])
+AC_MSG_RESULT([ documentation: . . . . . . $build_documentation])
+AC_MSG_RESULT()
+AC_MSG_RESULT([ Backends:])
+AC_MSG_RESULT([ collectd: . . . . . . . . . $enable_collectd])
+AC_MSG_RESULT()
+AC_MSG_RESULT([This package is maintained by $PACKAGE_MAINTAINER.])
+AC_MSG_RESULT([Please report bugs to $PACKAGE_BUGREPORT.])
+AC_MSG_RESULT()
+
diff --git a/doc/Makefile.am b/doc/Makefile.am
--- /dev/null
+++ b/doc/Makefile.am
@@ -0,0 +1,13 @@
+EXTRA_DIST = syscollectord.1.txt
+CLEANFILES = syscollectord.1
+
+man_MANS = syscollectord.1
+
+syscollectord.1: syscollectord.1.txt ../version
+
+%.1: %.1.txt
+ @A2X@ -d manpage -f manpage \
+ -apackage_version=$(PACKAGE_VERSION) \
+ -abuild_date="$$( date --utc '+%F' )" \
+ $<
+
diff --git a/doc/syscollectord.1.txt b/doc/syscollectord.1.txt
--- /dev/null
+++ b/doc/syscollectord.1.txt
@@ -0,0 +1,71 @@
+syscollectord(1)
+================
+Sebastian "tokkee" Harl <sh@tokkee.org>
+version {package_version}, {build_date}
+:doctype: manpage
+
+NAME
+----
+syscollectord - system management collection service
+
+SYNOPSIS
+--------
+*syscollectord* ['options']
+
+DESCRIPTION
+-----------
+*syscollectord* is a multi-backend system management and inventory collection
+daemon. It may be used to collect information from various backends (e.g.,
+inventory services, monitoring services, configuration services) and provides
+a uniform combined view of all data.
+
+The main daemon itself is the central instance managing all collected
+information and doing the correlation of objects provided by different
+backends. It can be thought of as a database server. All data retrieval,
+any further processing, storing and exporting of data is done by plugins.
+
+OPTIONS
+-------
+*syscollectord* accepts the following command-line options.
+
+*-C* '<file>'::
+ The main configuration file. This file defines the behavior of
+ *syscollectord* by specifying default settings and the plugins to be
+ loaded.
+
+*-d*::
+ Daemonize on startup: Start *syscollectord* as a background process
+ detached from the current terminal and session.
+
+*-h*::
+ Display a usage and help summary and exit.
+
+*-V*::
+ Display the version number and copyright information.
+
+EXIT CODES
+----------
+*0*::
+ Success.
+
+*1*::
+ Failure (syntax or usage error).
+
+BUGS
+----
+None known.
+
+AUTHOR
+------
+syscollectord was written by Sebastian "tokkee" Harl <sh@tokkee.org>.
+
+COPYRIGHT
+---------
+Copyright (C) 2012 Sebastian "tokkee" Harl <sh@tokkee.org>
+
+This is free software under the terms of the BSD license, see the source for
+copying conditions. There is NO WARRANTY; not even for MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.
+
+// vim: set tw=78 sw=4 ts=4 noexpandtab spell spelllang=en_us :
+
diff --git a/m4/sc_plugin.m4 b/m4/sc_plugin.m4
--- /dev/null
+++ b/m4/sc_plugin.m4
@@ -0,0 +1,85 @@
+dnl Autoconf helper functions for the syscollector plugin handling.
+dnl
+dnl Copyright (C) 2005-2012 Florian 'octo' Forster <octo@verplant.org>
+dnl Copyright (C) 2009-2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+dnl
+dnl This program is free software; you can redistribute it and/or modify it
+dnl under the terms of the GNU General Public License as published by the
+dnl Free Software Foundation; only version 2 of the License is applicable.
+dnl
+dnl This program is distributed in the hope that it will be useful, but
+dnl WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+dnl General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License along
+dnl with this program; if not, write to the Free Software Foundation, Inc.,
+dnl 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+AC_DEFUN([AC_SC_PLUGIN_INIT],
+ [
+ dependency_error="no"
+ dependency_warning="no"
+ AC_ARG_ENABLE([all-plugins],
+ AS_HELP_STRING([--enable-all-plugins],
+ [enable all plugins (auto by default)]),
+ [
+ if test "x$enableval" = "xyes"; then
+ enable_all_plugins="yes"
+ else if test "x$enableval" = "xauto"; then
+ enable_all_plugins="auto"
+ else
+ enable_all_plugins="no"
+ fi; fi
+ ],
+ [enable_all_plugins="auto"]
+ )
+ ]
+)
+
+dnl AC_SC_PLUGIN(name, default, info)
+dnl
+dnl Based on AC_PLUGIN of the collectd package.
+AC_DEFUN([AC_SC_PLUGIN],
+ [
+ enable_plugin="no"
+ force="no"
+ AC_ARG_ENABLE([$1], AS_HELP_STRING([--enable-$1], [$3]),
+ [
+ if test "x$enableval" = "xyes"; then
+ enable_plugin="yes"
+ else if test "x$enableval" = "xforce"; then
+ enable_plugin="yes"
+ force="yes"
+ else
+ enable_plugin="no (disabled on command line)"
+ fi; fi
+ ],
+ [
+ if test "x$enable_all_plugins" = "xauto"; then
+ if test "x$2" = "xyes"; then
+ enable_plugin="yes"
+ else
+ enable_plugin="no"
+ fi
+ else
+ enable_plugin="$enable_all_plugins"
+ fi
+ ]
+ )
+ if test "x$enable_plugin" = "xyes"; then
+ if test "x$2" = "xyes" || test "x$force" = "xyes"; then
+ AC_DEFINE([HAVE_PLUGIN_]m4_toupper([$1]), 1, [Define to 1 if the $1 plugin is enabled.])
+ if test "x$2" != "xyes"; then
+ dependency_warning="yes"
+ fi
+ else # User passed "yes" but dependency checking yielded "no" => Dependency problem.
+ dependency_error="yes"
+ enable_plugin="no (dependency error)"
+ fi
+ fi
+ AM_CONDITIONAL([BUILD_PLUGIN_]m4_toupper([$1]), test "x$enable_plugin" = "xyes")
+ enable_$1="$enable_plugin"
+ ]
+)
+
diff --git a/src/Makefile.am b/src/Makefile.am
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,74 @@
+SUBDIRS = liboconfig
+
+AM_CFLAGS = @STRICT_CFLAGS@ -Iinclude
+AM_CPPFLAGS = -DSYSCONFDIR='"${sysconfdir}"'
+AM_CPPFLAGS += -DPKGLIBDIR='"${pkglibdir}"'
+
+BUILT_SOURCES = include/syscollector.h
+
+pkginclude_HEADERS = include/syscollector.h
+pkgcoreincludedir = $(pkgincludedir)/core
+pkgcoreinclude_HEADERS = \
+ include/core/object.h \
+ include/core/plugin.h
+pkgutilsincludedir = $(pkgincludedir)/utils
+pkgutilsinclude_HEADERS = \
+ include/utils/llist.h \
+ include/utils/string.h
+
+lib_LTLIBRARIES = libsyscollector.la
+
+libsyscollector_la_SOURCES = \
+ syscollector.c include/syscollector.h \
+ core/object.c include/core/object.h \
+ core/plugin.c include/core/plugin.h \
+ core/store.c include/core/store.h \
+ utils/llist.c include/utils/llist.h \
+ utils/string.c include/utils/string.h \
+ utils/time.c include/utils/time.h \
+ utils/unixsock.c include/utils/unixsock.h
+libsyscollector_la_CFLAGS = $(AM_CFLAGS)
+libsyscollector_la_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL)
+libsyscollector_la_LDFLAGS = -version-info 0:0:0 -pthread
+libsyscollector_la_LIBADD = $(LIBLTDL) -lrt liboconfig/liboconfig.la
+libsyscollector_la_DEPENDENCIES = liboconfig/liboconfig.la
+
+bin_PROGRAMS = syscollectord
+
+syscollectord_SOURCES = daemon/syscollectord.c include/syscollector.h \
+ daemon/config.c include/daemon/config.h
+syscollectord_CFLAGS = $(AM_CFLAGS) -DBUILD_DATE="\"$$( date --utc '+%F %T' ) (UTC)\""
+syscollectord_LDADD = libsyscollector.la liboconfig/liboconfig.la
+syscollectord_DEPENDENCIES = liboconfig/liboconfig.la
+
+pkgbackendlibdir = $(pkglibdir)/backend
+
+pkgbackendlib_LTLIBRARIES =
+
+if BUILD_PLUGIN_COLLECTD
+pkgbackendlib_LTLIBRARIES += backend/collectd.la
+backend_collectd_la_SOURCES = backend/collectd.c
+backend_collectd_la_LDFLAGS = -module -avoid-version
+libsyscollector_la_LIBADD += -dlopen backend/collectd.la
+libsyscollector_la_DEPENDENCIES += backend/collectd.la
+endif
+
+include/syscollector.h: include/syscollector.h.in ../version
+ source ../version; sed \
+ -e "s/@SC_VERSION_MAJOR@/$$VERSION_MAJOR/g" \
+ -e "s/@SC_VERSION_MINOR@/$$VERSION_MINOR/g" \
+ -e "s/@SC_VERSION_PATCH@/$$VERSION_PATCH/g" \
+ -e "s/@SC_VERSION_EXTRA@/$$VERSION_EXTRA/g" \
+ -e "s/@SC_VERSION_STRING@/$$VERSION_STRING/g" \
+ include/syscollector.h.in > include/syscollector.h
+
+install-exec-hook:
+ $(mkinstalldirs) $(DESTDIR)$(sysconfdir)/syscollector
+ if test -e $(DESTDIR)$(sysconfdir)/syscollector/syscollectord.conf; then \
+ $(INSTALL) -m 0640 daemon/syscollectord.conf \
+ $(DESTDIR)$(sysconfdir)/syscollector/syscollectord.conf.pkg-orig; \
+ else \
+ $(INSTALL) -m 0640 daemon/syscollectord.conf \
+ $(DESTDIR)$(sysconfdir)/syscollector/syscollectord.conf; \
+ fi
+
diff --git a/src/backend/collectd.c b/src/backend/collectd.c
--- /dev/null
+++ b/src/backend/collectd.c
@@ -0,0 +1,388 @@
+/*
+ * syscollector - src/backend/collectd.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "syscollector.h"
+#include "core/plugin.h"
+#include "core/store.h"
+#include "utils/string.h"
+#include "utils/unixsock.h"
+
+#include "liboconfig/utils.h"
+
+#include <assert.h>
+
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+SC_PLUGIN_MAGIC;
+
+/*
+ * private helper functions
+ */
+
+static int
+sc_collectd_parse_value(char *line,
+ sc_time_t *timestamp, char **host, char **svc)
+{
+ char *timestamp_str;
+ double timestamp_dbl;
+
+ char *hostname, *service;
+
+ char *endptr = NULL;
+
+ timestamp_str = line;
+ hostname = strchr(timestamp_str, ' ');
+
+ if (! hostname) {
+ fprintf(stderr, "collectd backend: Failed to find hostname "
+ "in LISTVAL output, line: %s\n", line);
+ return -1;
+ }
+
+ *hostname = '\0';
+ ++hostname;
+
+ service = strchr(hostname, '/');
+
+ if (! service)
+ fprintf(stderr, "collectd backend: Failed to find service name "
+ "in LISTVAL output, line: %s\n", line);
+ else {
+ *service = '\0';
+ ++service;
+ }
+
+ errno = 0;
+ timestamp_dbl = strtod(timestamp_str, &endptr);
+ if (errno || (timestamp_str == endptr)) {
+ char errbuf[1024];
+ fprintf(stderr, "collectd backend: Failed to "
+ "parse timestamp (%s): %s\n", timestamp_str,
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+ if (endptr && (*endptr != '\0'))
+ fprintf(stderr, "collectd backend: Ignoring garbage "
+ "after number when parsing timestamp: %s.\n",
+ endptr);
+
+ *timestamp = DOUBLE_TO_SC_TIME(timestamp_dbl);
+ *host = hostname;
+ *svc = service;
+ return 0;
+} /* sc_collectd_parse_value */
+
+static int
+sc_collectd_add_host(char *hostname, sc_time_t last_update)
+{
+ sc_host_t host = SC_HOST_INIT;
+
+ host.host_name = hostname;
+ host.host_last_update = last_update;
+
+ if (sc_store_host(&host)) {
+ fprintf(stderr, "collectd backend: Failed to store/update "
+ "host '%s'.\n", hostname);
+ return -1;
+ }
+
+ fprintf(stderr, "collectd backend: Added/updated host '%s' "
+ "(last update timestamp = %"PRIscTIME").\n",
+ hostname, last_update);
+ return 0;
+} /* sc_collectd_add_host */
+
+static int
+sc_collectd_add_svc(char *hostname, char *name, sc_time_t last_update)
+{
+ sc_service_t svc = SC_SVC_INIT;
+
+ svc.hostname = hostname;
+ svc.svc_name = name;
+ svc.svc_last_update = last_update;
+
+ if (sc_store_service(&svc)) {
+ fprintf(stderr, "collectd backend: Failed to store/update "
+ "service '%s/%s'.\n", hostname, name);
+ return -1;
+ }
+ return 0;
+} /* sc_collectd_add_svc */
+
+/*
+ * plugin API
+ */
+
+static int
+sc_collectd_init(sc_object_t *user_data)
+{
+ sc_unixsock_client_t *client;
+
+ if (! user_data)
+ return -1;
+
+ client = SC_OBJ_WRAPPER(user_data)->data;
+ if (sc_unixsock_client_connect(client)) {
+ fprintf(stderr, "collectd backend: "
+ "Failed to connect to collectd.\n");
+ return -1;
+ }
+
+ fprintf(stderr, "collectd backend: Successfully "
+ "connected to collectd @ %s.\n",
+ sc_unixsock_client_path(client));
+ return 0;
+} /* sc_collectd_init */
+
+static int
+sc_collectd_shutdown(__attribute__((unused)) sc_object_t *user_data)
+{
+ return 0;
+} /* sc_collectd_shutdown */
+
+static int
+sc_collectd_collect(sc_object_t *user_data)
+{
+ sc_unixsock_client_t *client;
+
+ char buffer[1024];
+ char *line;
+ char *msg;
+
+ char *endptr = NULL;
+ long int count, i;
+
+ char *current_host = NULL;
+ sc_time_t current_timestamp = 0;
+
+ int svc_updated = 0;
+ int svc_failed = 0;
+
+ if (! user_data)
+ return -1;
+
+ client = SC_OBJ_WRAPPER(user_data)->data;
+
+ if (sc_unixsock_client_send(client, "LISTVAL") <= 0) {
+ fprintf(stderr, "collectd backend: Failed to send LISTVAL command "
+ "to collectd @ %s.\n", sc_unixsock_client_path(client));
+ return -1;
+ }
+
+ line = sc_unixsock_client_recv(client, buffer, sizeof(buffer));
+ if (! line) {
+ fprintf(stderr, "collectd backend: Failed to read status "
+ "of LISTVAL command from collectd @ %s.\n",
+ sc_unixsock_client_path(client));
+ return -1;
+ }
+
+ msg = strchr(line, ' ');
+ if (msg) {
+ *msg = '\0';
+ ++msg;
+ }
+
+ errno = 0;
+ count = strtol(line, &endptr, /* base */ 0);
+ if (errno || (line == endptr)) {
+ fprintf(stderr, "collectd backend: Failed to parse status "
+ "of LISTVAL command from collectd @ %s.\n",
+ sc_unixsock_client_path(client));
+ return -1;
+ }
+
+ if (count < 0) {
+ fprintf(stderr, "collectd backend: Failed to get value list "
+ "from collectd @ %s: %s\n", sc_unixsock_client_path(client),
+ msg ? msg : line);
+ return -1;
+ }
+
+ for (i = 0; i < count; ++i) {
+ char *hostname = NULL, *service = NULL;
+ sc_time_t last_update = 0;
+
+ line = sc_unixsock_client_recv(client, buffer, sizeof(buffer));
+
+ if (sc_collectd_parse_value(line, &last_update, &hostname, &service))
+ continue;
+
+ if (! current_host)
+ current_host = strdup(hostname);
+ if (! current_host) {
+ char errbuf[1024];
+ fprintf(stderr, "collectd backend: Failed to allocate "
+ "string buffer: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ if (! sc_store_get_host(hostname))
+ sc_collectd_add_host(hostname, last_update);
+
+ if (sc_collectd_add_svc(hostname, service, last_update))
+ ++svc_failed;
+ else
+ ++svc_updated;
+
+ assert(hostname && service);
+ if (! strcasecmp(current_host, hostname)) {
+ if (last_update > current_timestamp)
+ current_timestamp = last_update;
+ continue;
+ }
+
+ /* new host */
+ sc_collectd_add_host(current_host, current_timestamp);
+
+ fprintf(stderr, "collectd backend: Added/updated "
+ "%i service%s (%i failed) for host '%s'.\n",
+ svc_updated, svc_updated == 1 ? "" : "s",
+ svc_failed, current_host);
+ svc_updated = svc_failed = 0;
+
+ free(current_host);
+ current_host = strdup(hostname);
+ current_timestamp = last_update;
+ }
+
+ if (current_host) {
+ sc_collectd_add_host(current_host, current_timestamp);
+ fprintf(stderr, "collectd backend: Added/updated "
+ "%i service%s (%i failed) for host '%s'.\n",
+ svc_updated, svc_updated == 1 ? "" : "s",
+ svc_failed, current_host);
+ }
+ return 0;
+} /* sc_collectd_collect */
+
+static int
+sc_collectd_config_instance(oconfig_item_t *ci)
+{
+ char *name = NULL;
+ char *socket = NULL;
+
+ char cb_name[1024];
+
+ sc_object_t *user_data;
+ sc_unixsock_client_t *client;
+
+ int i;
+
+ if (oconfig_get_string(ci, &name)) {
+ fprintf(stderr, "collectd backend: Instance requires a single "
+ "string argument\n\tUsage: <Instance NAME>\n");
+ return -1;
+ }
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (! strcasecmp(child->key, "Socket"))
+ oconfig_get_string(child, &socket);
+ else
+ fprintf(stderr, "collectd backend: Ignoring unknown config "
+ "option '%s' inside <Instance %s>.\n",
+ child->key, name);
+ }
+
+ if (! socket) {
+ fprintf(stderr, "collectd backend: Instance '%s' missing "
+ "the 'Socket' option.\n", name);
+ return -1;
+ }
+
+ snprintf(cb_name, sizeof(cb_name), "collectd-%s", name);
+ cb_name[sizeof(cb_name) - 1] = '\0';
+
+ client = sc_unixsock_client_create(socket);
+ if (! client) {
+ char errbuf[1024];
+ fprintf(stderr, "collectd backend: Failed to create unixsock client: "
+ "%s\n", sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ user_data = sc_object_create_wrapper(client,
+ (void (*)(void *))sc_unixsock_client_destroy);
+ if (! user_data) {
+ sc_unixsock_client_destroy(client);
+ fprintf(stderr, "collectd backend: Failed to allocate sc_object_t\n");
+ return -1;
+ }
+
+ sc_plugin_register_init(cb_name, sc_collectd_init, user_data);
+ sc_plugin_register_shutdown(cb_name, sc_collectd_shutdown, user_data);
+
+ sc_plugin_register_collector(cb_name, sc_collectd_collect,
+ /* interval */ NULL, user_data);
+
+ /* pass control to the list */
+ sc_object_deref(user_data);
+ return 0;
+} /* sc_collectd_config_instance */
+
+static int
+sc_collectd_config(oconfig_item_t *ci)
+{
+ int i;
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (! strcasecmp(child->key, "Instance"))
+ sc_collectd_config_instance(child);
+ else
+ fprintf(stderr, "collectd backend: Ignoring unknown config "
+ "option '%s'.\n", child->key);
+ }
+ return 0;
+} /* sc_collectd_config */
+
+int
+sc_module_init(sc_plugin_info_t *info)
+{
+ sc_plugin_set_info(info, SC_PLUGIN_INFO_NAME, "collectd");
+ sc_plugin_set_info(info, SC_PLUGIN_INFO_DESC,
+ "backend accessing the system statistics collection daemon");
+ sc_plugin_set_info(info, SC_PLUGIN_INFO_COPYRIGHT,
+ "Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>");
+ sc_plugin_set_info(info, SC_PLUGIN_INFO_LICENSE, "BSD");
+ sc_plugin_set_info(info, SC_PLUGIN_INFO_VERSION, SC_VERSION);
+ sc_plugin_set_info(info, SC_PLUGIN_INFO_PLUGIN_VERSION, SC_VERSION);
+
+ sc_plugin_register_config("collectd", sc_collectd_config);
+ return 0;
+} /* sc_version_extra */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/core/object.c b/src/core/object.c
--- /dev/null
+++ b/src/core/object.c
@@ -0,0 +1,134 @@
+/*
+ * syscollector - src/core/object.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "core/object.h"
+
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * private helper functions
+ */
+
+static int
+sc_object_wrapper_init(sc_object_t *obj, va_list ap)
+{
+ void *data = va_arg(ap, void *);
+ void (*destructor)(void *) = va_arg(ap, void (*)(void *));
+
+ assert(obj);
+
+ SC_OBJ_WRAPPER(obj)->data = data;
+ SC_OBJ_WRAPPER(obj)->destructor = destructor;
+ return 0;
+} /* sc_object_wrapper_init */
+
+static void
+sc_object_wrapper_destroy(sc_object_t *obj)
+{
+ if (! obj)
+ return;
+
+ assert(obj->ref_cnt <= 0);
+
+ if (SC_OBJ_WRAPPER(obj)->destructor && SC_OBJ_WRAPPER(obj)->data)
+ SC_OBJ_WRAPPER(obj)->destructor(SC_OBJ_WRAPPER(obj)->data);
+ SC_OBJ_WRAPPER(obj)->data = NULL;
+} /* sc_object_wrapper_destroy */
+
+/*
+ * public API
+ */
+
+sc_object_t *
+sc_object_create(size_t size, int (*init)(sc_object_t *, va_list),
+ void (*destructor)(sc_object_t *), ...)
+{
+ sc_object_t *obj;
+
+ obj = malloc(size);
+ if (! obj)
+ return NULL;
+ memset(obj, 0, sizeof(*obj));
+
+ if (init) {
+ va_list ap;
+ va_start(ap, destructor);
+
+ if (init(obj, ap)) {
+ obj->ref_cnt = 1;
+ sc_object_deref(obj);
+ va_end(ap);
+ return NULL;
+ }
+
+ va_end(ap);
+ }
+
+ obj->ref_cnt = 1;
+ obj->destructor = destructor;
+ obj->size = size;
+ return obj;
+} /* sc_object_create */
+
+sc_object_t *
+sc_object_create_wrapper(void *data, void (*destructor)(void *))
+{
+ return sc_object_create(sizeof(sc_object_wrapper_t),
+ sc_object_wrapper_init, sc_object_wrapper_destroy,
+ data, destructor);
+} /* sc_object_create_wrapper */
+
+void
+sc_object_deref(sc_object_t *obj)
+{
+ if (! obj)
+ return;
+
+ --obj->ref_cnt;
+ if (obj->ref_cnt > 0)
+ return;
+
+ if (obj->destructor)
+ obj->destructor(obj);
+
+ free(obj);
+} /* sc_object_deref */
+
+void
+sc_object_ref(sc_object_t *obj)
+{
+ if (! obj)
+ return;
+ assert(obj->ref_cnt > 0);
+ ++obj->ref_cnt;
+} /* sc_object_ref */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/core/plugin.c b/src/core/plugin.c
--- /dev/null
+++ b/src/core/plugin.c
@@ -0,0 +1,637 @@
+/*
+ * syscollector - src/core/plugin.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "syscollector.h"
+#include "core/plugin.h"
+#include "utils/llist.h"
+#include "utils/string.h"
+#include "utils/time.h"
+
+#include <assert.h>
+
+#include <errno.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <ltdl.h>
+
+#include <pthread.h>
+
+/*
+ * private data types
+ */
+
+struct sc_plugin_info {
+ char *name;
+
+ char *description;
+ char *copyright;
+ char *license;
+
+ int version;
+ int plugin_version;
+};
+#define SC_PLUGIN_INFO_INIT { "no name set", "no description set", \
+ /* copyright */ "", /* license */ "", \
+ /* version */ -1, /* plugin_version */ -1 }
+
+typedef struct {
+ sc_object_t super;
+ char cb_name[64];
+ void *cb_callback;
+ sc_object_t *cb_user_data;
+ sc_plugin_ctx_t cb_ctx;
+} sc_plugin_cb_t;
+#define SC_PLUGIN_CB_INIT { SC_OBJECT_INIT, "", NULL, NULL, SC_PLUGIN_CTX_INIT }
+
+typedef struct {
+ sc_plugin_cb_t super;
+#define ccb_name super.cb_name
+#define ccb_callback super.cb_callback
+#define ccb_user_data super.cb_user_data
+#define ccb_ctx super.cb_ctx
+ sc_time_t ccb_interval;
+ sc_time_t ccb_next_update;
+} sc_plugin_collector_cb_t;
+
+#define SC_PLUGIN_CB(obj) ((sc_plugin_cb_t *)(obj))
+#define SC_PLUGIN_CCB(obj) ((sc_plugin_collector_cb_t *)(obj))
+
+/*
+ * private variables
+ */
+
+static sc_plugin_ctx_t plugin_default_ctx = SC_PLUGIN_CTX_INIT;
+
+static pthread_key_t plugin_ctx_key;
+static _Bool plugin_ctx_key_initialized = 0;
+
+static sc_llist_t *config_list = NULL;
+static sc_llist_t *init_list = NULL;
+static sc_llist_t *collector_list = NULL;
+static sc_llist_t *shutdown_list = NULL;
+
+/*
+ * private helper functions
+ */
+
+static void
+sc_plugin_ctx_destructor(void *ctx)
+{
+ if (! ctx)
+ return;
+ free(ctx);
+} /* sc_plugin_ctx_destructor */
+
+static void
+sc_plugin_ctx_init(void)
+{
+ if (plugin_ctx_key_initialized)
+ return;
+
+ pthread_key_create(&plugin_ctx_key, sc_plugin_ctx_destructor);
+ plugin_ctx_key_initialized = 1;
+} /* sc_plugin_ctx_init */
+
+static sc_plugin_ctx_t *
+sc_plugin_ctx_create(void)
+{
+ sc_plugin_ctx_t *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (! ctx)
+ return NULL;
+
+ *ctx = plugin_default_ctx;
+
+ if (! plugin_ctx_key_initialized)
+ sc_plugin_ctx_init();
+ pthread_setspecific(plugin_ctx_key, ctx);
+ return ctx;
+} /* sc_plugin_ctx_create */
+
+static int
+sc_plugin_cmp_name(const sc_object_t *a, const sc_object_t *b)
+{
+ const sc_plugin_cb_t *cb1 = (const sc_plugin_cb_t *)a;
+ const sc_plugin_cb_t *cb2 = (const sc_plugin_cb_t *)b;
+
+ assert(cb1 && cb2);
+ return strcasecmp(cb1->cb_name, cb2->cb_name);
+} /* sc_plugin_cmp_name */
+
+static int
+sc_plugin_cmp_next_update(const sc_object_t *a, const sc_object_t *b)
+{
+ const sc_plugin_collector_cb_t *ccb1
+ = (const sc_plugin_collector_cb_t *)a;
+ const sc_plugin_collector_cb_t *ccb2
+ = (const sc_plugin_collector_cb_t *)b;
+
+ assert(ccb1 && ccb2);
+
+ return (ccb1->ccb_next_update > ccb2->ccb_next_update)
+ ? 1 : (ccb1->ccb_next_update < ccb2->ccb_next_update)
+ ? -1 : 0;
+} /* sc_plugin_cmp_next_update */
+
+static sc_plugin_cb_t *
+sc_plugin_find_by_name(sc_llist_t *list, const char *name)
+{
+ sc_plugin_cb_t tmp = SC_PLUGIN_CB_INIT;
+
+ sc_object_t *obj;
+ assert(name);
+
+ if (! list)
+ return NULL;
+
+ snprintf(tmp.cb_name, sizeof(tmp.cb_name), "%s", name);
+ tmp.cb_name[sizeof(tmp.cb_name) - 1] = '\0';
+ obj = sc_llist_search(list, SC_OBJ(&tmp), sc_plugin_cmp_name);
+ if (! obj)
+ return NULL;
+ return SC_PLUGIN_CB(obj);
+} /* sc_plugin_find_by_name */
+
+static int
+sc_plugin_cb_init(sc_object_t *obj, va_list ap)
+{
+ sc_llist_t **list = va_arg(ap, sc_llist_t **);
+ const char *type = va_arg(ap, const char *);
+ const char *name = va_arg(ap, const char *);
+ void *callback = va_arg(ap, void *);
+ sc_object_t *ud = va_arg(ap, sc_object_t *);
+
+ assert(list);
+ assert(type);
+ assert(obj);
+
+ if (sc_plugin_find_by_name(*list, name)) {
+ fprintf(stderr, "plugin: %s callback '%s' has already been "
+ "registered. Ignoring newly registered version.\n",
+ type, name);
+ return -1;
+ }
+
+ snprintf(SC_PLUGIN_CB(obj)->cb_name,
+ sizeof(SC_PLUGIN_CB(obj)->cb_name),
+ "%s", name);
+ SC_PLUGIN_CB(obj)->cb_name[sizeof(SC_PLUGIN_CB(obj)->cb_name) - 1] = '\0';
+ SC_PLUGIN_CB(obj)->cb_callback = callback;
+ SC_PLUGIN_CB(obj)->cb_ctx = sc_plugin_get_ctx();
+
+ sc_object_ref(ud);
+ SC_PLUGIN_CB(obj)->cb_user_data = ud;
+ return 0;
+} /* sc_plugin_cb_init */
+
+static void
+sc_plugin_cb_destroy(sc_object_t *obj)
+{
+ assert(obj);
+ sc_object_deref(SC_PLUGIN_CB(obj)->cb_user_data);
+} /* sc_plugin_cb_destroy */
+
+static int
+sc_plugin_add_callback(sc_llist_t **list, const char *type,
+ const char *name, void *callback, sc_object_t *user_data)
+{
+ sc_object_t *obj;
+
+ if ((! name) || (! callback))
+ return -1;
+
+ assert(list);
+
+ if (! *list)
+ *list = sc_llist_create();
+ if (! *list)
+ return -1;
+
+ obj = sc_object_create(sizeof(sc_plugin_cb_t), sc_plugin_cb_init,
+ sc_plugin_cb_destroy, list, type, name, callback, user_data);
+ if (! obj)
+ return -1;
+
+ if (sc_llist_append(*list, obj)) {
+ sc_object_deref(obj);
+ return -1;
+ }
+
+ /* pass control to the list */
+ sc_object_deref(obj);
+
+ fprintf(stderr, "plugin: Registered %s callback '%s'.\n", type, name);
+ return 0;
+} /* sc_plugin_add_callback */
+
+/*
+ * public API
+ */
+
+int
+sc_plugin_load(const char *name)
+{
+ char filename[1024];
+
+ lt_dlhandle lh;
+
+ int (*mod_init)(sc_plugin_info_t *);
+ sc_plugin_info_t plugin_info = SC_PLUGIN_INFO_INIT;
+
+ int status;
+
+ snprintf(filename, sizeof(filename), "%s/%s.so",
+ PKGLIBDIR, name);
+ filename[sizeof(filename) - 1] = '\0';
+
+ if (access(filename, R_OK)) {
+ char errbuf[1024];
+ fprintf(stderr, "plugin: Failed to load plugin '%s': %s\n",
+ name, sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ lt_dlinit();
+ lt_dlerror();
+
+ lh = lt_dlopen(filename);
+ if (! lh) {
+ fprintf(stderr, "plugin: Failed to load plugin '%s': %s\n"
+ "The most common cause for this problem are missing "
+ "dependencies.\n", name, lt_dlerror());
+ return -1;
+ }
+
+ mod_init = (int (*)(sc_plugin_info_t *))lt_dlsym(lh, "sc_module_init");
+ if (! mod_init) {
+ fprintf(stderr, "plugin: Failed to load plugin '%s': "
+ "could not find symbol 'sc_module_init'\n", name);
+ return -1;
+ }
+
+ status = mod_init(&plugin_info);
+ if (status) {
+ fprintf(stderr, "plugin: Failed to initialize plugin '%s'\n", name);
+ return -1;
+ }
+
+ /* compare minor version */
+ if ((plugin_info.version < 0)
+ || ((int)(plugin_info.version / 100) != (int)(SC_VERSION / 100)))
+ fprintf(stderr, "plugin: WARNING: version of plugin '%s' (%i.%i.%i) "
+ "does not match our version (%i.%i.%i); "
+ "this might cause problems\n",
+ name, SC_VERSION_DECODE(plugin_info.version),
+ SC_VERSION_DECODE(SC_VERSION));
+
+ fprintf(stderr, "plugin: Successfully loaded "
+ "plugin '%s' v%i (%s)\n\t%s\n",
+ plugin_info.name, plugin_info.plugin_version,
+ plugin_info.description, plugin_info.copyright);
+ return 0;
+} /* sc_plugin_load */
+
+int
+sc_plugin_set_info(sc_plugin_info_t *info, int type, ...)
+{
+ va_list ap;
+
+ if (! info)
+ return -1;
+
+ va_start(ap, type);
+
+ switch (type) {
+ case SC_PLUGIN_INFO_NAME:
+ {
+ char *name = va_arg(ap, char *);
+ info->name = name;
+ }
+ break;
+ case SC_PLUGIN_INFO_DESC:
+ {
+ char *desc = va_arg(ap, char *);
+ info->description = desc;
+ }
+ break;
+ case SC_PLUGIN_INFO_COPYRIGHT:
+ {
+ char *copyright = va_arg(ap, char *);
+ info->copyright = copyright;
+ }
+ break;
+ case SC_PLUGIN_INFO_LICENSE:
+ {
+ char *license = va_arg(ap, char *);
+ info->license = license;
+ }
+ break;
+ case SC_PLUGIN_INFO_VERSION:
+ {
+ int version = va_arg(ap, int);
+ info->version = version;
+ }
+ break;
+ case SC_PLUGIN_INFO_PLUGIN_VERSION:
+ {
+ int version = va_arg(ap, int);
+ info->plugin_version = version;
+ }
+ break;
+ default:
+ va_end(ap);
+ return -1;
+ }
+
+ va_end(ap);
+ return 0;
+} /* sc_plugin_set_info */
+
+int
+sc_plugin_register_config(const char *name, sc_plugin_config_cb callback)
+{
+ return sc_plugin_add_callback(&config_list, "init", name,
+ callback, NULL);
+} /* sc_plugin_register_config */
+
+int
+sc_plugin_register_init(const char *name, sc_plugin_init_cb callback,
+ sc_object_t *user_data)
+{
+ return sc_plugin_add_callback(&init_list, "init", name,
+ callback, user_data);
+} /* sc_plugin_register_init */
+
+int
+sc_plugin_register_shutdown(const char *name, sc_plugin_shutdown_cb callback,
+ sc_object_t *user_data)
+{
+ return sc_plugin_add_callback(&shutdown_list, "shutdown", name,
+ callback, user_data);
+} /* sc_plugin_register_shutdown */
+
+int
+sc_plugin_register_collector(const char *name, sc_plugin_collector_cb callback,
+ const sc_time_t *interval, sc_object_t *user_data)
+{
+ sc_object_t *obj;
+
+ if ((! name) || (! callback))
+ return -1;
+
+ if (! collector_list)
+ collector_list = sc_llist_create();
+ if (! collector_list)
+ return -1;
+
+ obj = sc_object_create(sizeof(sc_plugin_collector_cb_t),
+ sc_plugin_cb_init, sc_plugin_cb_destroy,
+ &collector_list, "collector", name, callback, user_data);
+ if (! obj)
+ return -1;
+
+ if (interval)
+ SC_PLUGIN_CCB(obj)->ccb_interval = *interval;
+ else {
+ sc_time_t tmp = sc_plugin_get_ctx().interval;
+
+ if (tmp > 0)
+ SC_PLUGIN_CCB(obj)->ccb_interval = tmp;
+ else
+ SC_PLUGIN_CCB(obj)->ccb_interval = 0;
+ }
+
+ if (! (SC_PLUGIN_CCB(obj)->ccb_next_update = sc_gettime())) {
+ char errbuf[1024];
+ fprintf(stderr, "plugin: Failed to determine current time: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ sc_object_deref(obj);
+ return -1;
+ }
+
+ if (sc_llist_insert_sorted(collector_list, obj,
+ sc_plugin_cmp_next_update)) {
+ sc_object_deref(obj);
+ return -1;
+ }
+
+ /* pass control to the list */
+ sc_object_deref(obj);
+
+ fprintf(stderr, "plugin: Registered collector callback '%s' "
+ "(interval = %.3fs).\n", name,
+ SC_TIME_TO_DOUBLE(SC_PLUGIN_CCB(obj)->ccb_interval));
+ return 0;
+} /* sc_plugin_register_collector */
+
+sc_plugin_ctx_t
+sc_plugin_get_ctx(void)
+{
+ sc_plugin_ctx_t *ctx;
+
+ if (! plugin_ctx_key_initialized)
+ sc_plugin_ctx_init();
+ ctx = pthread_getspecific(plugin_ctx_key);
+
+ if (! ctx)
+ ctx = sc_plugin_ctx_create();
+ if (! ctx)
+ return plugin_default_ctx;
+ return *ctx;
+} /* sc_plugin_get_ctx */
+
+sc_plugin_ctx_t
+sc_plugin_set_ctx(sc_plugin_ctx_t ctx)
+{
+ sc_plugin_ctx_t *tmp;
+ sc_plugin_ctx_t old;
+
+ if (! plugin_ctx_key_initialized)
+ sc_plugin_ctx_init();
+ tmp = pthread_getspecific(plugin_ctx_key);
+
+ if (! tmp)
+ tmp = sc_plugin_ctx_create();
+ if (! tmp)
+ return plugin_default_ctx;
+
+ old = *tmp;
+ *tmp = ctx;
+ return old;
+} /* sc_plugin_set_ctx */
+
+int
+sc_plugin_configure(const char *name, oconfig_item_t *ci)
+{
+ sc_plugin_cb_t *plugin;
+ sc_plugin_config_cb callback;
+
+ sc_plugin_ctx_t old_ctx;
+
+ int status;
+
+ if ((! name) || (! ci))
+ return -1;
+
+ plugin = sc_plugin_find_by_name(config_list, name);
+ if (! plugin) {
+ fprintf(stderr, "plugin: Plugin '%s' did not register "
+ "a config callback.\n", name);
+ errno = ENOENT;
+ return -1;
+ }
+
+ old_ctx = sc_plugin_set_ctx(plugin->cb_ctx);
+ callback = plugin->cb_callback;
+ status = callback(ci);
+ sc_plugin_set_ctx(old_ctx);
+ return status;
+} /* sc_plugin_configure */
+
+int
+sc_plugin_init_all(void)
+{
+ sc_llist_iter_t *iter;
+
+ iter = sc_llist_get_iter(init_list);
+ while (sc_llist_iter_has_next(iter)) {
+ sc_plugin_init_cb callback;
+ sc_plugin_ctx_t old_ctx;
+
+ sc_object_t *obj = sc_llist_iter_get_next(iter);
+ assert(obj);
+
+ callback = SC_PLUGIN_CB(obj)->cb_callback;
+
+ old_ctx = sc_plugin_set_ctx(SC_PLUGIN_CB(obj)->cb_ctx);
+ if (callback(SC_PLUGIN_CB(obj)->cb_user_data)) {
+ /* XXX: unload plugin */
+ }
+ sc_plugin_set_ctx(old_ctx);
+ }
+ return 0;
+} /* sc_plugin_init_all */
+
+int
+sc_plugin_collector_loop(sc_plugin_loop_t *loop)
+{
+ if ((! collector_list) || (! loop))
+ return -1;
+
+ while (loop->do_loop) {
+ sc_plugin_collector_cb callback;
+ sc_plugin_ctx_t old_ctx;
+
+ sc_time_t interval, now;
+
+ sc_object_t *obj = sc_llist_shift(collector_list);
+ if (! obj)
+ return -1;
+
+ callback = SC_PLUGIN_CCB(obj)->ccb_callback;
+
+ if (! (now = sc_gettime())) {
+ char errbuf[1024];
+ fprintf(stderr, "plugin: Failed to determine current time: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ now = SC_PLUGIN_CCB(obj)->ccb_next_update;
+ }
+
+ if (now < SC_PLUGIN_CCB(obj)->ccb_next_update) {
+ interval = SC_PLUGIN_CCB(obj)->ccb_next_update - now;
+
+ errno = 0;
+ while (loop->do_loop && sc_sleep(interval, &interval)) {
+ if (errno != EINTR) {
+ char errbuf[1024];
+ fprintf(stderr, "plugin: Failed to sleep: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+ errno = 0;
+ }
+
+ if (! loop->do_loop)
+ return 0;
+ }
+
+ old_ctx = sc_plugin_set_ctx(SC_PLUGIN_CCB(obj)->ccb_ctx);
+ if (callback(SC_PLUGIN_CCB(obj)->ccb_user_data)) {
+ /* XXX */
+ }
+ sc_plugin_set_ctx(old_ctx);
+
+ interval = SC_PLUGIN_CCB(obj)->ccb_interval;
+ if (! interval)
+ interval = loop->default_interval;
+ if (! interval) {
+ fprintf(stderr, "plugin: No interval configured "
+ "for plugin '%s'; skipping any further "
+ "iterations.\n", SC_PLUGIN_CCB(obj)->ccb_name);
+ sc_object_deref(obj);
+ continue;
+ }
+
+ SC_PLUGIN_CCB(obj)->ccb_next_update += interval;
+
+ if (! (now = sc_gettime())) {
+ char errbuf[1024];
+ fprintf(stderr, "plugin: Failed to determine current time: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ now = SC_PLUGIN_CCB(obj)->ccb_next_update;
+ }
+
+ if (now > SC_PLUGIN_CCB(obj)->ccb_next_update) {
+ fprintf(stderr, "plugin: Plugin '%s' took too long; "
+ "skipping iterations to keep up.\n",
+ SC_PLUGIN_CCB(obj)->ccb_name);
+ SC_PLUGIN_CCB(obj)->ccb_next_update = now;
+ }
+
+ if (sc_llist_insert_sorted(collector_list, obj,
+ sc_plugin_cmp_next_update)) {
+ fprintf(stderr, "plugin: Failed to re-insert "
+ "plugin '%s' into collector list.\n",
+ SC_PLUGIN_CCB(obj)->ccb_name);
+ sc_object_deref(obj);
+ return -1;
+ }
+
+ /* pass control back to the list */
+ sc_object_deref(obj);
+ }
+ return 0;
+} /* sc_plugin_read_loop */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/core/store.c b/src/core/store.c
--- /dev/null
+++ b/src/core/store.c
@@ -0,0 +1,420 @@
+/*
+ * syscollector - src/core/store.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "syscollector.h"
+#include "core/store.h"
+#include "utils/llist.h"
+#include "utils/string.h"
+
+#include <assert.h>
+
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <pthread.h>
+
+/*
+ * private variables
+ */
+
+static sc_llist_t *host_list = NULL;
+static pthread_rwlock_t host_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+/*
+ * private helper functions
+ */
+
+static int
+sc_store_obj_cmp_by_name(const sc_object_t *a, const sc_object_t *b)
+{
+ const sc_store_obj_t *h1 = (const sc_store_obj_t *)a;
+ const sc_store_obj_t *h2 = (const sc_store_obj_t *)b;
+
+ assert(h1 && h2);
+ return strcasecmp(h1->name, h2->name);
+} /* sc_store_obj_cmp_by_name */
+
+static int
+sc_host_init(sc_object_t *obj, va_list ap)
+{
+ char *name = va_arg(ap, char *);
+
+ SC_HOST(obj)->host_name = strdup(name);
+ if (! SC_HOST(obj)->host_name)
+ return -1;
+
+ SC_HOST(obj)->host_last_update = sc_gettime();
+ /* ignore errors -> last_update will be updated later */
+
+ SC_HOST(obj)->services = sc_llist_create();
+ if (! SC_HOST(obj)->services)
+ return -1;
+ return 0;
+} /* sc_host_init */
+
+static void
+sc_host_destroy(sc_object_t *obj)
+{
+ assert(obj);
+
+ if (SC_HOST(obj)->host_name)
+ free(SC_HOST(obj)->host_name);
+
+ if (SC_HOST(obj)->services)
+ sc_llist_destroy(SC_HOST(obj)->services);
+} /* sc_host_destroy */
+
+static int
+sc_svc_init(sc_object_t *obj, va_list ap)
+{
+ char *hostname = va_arg(ap, char *);
+ char *name = va_arg(ap, char *);
+
+ SC_SVC(obj)->hostname = strdup(hostname);
+ SC_SVC(obj)->svc_name = strdup(name);
+ if ((! SC_SVC(obj)->hostname) || (! SC_SVC(obj)->svc_name))
+ return -1;
+
+ SC_SVC(obj)->svc_last_update = sc_gettime();
+ /* ignore errors -> last_update will be updated later */
+ return 0;
+} /* sc_svc_init */
+
+static void
+sc_svc_destroy(sc_object_t *obj)
+{
+ assert(obj);
+
+ if (SC_SVC(obj)->hostname)
+ free(SC_SVC(obj)->hostname);
+ if (SC_SVC(obj)->svc_name)
+ free(SC_SVC(obj)->svc_name);
+} /* sc_svc_destroy */
+
+/*
+ * public API
+ */
+
+sc_host_t *
+sc_host_create(char *name)
+{
+ sc_object_t *obj;
+
+ if (! name)
+ return NULL;
+
+ obj = sc_object_create(sizeof(sc_host_t), sc_host_init,
+ sc_host_destroy, name);
+ if (! obj)
+ return NULL;
+ return SC_HOST(obj);
+} /* sc_host_create */
+
+sc_host_t *
+sc_host_clone(const sc_host_t *host)
+{
+ sc_host_t *clone;
+
+ clone = sc_host_create(host->host_name);
+ if (! clone)
+ return NULL;
+
+ clone->host_last_update = host->host_last_update;
+ if (host->services) {
+ clone->services = sc_llist_clone(host->services);
+ if (! clone->services) {
+ sc_object_deref(SC_OBJ(clone));
+ return NULL;
+ }
+ }
+ else
+ clone->services = NULL;
+ return clone;
+} /* sc_host_clone */
+
+int
+sc_store_host(const sc_host_t *host)
+{
+ sc_time_t last_update;
+
+ sc_host_t *old;
+ int status = 0;
+
+ if ((! host) || (! host->host_name))
+ return -1;
+
+ last_update = host->host_last_update;
+ if (last_update <= 0)
+ last_update = sc_gettime();
+
+ pthread_rwlock_wrlock(&host_lock);
+
+ if (! host_list) {
+ if (! (host_list = sc_llist_create())) {
+ pthread_rwlock_unlock(&host_lock);
+ return -1;
+ }
+ }
+
+ old = SC_HOST(sc_llist_search(host_list, (const sc_object_t *)host,
+ sc_store_obj_cmp_by_name));
+
+ if (old) {
+ if (old->host_last_update > last_update) {
+ fprintf(stderr, "store: Cannot update host '%s' - "
+ "value too old (%"PRIscTIME" < %"PRIscTIME")\n",
+ host->host_name, last_update, old->host_last_update);
+ status = -1;
+ }
+ else {
+ old->host_last_update = last_update;
+ }
+ }
+ else {
+ sc_host_t *new = sc_host_clone(host);
+ if (! new) {
+ char errbuf[1024];
+ fprintf(stderr, "store: Failed to clone host object: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ if (! new->services) {
+ if (! (new->services = sc_llist_create())) {
+ char errbuf[1024];
+ fprintf(stderr, "store: Failed to initialize "
+ "host object '%s': %s\n", host->host_name,
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ sc_object_deref(SC_OBJ(new));
+ return -1;
+ }
+ }
+
+ status = sc_llist_insert_sorted(host_list, SC_OBJ(new),
+ sc_store_obj_cmp_by_name);
+
+ /* pass control to the list or destroy in case of an error */
+ sc_object_deref(SC_OBJ(new));
+ }
+
+ pthread_rwlock_unlock(&host_lock);
+ return status;
+} /* sc_store_host */
+
+const sc_host_t *
+sc_store_get_host(char *name)
+{
+ sc_host_t tmp = SC_HOST_INIT;
+ sc_host_t *host;
+
+ if (! name)
+ return NULL;
+
+ tmp.host_name = name;
+ host = SC_HOST(sc_llist_search(host_list, (const sc_object_t *)&tmp,
+ sc_store_obj_cmp_by_name));
+
+ if (! host)
+ return NULL;
+ return host;
+} /* sc_store_get_host */
+
+sc_service_t *
+sc_service_create(char *hostname, char *name)
+{
+ sc_object_t *obj;
+
+ if ((! hostname) || (! name))
+ return NULL;
+
+ obj = sc_object_create(sizeof(sc_service_t), sc_svc_init,
+ sc_svc_destroy, hostname, name);
+ if (! obj)
+ return NULL;
+ return SC_SVC(obj);
+} /* sc_service_create */
+
+sc_service_t *
+sc_service_clone(const sc_service_t *svc)
+{
+ sc_service_t *clone;
+
+ clone = sc_service_create(svc->hostname, svc->svc_name);
+ if (! clone)
+ return NULL;
+
+ clone->svc_last_update = svc->svc_last_update;
+ return clone;
+} /* sc_service_clone */
+
+int
+sc_store_service(const sc_service_t *svc)
+{
+ sc_host_t tmp = SC_HOST_INIT;
+ sc_host_t *host;
+
+ sc_service_t *old;
+
+ sc_time_t last_update;
+
+ int status = 0;
+
+ if (! svc)
+ return -1;
+
+ last_update = svc->svc_last_update;
+ if (last_update <= 0)
+ last_update = sc_gettime();
+
+ if (! host_list)
+ return -1;
+
+ pthread_rwlock_wrlock(&host_lock);
+
+ tmp.host_name = svc->hostname;
+ host = SC_HOST(sc_llist_search(host_list, (const sc_object_t *)&tmp,
+ sc_store_obj_cmp_by_name));
+
+ if (! host)
+ return -1;
+
+ old = SC_SVC(sc_llist_search(host->services, (const sc_object_t *)svc,
+ sc_store_obj_cmp_by_name));
+
+ if (old) {
+ if (old->host_last_update > last_update) {
+ fprintf(stderr, "store: Cannot update service '%s/%s' - "
+ "value too old (%"PRIscTIME" < %"PRIscTIME")\n",
+ svc->hostname, svc->svc_name, last_update,
+ old->host_last_update);
+ status = -1;
+ }
+ else {
+ old->svc_last_update = last_update;
+ }
+ }
+ else {
+ sc_service_t *new = sc_service_clone(svc);
+ if (! new) {
+ char errbuf[1024];
+ fprintf(stderr, "store: Failed to clone service object: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ status = sc_llist_insert_sorted(host->services, SC_OBJ(new),
+ sc_store_obj_cmp_by_name);
+
+ /* pass control to the list or destroy in case of an error */
+ sc_object_deref(SC_OBJ(new));
+ }
+
+ pthread_rwlock_unlock(&host_lock);
+ return status;
+} /* sc_store_service */
+
+const sc_service_t *
+sc_store_get_service(const sc_host_t *host, char *name)
+{
+ sc_service_t tmp = SC_SVC_INIT;
+ sc_service_t *svc;
+
+ if ((! host) || (! name))
+ return NULL;
+
+ tmp.svc_name = name;
+ svc = SC_SVC(sc_llist_search(host->services, (const sc_object_t *)&tmp,
+ sc_store_obj_cmp_by_name));
+
+ if (! svc)
+ return NULL;
+ return svc;
+} /* sc_store_get_service */
+
+int
+sc_store_dump(FILE *fh)
+{
+ sc_llist_iter_t *host_iter;
+
+ if (! fh)
+ return -1;
+
+ pthread_rwlock_rdlock(&host_lock);
+
+ host_iter = sc_llist_get_iter(host_list);
+ if (! host_iter)
+ return -1;
+
+ while (sc_llist_iter_has_next(host_iter)) {
+ sc_host_t *host = SC_HOST(sc_llist_iter_get_next(host_iter));
+ sc_llist_iter_t *svc_iter;
+
+ char time_str[64];
+
+ assert(host);
+
+ if (! sc_strftime(time_str, sizeof(time_str),
+ "%F %T %z", host->host_last_update))
+ snprintf(time_str, sizeof(time_str), "<error>");
+ time_str[sizeof(time_str) - 1] = '\0';
+
+ fprintf(fh, "Host '%s' (last updated: %s):\n",
+ host->host_name, time_str);
+
+ svc_iter = sc_llist_get_iter(host->services);
+ if (! svc_iter) {
+ char errbuf[1024];
+ fprintf(fh, "Failed to retrieve services: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ continue;
+ }
+
+ while (sc_llist_iter_has_next(svc_iter)) {
+ sc_service_t *svc = SC_SVC(sc_llist_iter_get_next(svc_iter));
+ assert(svc);
+
+ if (! sc_strftime(time_str, sizeof(time_str),
+ "%F %T %z", host->host_last_update))
+ snprintf(time_str, sizeof(time_str), "<error>");
+ time_str[sizeof(time_str) - 1] = '\0';
+
+ fprintf(fh, "\tService '%s' (last updated: %s)\n",
+ svc->svc_name, time_str);
+ }
+
+ sc_llist_iter_destroy(svc_iter);
+ }
+
+ sc_llist_iter_destroy(host_iter);
+ return 0;
+} /* sc_store_dump */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/daemon/config.c b/src/daemon/config.c
--- /dev/null
+++ b/src/daemon/config.c
@@ -0,0 +1,195 @@
+/*
+ * syscollector - src/daemon_config.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "syscollector.h"
+#include "core/plugin.h"
+#include "utils/time.h"
+
+#include "daemon/config.h"
+
+#include "liboconfig/oconfig.h"
+#include "liboconfig/utils.h"
+
+#include <assert.h>
+#include <strings.h>
+
+/*
+ * private variables
+ */
+
+static sc_time_t default_interval = 0;
+
+/*
+ * private helper functions
+ */
+
+static int
+config_get_interval(oconfig_item_t *ci, sc_time_t *interval)
+{
+ double interval_dbl = 0.0;
+
+ assert(ci && interval);
+
+ if (oconfig_get_number(ci, &interval_dbl)) {
+ fprintf(stderr, "config: Interval requires "
+ "a single numeric argument\n"
+ "\tUsage: Interval SECONDS\n");
+ return -1;
+ }
+
+ if (interval_dbl <= 0.0) {
+ fprintf(stderr, "config: Invalid interval: %f\n"
+ "\tInterval may not be less than or equal to zero.\n",
+ interval_dbl);
+ return -1;
+ }
+
+ *interval = DOUBLE_TO_SC_TIME(interval_dbl);
+ return 0;
+} /* config_get_interval */
+
+/*
+ * token parser
+ */
+
+typedef struct {
+ char *name;
+ int (*dispatcher)(oconfig_item_t *);
+} token_parser_t;
+
+static int
+daemon_set_interval(oconfig_item_t *ci)
+{
+ return config_get_interval(ci, &default_interval);
+} /* daemon_set_interval */
+
+static int
+daemon_load_backend(oconfig_item_t *ci)
+{
+ char plugin_name[1024];
+ char *name;
+
+ sc_plugin_ctx_t ctx = SC_PLUGIN_CTX_INIT;
+ sc_plugin_ctx_t old_ctx;
+
+ int status, i;
+
+ ctx.interval = default_interval;
+
+ if (oconfig_get_string(ci, &name)) {
+ fprintf(stderr, "config: LoadBackend requires a single "
+ "string argument\n"
+ "\tUsage: LoadBackend BACKEND\n");
+ return -1;
+ }
+
+ snprintf(plugin_name, sizeof(plugin_name), "backend/%s", name);
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *child = ci->children + i;
+
+ if (! strcasecmp(child->key, "Interval")) {
+ if (config_get_interval(child, &ctx.interval))
+ return -1;
+ }
+ else {
+ fprintf(stderr, "config: Unknown option '%s' inside 'LoadBackend' "
+ "-- see the documentation for details.\n", child->key);
+ return -1;
+ }
+ }
+
+ old_ctx = sc_plugin_set_ctx(ctx);
+ status = sc_plugin_load(plugin_name);
+ sc_plugin_set_ctx(old_ctx);
+ return status;
+} /* daemon_load_backend */
+
+static int
+daemon_configure_plugin(oconfig_item_t *ci)
+{
+ char *name;
+
+ assert(ci);
+
+ if (oconfig_get_string(ci, &name)) {
+ fprintf(stderr, "config: %s requires a single "
+ "string argument\n"
+ "\tUsage: LoadBackend BACKEND\n",
+ ci->key);
+ return -1;
+ }
+
+ return sc_plugin_configure(name, ci);
+} /* daemon_configure_backend */
+
+static token_parser_t token_parser_list[] = {
+ { "Interval", daemon_set_interval },
+ { "LoadBackend", daemon_load_backend },
+ { "Backend", daemon_configure_plugin },
+ { "Plugin", daemon_configure_plugin },
+ { NULL, NULL },
+};
+
+/*
+ * public API
+ */
+
+int
+daemon_parse_config(const char *filename)
+{
+ oconfig_item_t *ci;
+ int retval = 0, i;
+
+ ci = oconfig_parse_file(filename);
+ if (! ci)
+ return -1;
+
+ for (i = 0; i < ci->children_num; ++i) {
+ oconfig_item_t *child = ci->children + i;
+ int status = 1, j;
+
+ for (j = 0; token_parser_list[j].name; ++j) {
+ if (! strcasecmp(token_parser_list[j].name, child->key))
+ status = token_parser_list[j].dispatcher(child);
+ }
+
+ if (status) {
+ fprintf(stderr, "config: Failed to parse option '%s'\n",
+ child->key);
+ if (status > 0)
+ fprintf(stderr, "\tUnknown option '%s' -- "
+ "see the documentation for details\n",
+ child->key);
+ retval = -1;
+ }
+ }
+ return retval;
+} /* daemon_parse_config */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/daemon/syscollectord.c b/src/daemon/syscollectord.c
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * syscollector - src/syscollecord.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "syscollector.h"
+#include "core/plugin.h"
+#include "core/store.h"
+#include "utils/string.h"
+
+#include "daemon/config.h"
+
+#if HAVE_LIBGEN_H
+# include <libgen.h>
+#else /* HAVE_LIBGEN_H */
+# define basename(path) (path)
+#endif /* ! HAVE_LIBGEN_H */
+
+#include <errno.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <signal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#ifndef CONFIGFILE
+# define CONFIGFILE SYSCONFDIR"/syscollector/syscollectord.conf"
+#endif
+
+static sc_plugin_loop_t plugin_main_loop = SC_PLUGIN_LOOP_INIT;
+
+static void
+sigintterm_handler(int __attribute__((unused)) signo)
+{
+ plugin_main_loop.do_loop = 0;
+} /* sigintterm_handler */
+
+static void
+exit_usage(char *name, int status)
+{
+ printf(
+"Usage: %s <options>\n"
+
+"\nOptions:\n"
+" -C FILE the main configuration file\n"
+" default: "CONFIGFILE"\n"
+" -d run in background (daemonize)\n"
+"\n"
+" -h display this help and exit\n"
+" -V display the version number and copyright\n"
+
+"\nsyscollectord "SC_VERSION_STRING SC_VERSION_EXTRA", "PACKAGE_URL"\n",
+basename(name));
+ exit(status);
+} /* exit_usage */
+
+static void
+exit_version(void)
+{
+ printf("syscollectord version "SC_VERSION_STRING SC_VERSION_EXTRA", "
+ "built "BUILD_DATE"\n"
+ "using libsyscollection verion %s%s\n"
+ "Copyright (C) 2012 "PACKAGE_MAINTAINER"\n"
+
+ "\nThis is free software under the terms of the BSD license, see "
+ "the source for\ncopying conditions. There is NO WARRANTY; not "
+ "even for MERCHANTABILITY or\nFITNESS FOR A PARTICULAR "
+ "PURPOSE.\n", sc_version_string(), sc_version_extra());
+ exit(0);
+} /* exit_version */
+
+static int
+daemonize(void)
+{
+ pid_t pid;
+
+ if ((pid = fork()) < 0) {
+ char errbuf[1024];
+ fprintf(stderr, "Failed to fork to background: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return errno;
+ }
+ else if (pid != 0) {
+ /* parent */
+ exit(0);
+ }
+
+ if (chdir("/")) {
+ char errbuf[1024];
+ fprintf(stderr, "Failed to change working directory to /: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return errno;
+ }
+
+ /* detach from session */
+ setsid();
+
+ close(0);
+ if (open("/dev/null", O_RDWR)) {
+ char errbuf[1024];
+ fprintf(stderr, "Failed to connect stdin to '/dev/null': %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return errno;
+ }
+
+ close(1);
+ if (dup(0) != 1) {
+ char errbuf[1024];
+ fprintf(stderr, "Could not connect stdout to '/dev/null': %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return errno;
+ }
+
+ close(2);
+ if (dup(0) != 2) {
+ char errbuf[1024];
+ fprintf(stdout, "Could not connect stderr to '/dev/null': %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return errno;
+ }
+ return 0;
+} /* daemonize */
+
+int
+main(int argc, char **argv)
+{
+ char *config_filename = NULL;
+ _Bool daemon = 0;
+
+ struct sigaction sa_intterm;
+
+ while (42) {
+ int opt = getopt(argc, argv, "C:dhV");
+
+ if (-1 == opt)
+ break;
+
+ switch (opt) {
+ case 'C':
+ config_filename = optarg;
+ break;
+ case 'd':
+ daemon = 1;
+ break;
+
+ case 'h':
+ exit_usage(argv[0], 0);
+ break;
+ case 'V':
+ exit_version();
+ break;
+ default:
+ exit_usage(argv[0], 1);
+ }
+ }
+
+ if (optind < argc)
+ exit_usage(argv[0], 1);
+
+ if (! config_filename)
+ config_filename = CONFIGFILE;
+
+ if (daemon_parse_config(config_filename)) {
+ fprintf(stderr, "Failed to parse configuration file.\n");
+ exit(1);
+ }
+
+ memset(&sa_intterm, 0, sizeof(sa_intterm));
+ sa_intterm.sa_handler = sigintterm_handler;
+ sa_intterm.sa_flags = 0;
+
+ if (sigaction(SIGINT, &sa_intterm, /* old action */ NULL)) {
+ char errbuf[1024];
+ fprintf(stderr, "Failed to install signal handler for SIGINT: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ exit(1);
+ }
+ if (sigaction(SIGTERM, &sa_intterm, /* old action */ NULL)) {
+ char errbuf[1024];
+ fprintf(stderr, "Failed to install signal handler for SIGTERM: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ exit(1);
+ }
+
+ if (daemon)
+ if (daemonize())
+ exit(1);
+
+ fprintf(stderr, "syscollectord "SC_VERSION_STRING" (pid %i) "
+ "initialized successfully\n", (int)getpid());
+
+ sc_plugin_init_all();
+ sc_plugin_collector_loop(&plugin_main_loop);
+
+ fprintf(stderr, "Shutting down syscollector "SC_VERSION_STRING
+ " (pid %i)\n", (int)getpid());
+
+ fprintf(stderr, "Store dump:\n");
+ sc_store_dump(stderr);
+ return 0;
+} /* main */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/daemon/syscollectord.conf b/src/daemon/syscollectord.conf
--- /dev/null
@@ -0,0 +1,2 @@
+LoadBackend "collectd"
+
diff --git a/src/include/core/object.h b/src/include/core/object.h
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * syscollector - src/include/core/object.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_CORE_OBJECT_H
+#define SC_CORE_OBJECT_H 1
+
+#include <stdarg.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sc_object;
+typedef struct sc_object sc_object_t;
+
+struct sc_object {
+ int ref_cnt;
+ void (*destructor)(sc_object_t *);
+ size_t size;
+};
+#define SC_OBJECT_INIT { 1, NULL, 0 }
+
+typedef struct {
+ sc_object_t super;
+ void *data;
+ void (*destructor)(void *);
+} sc_object_wrapper_t;
+
+#define SC_OBJ(obj) ((sc_object_t *)(obj))
+#define SC_OBJ_WRAPPER(obj) ((sc_object_wrapper_t *)(obj))
+
+/*
+ * sc_object_create:
+ * Allocates a new sc_object_t of the specified 'size'. The object will be
+ * initialized to zero and then passed on to the 'init' function (if
+ * specified). If specified, the 'destructor' will be called, when the
+ * reference count drops to zero and before freeing the memory allocated by
+ * the object itself.
+ *
+ * If the init function fails (returns a non-zero value), the object will be
+ * destructed and destroyed.
+ *
+ * The reference count of the new object will be 1.
+ *
+ * Returns:
+ * - the newly allocated object
+ * - NULL on error
+ */
+sc_object_t *
+sc_object_create(size_t size, int (*init)(sc_object_t *, va_list),
+ void (*destructor)(sc_object_t *), ...);
+
+/*
+ * sc_object_create_wrapper:
+ * Create a new sc_object_t wrapping some arbitrary other object.
+ */
+sc_object_t *
+sc_object_create_wrapper(void *data, void (*destructor)(void *));
+
+/*
+ * sc_object_deref:
+ * Dereference the object and free the allocated memory in case the ref-count
+ * drops to zero. In case a 'destructor' had been registered with the object,
+ * it will be called before freeing the memory.
+ */
+void
+sc_object_deref(sc_object_t *obj);
+
+/*
+ * sc_object_ref:
+ * Take ownership of the specified object, that is, increment the reference
+ * count by one.
+ */
+void
+sc_object_ref(sc_object_t *obj);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_CORE_OBJECT_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/core/plugin.h b/src/include/core/plugin.h
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * syscollector - src/include/core/plugin.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_CORE_PLUGIN_H
+#define SC_CORE_PLUGIN_H 1
+
+#include "syscollector.h"
+#include "core/object.h"
+#include "utils/time.h"
+
+#include "liboconfig/oconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ sc_time_t interval;
+} sc_plugin_ctx_t;
+#define SC_PLUGIN_CTX_INIT { 0 }
+
+struct sc_plugin_info;
+typedef struct sc_plugin_info sc_plugin_info_t;
+
+/* this should be used in the header of a plugin to avoid
+ * missing prototype warnings/errors for the plugin init
+ * function */
+#define SC_PLUGIN_MAGIC \
+ int sc_module_init(sc_plugin_info_t *info);
+
+typedef struct {
+ _Bool do_loop;
+ sc_time_t default_interval;
+} sc_plugin_loop_t;
+#define SC_PLUGIN_LOOP_INIT { 1, 0 }
+
+/*
+ * sc_plugin_load:
+ * Load (any type of) plugin by loading the shared object file and calling the
+ * sc_module_init function.
+ */
+int
+sc_plugin_load(const char *name);
+
+/*
+ * sc_plugin_set_info:
+ * Fill in the fields of the sc_plugin_info_t object passed to the
+ * sc_module_init function. This information is used to identify the plugin
+ * and also to provide additional information to the user.
+ */
+enum {
+ SC_PLUGIN_INFO_NAME, /* plugin name: string */
+ SC_PLUGIN_INFO_DESC, /* plugin description: string */
+ SC_PLUGIN_INFO_COPYRIGHT, /* plugin copyright: string */
+ SC_PLUGIN_INFO_LICENSE, /* plugin license: string */
+ SC_PLUGIN_INFO_VERSION, /* libsyscollector version: integer */
+ SC_PLUGIN_INFO_PLUGIN_VERSION /* plugin version: integer */
+};
+
+int
+sc_plugin_set_info(sc_plugin_info_t *info, int type, ...);
+
+/*
+ * plugin callback functions
+ */
+
+typedef int (*sc_plugin_config_cb)(oconfig_item_t *ci);
+typedef int (*sc_plugin_init_cb)(sc_object_t *user_data);
+typedef int (*sc_plugin_collector_cb)(sc_object_t *user_data);
+typedef int (*sc_plugin_shutdown_cb)(sc_object_t *user_data);
+
+/*
+ * sc_plugin_register_config:
+ * Register a "config" function. This will be used to pass on the
+ * configuration for a plugin. The plugin has to make sure that the function
+ * can be called multiple times in order to process multiple <Plugin> blocks
+ * specified in the configuration file(s).
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_plugin_register_config(const char *name, sc_plugin_config_cb callback);
+
+/*
+ * sc_plugin_register_init:
+ * Register an "init" function. All "init" functions will be called after
+ * finishing the config parsing and before starting any other work. The
+ * functions will be called in the same order as they have been registered,
+ * that is, functions of different plugins will be called in the same order as
+ * the appropriate "Load" statements in the config file.
+ *
+ * If the "init" function returns a non-zero value, *all* callbacks of the
+ * plugin will be unloaded.
+ *
+ * Arguments:
+ * - user_data: If specified, this will be passed on to each call of the
+ * callback. The function will take ownership of the object, that is,
+ * increment the reference count by one. In case the caller does not longer
+ * use the object for other purposes, it should thus deref it.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_plugin_register_init(const char *name, sc_plugin_init_cb callback,
+ sc_object_t *user_data);
+
+/*
+ * sc_plugin_register_collector:
+ * Register a "collector" function. This is where a backend is doing its main
+ * work. This function will be called whenever an update of a backend has been
+ * requested (either by regular interval or by user request). The backend
+ * should then query the appropriate data-source and submit all values to the
+ * core.
+ *
+ * Arguments:
+ * - interval: Specifies the regular interval at which to update the backend.
+ * If this is NULL, global settings will be used.
+ * - user_data: If specified, this will be passed on to each call of the
+ * callback. The function will take ownership of the object, that is,
+ * increment the reference count by one. In case the caller does not longer
+ * use the object for other purposes, it should thus deref it.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_plugin_register_collector(const char *name, sc_plugin_collector_cb callback,
+ const sc_time_t *interval, sc_object_t *user_data);
+
+/*
+ * sc_plugin_register_shutdown:
+ * Register a "shutdown" function to be called after stopping all update
+ * processes and before shutting down the daemon.
+ *
+ * Arguments:
+ * - user_data: If specified, this will be passed on to each call of the
+ * callback. The function will take ownership of the object, that is,
+ * increment the reference count by one. In case the caller does not longer
+ * use the object for other purposes, it should thus deref it.
+ */
+int
+sc_plugin_register_shutdown(const char *name, sc_plugin_shutdown_cb callback,
+ sc_object_t *user_data);
+
+/*
+ * sc_plugin_get_ctx, sc_plugin_set_ctx:
+ * The plugin context defines a set of settings that are available whenever a
+ * plugin has been called. It may be used to pass around various information
+ * between the different component of the library without having each and
+ * every plugin care about it.
+ */
+sc_plugin_ctx_t
+sc_plugin_get_ctx(void);
+sc_plugin_ctx_t
+sc_plugin_set_ctx(sc_plugin_ctx_t ctx);
+
+/*
+ * sc_plugin_configure:
+ * Configure the plugin called 'name' (according to the registered config
+ * callback) using the config tree 'ci'.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_plugin_configure(const char *name, oconfig_item_t *ci);
+
+/*
+ * sc_plugin_init_all:
+ * Initialize all plugins using their registered "init" function.
+ */
+int
+sc_plugin_init_all(void);
+
+/*
+ * sc_plugin_collector_loop:
+ * Loop until loop->do_loop is false, calling the next collector function on
+ * each iteration and once its next update interval is passed.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+sc_plugin_collector_loop(sc_plugin_loop_t *loop);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_CORE_PLUGIN_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/core/store.h b/src/include/core/store.h
--- /dev/null
+++ b/src/include/core/store.h
@@ -0,0 +1,105 @@
+/*
+ * syscollector - src/include/core/store.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_CORE_STORE_H
+#define SC_CORE_STORE_H 1
+
+#include "syscollector.h"
+#include "core/object.h"
+#include "utils/time.h"
+#include "utils/llist.h"
+
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct {
+ sc_object_t parent;
+
+ sc_time_t last_update;
+ char *name;
+} sc_store_obj_t;
+#define SC_STORE_OBJ_INIT { SC_OBJECT_INIT, 0, NULL }
+#define SC_STORE_OBJ(obj) ((sc_store_obj_t *)(obj))
+
+typedef struct {
+ sc_store_obj_t parent;
+#define svc_last_update parent.last_update
+#define svc_name parent.name
+
+ char *hostname;
+} sc_service_t;
+#define SC_SVC_INIT { SC_STORE_OBJ_INIT, NULL }
+#define SC_SVC(obj) ((sc_service_t *)(obj))
+
+typedef struct {
+ sc_store_obj_t parent;
+#define host_last_update parent.last_update
+#define host_name parent.name
+
+ sc_llist_t *services;
+} sc_host_t;
+#define SC_HOST_INIT { SC_STORE_OBJ_INIT, NULL }
+#define SC_HOST(obj) ((sc_host_t *)(obj))
+
+sc_host_t *
+sc_host_create(char *name);
+
+sc_host_t *
+sc_host_clone(const sc_host_t *host);
+
+int
+sc_store_host(const sc_host_t *host);
+
+const sc_host_t *
+sc_store_get_host(char *name);
+
+sc_service_t *
+sc_service_create(char *hostname, char *name);
+
+sc_service_t *
+sc_service_clone(const sc_service_t *svc);
+
+int
+sc_store_service(const sc_service_t *svc);
+
+const sc_service_t *
+sc_store_get_service(const sc_host_t *host, char *name);
+
+int
+sc_store_dump(FILE *fh);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_CORE_STORE_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/syscollector.h.in b/src/include/syscollector.h.in
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * syscollector - src/include/syscollector.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SYSCOLLECTOR_H
+#define SYSCOLLECTOR_H 1
+
+#define SC_VERSION_MAJOR @SC_VERSION_MAJOR@
+#define SC_VERSION_MINOR @SC_VERSION_MINOR@
+#define SC_VERSION_PATCH @SC_VERSION_PATCH@
+
+#define SC_VERSION_EXTRA "@SC_VERSION_EXTRA@"
+
+#define SC_VERSION_STRING "@SC_VERSION_STRING@"
+
+#define SC_VERSION_ENCODE(major, minor, patch) \
+ ((major) * 10000 + (minor) * 100 + (patch))
+#define SC_VERSION_DECODE(version) \
+ (int)((version) / 10000), \
+ (int)((version) / 100) - (int)((version) / 10000) * 100, \
+ (int)(version) - (int)((version) / 100) * 100
+
+#define SC_VERSION SC_VERSION_ENCODE(SC_VERSION_MAJOR, SC_VERSION_MINOR, \
+ SC_VERSION_PATCH)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+unsigned int
+sc_version(void);
+
+const char *
+sc_version_string(void);
+
+const char *
+sc_version_extra(void);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SYSCOLLECTOR_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/utils/llist.h b/src/include/utils/llist.h
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * syscollector - src/include/utils/llist.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_UTILS_LLIST_H
+#define SC_UTILS_LLIST_H 1
+
+#include "core/object.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sc_llist;
+typedef struct sc_llist sc_llist_t;
+
+struct sc_llist_iter;
+typedef struct sc_llist_iter sc_llist_iter_t;
+
+/*
+ * sc_llist_create, sc_llist_destroy:
+ * Create and destroy a doubly linked list object.
+ *
+ * sc_llist_create returns NULL on error.
+ * sc_llist_destroy will also destroy all remaining elements, thus releasing
+ * the included objects (decrement the ref-count).
+ */
+sc_llist_t *
+sc_llist_create(void);
+void
+sc_llist_destroy(sc_llist_t *list);
+
+/*
+ * sc_llist_clone:
+ * Clone an existing list. The objects stored in the list will not be copied
+ * but rather their reference count incremented.
+ *
+ * Returns:
+ * - the copied list on success
+ * - NULL else
+ */
+sc_llist_t *
+sc_llist_clone(sc_llist_t *list);
+
+/*
+ * sc_llist_append:
+ * Append the given 'obj' to the end of 'list'. The list will take ownership
+ * of the object, that is, increment the reference count by one. In case the
+ * caller does not longer use the object for other purposes, it should thus
+ * deref it.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value on failure
+ */
+int
+sc_llist_append(sc_llist_t *list, sc_object_t *obj);
+
+/*
+ * sc_llist_insert:
+ * Insert the new element at the specified position (zero being the head of
+ * the list; the length of the list being the tail)). If the index is greater
+ * than the length of the list (i.e. past the tail of the list), an error is
+ * returned. The list will take ownership of the object, that is, increment
+ * the reference count by one. In case the caller does not longer use the
+ * object for other purposes, it should thus deref it.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value on failure
+ */
+int
+sc_llist_insert(sc_llist_t *list, sc_object_t *obj, size_t index);
+
+/*
+ * sc_llist_insert_sorted:
+ * Insert the given 'obj' in the 'list' using a sort order as determined by
+ * the 'compare' function. The function will insert the new entry before the
+ * first entry which sorts later than the new entry. It will not ensure that
+ * the rest of the list is sorted. The list will take ownership of the object,
+ * that is, increment the reference count by one. In case the caller does not
+ * longer use the object for other purposes, it should thus deref it.
+ *
+ * The 'compare' function should return less than zero, zero, greater than
+ * zero if the first argument sorts less than, equal or greater than the
+ * second argument respectively.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value on failure
+ */
+int
+sc_llist_insert_sorted(sc_llist_t *list, sc_object_t *obj,
+ int (*compare)(const sc_object_t *, const sc_object_t *));
+
+/* sc_llist_search:
+ * Search for a 'key' in the given 'list'. The function will return the first
+ * entry that matches the specified 'key'. For that purpose, the 'compare'
+ * function is used. It should return 0 iff the two arguments compare equal.
+ *
+ * Returns:
+ * - a pointer the sc_object_t containing the matching entry
+ * - NULL else
+ */
+sc_object_t *
+sc_llist_search(sc_llist_t *list, const sc_object_t *key,
+ int (*compare)(const sc_object_t *, const sc_object_t *));
+
+/*
+ * sc_llist_shift:
+ * Removes and returns the first element of the list. The ref-count of the
+ * item will not be changed, that is, if the element will not be used any
+ * further, it should be re-referenced by the caller.
+ *
+ * Returns:
+ * - the former first element of the list
+ * - NULL if the list is empty
+ */
+sc_object_t *
+sc_llist_shift(sc_llist_t *list);
+
+/* sc_llist_get_iter, sc_llist_iter_has_next, sc_llist_iter_get_next:
+ * Iterate through the list, element by element.
+ *
+ * sc_llist_iter_get_next returns NULL if there is no next element.
+ */
+sc_llist_iter_t *
+sc_llist_get_iter(sc_llist_t *list);
+void
+sc_llist_iter_destroy(sc_llist_iter_t *iter);
+
+_Bool
+sc_llist_iter_has_next(sc_llist_iter_t *iter);
+sc_object_t *
+sc_llist_iter_get_next(sc_llist_iter_t *iter);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_UTILS_LLIST_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/utils/string.h b/src/include/utils/string.h
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * syscollector - src/include/utils/string.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_UTILS_STRING_H
+#define SC_UTILS_STRING_H 1
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+char *
+sc_strerror(int errnum, char *strerrbuf, size_t buflen);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_UTILS_STRING_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/utils/time.h b/src/include/utils/time.h
--- /dev/null
+++ b/src/include/utils/time.h
@@ -0,0 +1,73 @@
+/*
+ * syscollector - src/include/utils/time.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_UTILS_TIME_H
+#define SC_UTILS_TIME_H 1
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * sc_time_t:
+ * The time, in nano-seconds, since the epoch.
+ */
+typedef uint64_t sc_time_t;
+#define PRIscTIME PRIu64
+
+#define SECS_TO_SC_TIME(s) ((sc_time_t)(s) * (sc_time_t)1000000000)
+#define SC_TIME_TO_SECS(t) ((t) / (sc_time_t)1000000000)
+
+#define NSECS_TO_SC_TIME(ns) ((sc_time_t)ns)
+
+#define DOUBLE_TO_SC_TIME(d) ((sc_time_t)((d) * 1000000000.0))
+#define SC_TIME_TO_DOUBLE(t) ((double)(t) / 1000000000.0)
+
+#define TIMESPEC_TO_SC_TIME(ts) (SECS_TO_SC_TIME((ts).tv_sec) \
+ + NSECS_TO_SC_TIME((ts).tv_nsec))
+
+sc_time_t
+sc_gettime(void);
+
+int
+sc_sleep(sc_time_t reg, sc_time_t *rem);
+
+size_t
+sc_strftime(char *s, size_t len, const char *format, sc_time_t);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_UTILS_TIME_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/include/utils/unixsock.h b/src/include/utils/unixsock.h
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * syscollector - src/include/utils/unixsock.h
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 SC_UTILS_UNIXSOCK_H
+#define SC_UTILS_UNIXSOCK_H 1
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct sc_unixsock_client;
+typedef struct sc_unixsock_client sc_unixsock_client_t;
+
+sc_unixsock_client_t *
+sc_unixsock_client_create(const char *path);
+
+int
+sc_unixsock_client_connect(sc_unixsock_client_t *client);
+
+int
+sc_unixsock_client_send(sc_unixsock_client_t *client, const char *msg);
+
+char *
+sc_unixsock_client_recv(sc_unixsock_client_t *client, char *buffer, size_t buflen);
+
+void
+sc_unixsock_client_destroy(sc_unixsock_client_t *client);
+
+const char *
+sc_unixsock_client_path(sc_unixsock_client_t *client);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* ! SC_UTILS_UNIXSOCK_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/liboconfig/COPYING b/src/liboconfig/COPYING
--- /dev/null
+++ b/src/liboconfig/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 2 of the License, 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
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/src/liboconfig/Makefile.am b/src/liboconfig/Makefile.am
--- /dev/null
@@ -0,0 +1,10 @@
+AUTOMAKE_OPTIONS = foreign no-dependencies
+
+BUILT_SOURCES = parser.h
+#CLEANFILES = parser.[ch] scanner.c
+AM_YFLAGS = -d
+
+noinst_LTLIBRARIES = liboconfig.la
+
+liboconfig_la_LDFLAGS = -version-info 0:0:0 $(LEXLIB)
+liboconfig_la_SOURCES = oconfig.c oconfig.h aux_types.h scanner.l parser.y utils.c utils.h
diff --git a/src/liboconfig/aux_types.h b/src/liboconfig/aux_types.h
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef AUX_TYPES_H
+#define AUX_TYPES_H 1
+
+struct statement_list_s
+{
+ oconfig_item_t *statement;
+ int statement_num;
+};
+typedef struct statement_list_s statement_list_t;
+
+struct argument_list_s
+{
+ oconfig_value_t *argument;
+ int argument_num;
+};
+typedef struct argument_list_s argument_list_t;
+
+#endif /* AUX_TYPES_H */
diff --git a/src/liboconfig/oconfig.c b/src/liboconfig/oconfig.c
--- /dev/null
+++ b/src/liboconfig/oconfig.c
@@ -0,0 +1,217 @@
+/**
+ * oconfig - src/oconfig.c
+ * Copyright (C) 2006,2007 Florian octo Forster <octo at verplant.org>
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+
+#include "oconfig.h"
+
+extern FILE *yyin;
+
+oconfig_item_t *ci_root;
+const char *c_file;
+
+static void yyset_in (FILE *fd)
+{
+ yyin = fd;
+} /* void yyset_in */
+
+oconfig_item_t *oconfig_parse_fh (FILE *fh)
+{
+ int status;
+ oconfig_item_t *ret;
+
+ char file[10];
+
+ yyset_in (fh);
+
+ if (NULL == c_file) {
+ int status;
+
+ status = snprintf (file, sizeof (file), "<fd#%d>", fileno (fh));
+
+ if ((status < 0) || (status >= sizeof (file))) {
+ c_file = "<unknown>";
+ }
+ else {
+ file[sizeof (file) - 1] = '\0';
+ c_file = file;
+ }
+ }
+
+ status = yyparse ();
+ if (status != 0)
+ {
+ fprintf (stderr, "yyparse returned error #%i\n", status);
+ return (NULL);
+ }
+
+ c_file = NULL;
+
+ ret = ci_root;
+ ci_root = NULL;
+ yyset_in ((FILE *) 0);
+
+ return (ret);
+} /* oconfig_item_t *oconfig_parse_fh */
+
+oconfig_item_t *oconfig_parse_file (const char *file)
+{
+ FILE *fh;
+ oconfig_item_t *ret;
+
+ c_file = file;
+
+ fh = fopen (file, "r");
+ if (fh == NULL)
+ {
+ fprintf (stderr, "fopen (%s) failed: %s\n", file, strerror (errno));
+ return (NULL);
+ }
+
+ ret = oconfig_parse_fh (fh);
+ fclose (fh);
+
+ c_file = NULL;
+
+ return (ret);
+} /* oconfig_item_t *oconfig_parse_file */
+
+oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig)
+{
+ oconfig_item_t *ci_copy;
+
+ ci_copy = (oconfig_item_t *) malloc (sizeof (*ci_copy));
+ if (ci_copy == NULL)
+ {
+ fprintf (stderr, "malloc failed.\n");
+ return (NULL);
+ }
+ memset (ci_copy, 0, sizeof (*ci_copy));
+ ci_copy->values = NULL;
+ ci_copy->parent = NULL;
+ ci_copy->children = NULL;
+
+ ci_copy->key = strdup (ci_orig->key);
+ if (ci_copy->key == NULL)
+ {
+ fprintf (stderr, "strdup failed.\n");
+ free (ci_copy);
+ return (NULL);
+ }
+
+ if (ci_orig->values_num > 0) /* {{{ */
+ {
+ int i;
+
+ ci_copy->values = (oconfig_value_t *) calloc (ci_orig->values_num,
+ sizeof (*ci_copy->values));
+ if (ci_copy->values == NULL)
+ {
+ fprintf (stderr, "calloc failed.\n");
+ free (ci_copy->key);
+ free (ci_copy);
+ return (NULL);
+ }
+ ci_copy->values_num = ci_orig->values_num;
+
+ for (i = 0; i < ci_copy->values_num; i++)
+ {
+ ci_copy->values[i].type = ci_orig->values[i].type;
+ if (ci_copy->values[i].type == OCONFIG_TYPE_STRING)
+ {
+ ci_copy->values[i].value.string
+ = strdup (ci_orig->values[i].value.string);
+ if (ci_copy->values[i].value.string == NULL)
+ {
+ fprintf (stderr, "strdup failed.\n");
+ oconfig_free (ci_copy);
+ return (NULL);
+ }
+ }
+ else /* ci_copy->values[i].type != OCONFIG_TYPE_STRING) */
+ {
+ ci_copy->values[i].value = ci_orig->values[i].value;
+ }
+ }
+ } /* }}} if (ci_orig->values_num > 0) */
+
+ if (ci_orig->children_num > 0) /* {{{ */
+ {
+ int i;
+
+ ci_copy->children = (oconfig_item_t *) calloc (ci_orig->children_num,
+ sizeof (*ci_copy->children));
+ if (ci_copy->children == NULL)
+ {
+ fprintf (stderr, "calloc failed.\n");
+ oconfig_free (ci_copy);
+ return (NULL);
+ }
+ ci_copy->children_num = ci_orig->children_num;
+
+ for (i = 0; i < ci_copy->children_num; i++)
+ {
+ oconfig_item_t *child;
+
+ child = oconfig_clone (ci_orig->children + i);
+ if (child == NULL)
+ {
+ oconfig_free (ci_copy);
+ return (NULL);
+ }
+ child->parent = ci_copy;
+ ci_copy->children[i] = *child;
+ free (child);
+ } /* for (i = 0; i < ci_copy->children_num; i++) */
+ } /* }}} if (ci_orig->children_num > 0) */
+
+ return (ci_copy);
+} /* oconfig_item_t *oconfig_clone */
+
+void oconfig_free (oconfig_item_t *ci)
+{
+ int i;
+
+ if (ci == NULL)
+ return;
+
+ if (ci->key != NULL)
+ free (ci->key);
+
+ for (i = 0; i < ci->values_num; i++)
+ if ((ci->values[i].type == OCONFIG_TYPE_STRING)
+ && (NULL != ci->values[i].value.string))
+ free (ci->values[i].value.string);
+
+ if (ci->values != NULL)
+ free (ci->values);
+
+ for (i = 0; i < ci->children_num; i++)
+ oconfig_free (ci->children + i);
+
+ if (ci->children != NULL)
+ free (ci->children);
+}
+
+/*
+ * vim:shiftwidth=2:tabstop=8:softtabstop=2:fdm=marker
+ */
diff --git a/src/liboconfig/oconfig.h b/src/liboconfig/oconfig.h
--- /dev/null
+++ b/src/liboconfig/oconfig.h
@@ -0,0 +1,69 @@
+#ifndef OCONFIG_H
+#define OCONFIG_H 1
+
+#include <stdio.h>
+
+/**
+ * oconfig - src/oconfig.h
+ * Copyright (C) 2006-2009 Florian octo Forster <octo at verplant.org>
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Types
+ */
+#define OCONFIG_TYPE_STRING 0
+#define OCONFIG_TYPE_NUMBER 1
+#define OCONFIG_TYPE_BOOLEAN 2
+
+struct oconfig_value_s
+{
+ union
+ {
+ char *string;
+ double number;
+ int boolean;
+ } value;
+ int type;
+};
+typedef struct oconfig_value_s oconfig_value_t;
+
+struct oconfig_item_s;
+typedef struct oconfig_item_s oconfig_item_t;
+struct oconfig_item_s
+{
+ char *key;
+ oconfig_value_t *values;
+ int values_num;
+
+ oconfig_item_t *parent;
+ oconfig_item_t *children;
+ int children_num;
+};
+
+/*
+ * Functions
+ */
+oconfig_item_t *oconfig_parse_fh (FILE *fh);
+oconfig_item_t *oconfig_parse_file (const char *file);
+
+oconfig_item_t *oconfig_clone (const oconfig_item_t *ci);
+
+void oconfig_free (oconfig_item_t *ci);
+
+/*
+ * vim: shiftwidth=2:tabstop=8:softtabstop=2
+ */
+#endif /* OCONFIG_H */
diff --git a/src/liboconfig/parser.y b/src/liboconfig/parser.y
--- /dev/null
+++ b/src/liboconfig/parser.y
@@ -0,0 +1,259 @@
+/**
+ * oconfig - src/parser.y
+ * Copyright (C) 2007,2008 Florian octo Forster <octo at verplant.org>
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%{
+#include <stdlib.h>
+#include <string.h>
+#include "oconfig.h"
+#include "aux_types.h"
+
+static char *unquote (const char *orig);
+static int yyerror (const char *s);
+
+/* Lexer variables */
+extern int yylineno;
+extern char *yytext;
+
+extern oconfig_item_t *ci_root;
+extern char *c_file;
+%}
+
+%start entire_file
+
+%union {
+ double number;
+ int boolean;
+ char *string;
+ oconfig_value_t cv;
+ oconfig_item_t ci;
+ argument_list_t al;
+ statement_list_t sl;
+}
+
+%token <number> NUMBER
+%token <boolean> BTRUE BFALSE
+%token <string> QUOTED_STRING UNQUOTED_STRING
+%token SLASH OPENBRAC CLOSEBRAC EOL
+
+%type <string> string
+%type <string> identifier
+/* arguments */
+%type <cv> argument
+%type <al> argument_list
+/* blocks */
+%type <ci> block_begin
+%type <ci> block
+%type <string> block_end
+/* statements */
+%type <ci> option
+%type <ci> statement
+%type <sl> statement_list
+%type <ci> entire_file
+
+/* pass an verbose, specific error message to yyerror() */
+%error-verbose
+
+%%
+string:
+ QUOTED_STRING {$$ = unquote ($1);}
+ | UNQUOTED_STRING {$$ = strdup ($1);}
+ ;
+
+argument:
+ NUMBER {$$.value.number = $1; $$.type = OCONFIG_TYPE_NUMBER;}
+ | BTRUE {$$.value.boolean = 1; $$.type = OCONFIG_TYPE_BOOLEAN;}
+ | BFALSE {$$.value.boolean = 0; $$.type = OCONFIG_TYPE_BOOLEAN;}
+ | string {$$.value.string = $1; $$.type = OCONFIG_TYPE_STRING;}
+ ;
+
+argument_list:
+ argument_list argument
+ {
+ $$ = $1;
+ $$.argument_num++;
+ $$.argument = realloc ($$.argument, $$.argument_num * sizeof (oconfig_value_t));
+ $$.argument[$$.argument_num-1] = $2;
+ }
+ | argument
+ {
+ $$.argument = malloc (sizeof (oconfig_value_t));
+ $$.argument[0] = $1;
+ $$.argument_num = 1;
+ }
+ ;
+
+identifier:
+ UNQUOTED_STRING {$$ = strdup ($1);}
+ ;
+
+option:
+ identifier argument_list EOL
+ {
+ memset (&$$, '\0', sizeof ($$));
+ $$.key = $1;
+ $$.values = $2.argument;
+ $$.values_num = $2.argument_num;
+ }
+ ;
+
+block_begin:
+ OPENBRAC identifier CLOSEBRAC EOL
+ {
+ memset (&$$, '\0', sizeof ($$));
+ $$.key = $2;
+ }
+ |
+ OPENBRAC identifier argument_list CLOSEBRAC EOL
+ {
+ memset (&$$, '\0', sizeof ($$));
+ $$.key = $2;
+ $$.values = $3.argument;
+ $$.values_num = $3.argument_num;
+ }
+ ;
+
+block_end:
+ OPENBRAC SLASH identifier CLOSEBRAC EOL
+ {
+ $$ = $3;
+ }
+ ;
+
+block:
+ block_begin statement_list block_end
+ {
+ if (strcmp ($1.key, $3) != 0)
+ {
+ printf ("block_begin = %s; block_end = %s;\n", $1.key, $3);
+ yyerror ("Block not closed..\n");
+ exit (1);
+ }
+ free ($3); $3 = NULL;
+ $$ = $1;
+ $$.children = $2.statement;
+ $$.children_num = $2.statement_num;
+ }
+ | block_begin block_end
+ {
+ if (strcmp ($1.key, $2) != 0)
+ {
+ printf ("block_begin = %s; block_end = %s;\n", $1.key, $2);
+ yyerror ("Block not closed..\n");
+ exit (1);
+ }
+ free ($2); $2 = NULL;
+ $$ = $1;
+ $$.children = NULL;
+ $$.children_num = 0;
+ }
+ ;
+
+statement:
+ option {$$ = $1;}
+ | block {$$ = $1;}
+ | EOL {$$.values_num = 0;}
+ ;
+
+statement_list:
+ statement_list statement
+ {
+ $$ = $1;
+ if (($2.values_num > 0) || ($2.children_num > 0))
+ {
+ $$.statement_num++;
+ $$.statement = realloc ($$.statement, $$.statement_num * sizeof (oconfig_item_t));
+ $$.statement[$$.statement_num-1] = $2;
+ }
+ }
+ | statement
+ {
+ if (($1.values_num > 0) || ($1.children_num > 0))
+ {
+ $$.statement = malloc (sizeof (oconfig_item_t));
+ $$.statement[0] = $1;
+ $$.statement_num = 1;
+ }
+ else
+ {
+ $$.statement = NULL;
+ $$.statement_num = 0;
+ }
+ }
+ ;
+
+entire_file:
+ statement_list
+ {
+ ci_root = malloc (sizeof (oconfig_item_t));
+ memset (ci_root, '\0', sizeof (oconfig_item_t));
+ ci_root->children = $1.statement;
+ ci_root->children_num = $1.statement_num;
+ }
+ | /* epsilon */
+ {
+ ci_root = malloc (sizeof (oconfig_item_t));
+ memset (ci_root, '\0', sizeof (oconfig_item_t));
+ ci_root->children = NULL;
+ ci_root->children_num = 0;
+ }
+ ;
+
+%%
+static int yyerror (const char *s)
+{
+ char *text;
+
+ if (*yytext == '\n')
+ text = "<newline>";
+ else
+ text = yytext;
+
+ fprintf (stderr, "Parse error in file `%s', line %i near `%s': %s\n",
+ c_file, yylineno, text, s);
+ return (-1);
+} /* int yyerror */
+
+static char *unquote (const char *orig)
+{
+ char *ret = strdup (orig);
+ int len;
+ int i;
+
+ if (ret == NULL)
+ return (NULL);
+
+ len = strlen (ret);
+
+ if ((len < 2) || (ret[0] != '"') || (ret[len - 1] != '"'))
+ return (ret);
+
+ len -= 2;
+ memmove (ret, ret + 1, len);
+ ret[len] = '\0';
+
+ for (i = 0; i < len; i++)
+ {
+ if (ret[i] == '\\')
+ {
+ memmove (ret + i, ret + (i + 1), len - i);
+ len--;
+ }
+ }
+
+ return (ret);
+} /* char *unquote */
diff --git a/src/liboconfig/scanner.l b/src/liboconfig/scanner.l
--- /dev/null
+++ b/src/liboconfig/scanner.l
@@ -0,0 +1,137 @@
+/**
+ * oconfig - src/scanner.l
+ * Copyright (C) 2007 Florian octo Forster <octo at verplant.org>
+ * Copyright (C) 2008 Sebastian tokkee Harl <sh at tokkee.org>
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+%{
+#include <stdlib.h>
+#include "oconfig.h"
+#include "aux_types.h"
+#include "parser.h"
+
+/* multiline string buffer */
+static char *ml_buffer = NULL;
+static int ml_pos = 0;
+static int ml_len = 0;
+
+#define ml_free (ml_len - ml_pos)
+
+static void ml_append (char *);
+
+#ifdef yyterminate
+# undef yyterminate
+#endif
+#define yyterminate() \
+ do { free (ml_buffer); ml_buffer = NULL; ml_pos = 0; ml_len = 0; \
+ return YY_NULL; } while (0)
+%}
+%option yylineno
+%option noyywrap
+%x ML
+WHITE_SPACE [\ \t\b]
+NON_WHITE_SPACE [^\ \t\b]
+EOL (\r\n|\n)
+QUOTED_STRING ([^\\"]+|\\.)*
+UNQUOTED_STRING [0-9A-Za-z_]+
+HEX_NUMBER 0[xX][0-9a-fA-F]+
+OCT_NUMBER 0[0-7]+
+DEC_NUMBER [\+\-]?[0-9]+
+FLOAT_NUMBER [\+\-]?[0-9]*\.[0-9]+([eE][\+\-][0-9]+)?
+NUMBER ({FLOAT_NUMBER}|{HEX_NUMBER}|{OCT_NUMBER}|{DEC_NUMBER})
+BOOL_TRUE (true|yes|on)
+BOOL_FALSE (false|no|off)
+COMMENT #.*
+PORT (6(5(5(3[0-5]|[0-2][0-9])|[0-4][0-9][0-9])|[0-4][0-9][0-9][0-9])|[1-5][0-9][0-9][0-9][0-9]|[1-9][0-9]?[0-9]?[0-9]?)
+IP_BYTE (2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])
+IPV4_ADDR {IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}\.{IP_BYTE}(:{PORT})?
+
+%%
+{WHITE_SPACE} |
+{COMMENT} {/* ignore */}
+
+\\{EOL} {/* continue line */}
+
+{EOL} {return (EOL);}
+"/" {return (SLASH);}
+"<" {return (OPENBRAC);}
+">" {return (CLOSEBRAC);}
+{BOOL_TRUE} {yylval.boolean = 1; return (BTRUE);}
+{BOOL_FALSE} {yylval.boolean = 0; return (BFALSE);}
+
+{IPV4_ADDR} {yylval.string = yytext; return (UNQUOTED_STRING);}
+
+{NUMBER} {yylval.number = strtod (yytext, NULL); return (NUMBER);}
+
+\"{QUOTED_STRING}\" {yylval.string = yytext; return (QUOTED_STRING);}
+{UNQUOTED_STRING} {yylval.string = yytext; return (UNQUOTED_STRING);}
+
+\"{QUOTED_STRING}\\{EOL} {
+ int len = strlen (yytext);
+
+ ml_pos = 0;
+
+ /* remove "\\<EOL>" */
+ if ('\r' == yytext[len - 2])
+ len -= 3;
+ else
+ len -= 2;
+ yytext[len] = '\0';
+
+ ml_append (yytext);
+ BEGIN (ML);
+}
+<ML>^{WHITE_SPACE}+ {/* remove leading white-space */}
+<ML>{NON_WHITE_SPACE}{QUOTED_STRING}\\{EOL} {
+ int len = strlen (yytext);
+
+ /* remove "\\<EOL>" */
+ if ('\r' == yytext[len - 2])
+ len -= 3;
+ else
+ len -= 2;
+ yytext[len] = '\0';
+
+ ml_append(yytext);
+}
+<ML>{NON_WHITE_SPACE}{QUOTED_STRING}\" {
+ ml_append(yytext);
+ yylval.string = ml_buffer;
+
+ BEGIN (INITIAL);
+ return (QUOTED_STRING);
+}
+%%
+static void ml_append (char *string)
+{
+ int len = strlen (string);
+ int s;
+
+ if (ml_free <= len) {
+ ml_len += len - ml_free + 1;
+ ml_buffer = (char *)realloc (ml_buffer, ml_len);
+ if (NULL == ml_buffer)
+ YY_FATAL_ERROR ("out of dynamic memory in ml_append");
+ }
+
+ s = snprintf (ml_buffer + ml_pos, ml_free, "%s", string);
+ if ((0 > s) || (ml_free <= s))
+ YY_FATAL_ERROR ("failed to write to multiline buffer");
+
+ ml_pos += s;
+ return;
+} /* ml_append */
+
diff --git a/src/liboconfig/utils.c b/src/liboconfig/utils.c
--- /dev/null
+++ b/src/liboconfig/utils.c
@@ -0,0 +1,77 @@
+/**
+ * oconfig - src/utils.h
+ * Copyright (C) 2012 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OCONFIG_UTILS_H
+#define OCONFIG_UTILS_H 1
+
+#include "liboconfig/oconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int
+oconfig_get_string(oconfig_item_t *ci, char **value)
+{
+ if (! ci)
+ return -1;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_STRING))
+ return -1;
+
+ if (value)
+ *value = ci->values[0].value.string;
+ return 0;
+} /* oconfig_get_string */
+
+int
+oconfig_get_number(oconfig_item_t *ci, double *value)
+{
+ if (! ci)
+ return -1;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_NUMBER))
+ return -1;
+
+ if (value)
+ *value = ci->values[0].value.number;
+ return 0;
+} /* oconfig_get_number */
+
+int
+oconfig_get_boolean(oconfig_item_t *ci, _Bool *value)
+{
+ if (! ci)
+ return -1;
+
+ if ((ci->values_num != 1) || (ci->values[0].type != OCONFIG_TYPE_BOOLEAN))
+ return -1;
+
+ if (value)
+ *value = ci->values[0].value.boolean != 0;
+ return 0;
+} /* oconfig_get_boolean */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* OCONFIG_UTILS_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/liboconfig/utils.h b/src/liboconfig/utils.h
--- /dev/null
+++ b/src/liboconfig/utils.h
@@ -0,0 +1,50 @@
+/**
+ * oconfig - src/utils.h
+ * Copyright (C) 2012 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ *
+ * 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; only version 2 of the License is applicable.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef OCONFIG_UTILS_H
+#define OCONFIG_UTILS_H 1
+
+#include "liboconfig/oconfig.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* oconfig_get_<type>:
+ * Checks if the specified item has exactly one value of the respective type
+ * and returns that value through the appropriate parameter.
+ *
+ * Returns:
+ * - 0 on success
+ * - a negative value else
+ */
+int
+oconfig_get_string(oconfig_item_t *ci, char **value);
+int
+oconfig_get_number(oconfig_item_t *ci, double *value);
+int
+oconfig_get_boolean(oconfig_item_t *ci, _Bool *value);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* OCONFIG_UTILS_H */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/syscollector.c b/src/syscollector.c
--- /dev/null
+++ b/src/syscollector.c
@@ -0,0 +1,53 @@
+/*
+ * syscollector - src/syscollector.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "syscollector.h"
+
+/*
+ * public API
+ */
+
+unsigned int
+sc_version(void)
+{
+ return SC_VERSION;
+} /* sc_version */
+
+const char *
+sc_version_string(void)
+{
+ return SC_VERSION_STRING;
+} /* sc_version_string */
+
+const char *
+sc_version_extra(void)
+{
+ return SC_VERSION_EXTRA;
+} /* sc_version_extra */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/utils/llist.c b/src/utils/llist.c
--- /dev/null
+++ b/src/utils/llist.c
@@ -0,0 +1,387 @@
+/*
+ * syscollector - src/utils/llist.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "utils/llist.h"
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include <pthread.h>
+
+/*
+ * private data types
+ */
+
+struct sc_llist_elem;
+typedef struct sc_llist_elem sc_llist_elem_t;
+
+struct sc_llist_elem {
+ sc_object_t *obj;
+
+ sc_llist_elem_t *next;
+ sc_llist_elem_t *prev;
+};
+
+struct sc_llist {
+ pthread_rwlock_t lock;
+
+ sc_llist_elem_t *head;
+ sc_llist_elem_t *tail;
+
+ size_t length;
+};
+
+struct sc_llist_iter {
+ sc_llist_t *list;
+ sc_llist_elem_t *elem;
+};
+
+/*
+ * private helper functions
+ */
+
+/* Insert a new element after 'elem'. If 'elem' is NULL, insert at the head of
+ * the list. */
+static int
+sc_llist_insert_after(sc_llist_t *list, sc_llist_elem_t *elem,
+ sc_object_t *obj)
+{
+ sc_llist_elem_t *new;
+
+ assert(list);
+
+ new = malloc(sizeof(*new));
+ if (! new)
+ return -1;
+
+ new->obj = obj;
+ if (elem)
+ new->next = elem->next;
+ else if (list->head)
+ new->next = list->head;
+ else
+ new->next = NULL;
+ new->prev = elem;
+
+ if (elem) {
+ if (elem->next)
+ elem->next->prev = new;
+ else
+ list->tail = new;
+ elem->next = new;
+ }
+ else {
+ /* new entry will be new head */
+ if (list->head)
+ list->head->prev = new;
+
+ list->head = new;
+ if (! list->tail)
+ list->tail = new;
+ }
+
+ assert(list->head && list->tail);
+ if (! list->length) {
+ assert(list->head == list->tail);
+ }
+
+ sc_object_ref(obj);
+ ++list->length;
+ return 0;
+} /* sc_llist_insert_after */
+
+static sc_object_t *
+sc_llist_remove_elem(sc_llist_t *list, sc_llist_elem_t *elem)
+{
+ sc_object_t *obj;
+
+ assert(list && elem);
+
+ obj = elem->obj;
+
+ if (elem->prev)
+ elem->prev->next = elem->next;
+ else {
+ assert(elem == list->head);
+ list->head = elem->next;
+ }
+
+ if (elem->next)
+ elem->next->prev = elem->prev;
+ else {
+ assert(elem == list->tail);
+ list->tail = elem->prev;
+ }
+
+ elem->prev = elem->next = NULL;
+ free(elem);
+
+ --list->length;
+ return obj;
+} /* sc_llist_remove_elem */
+
+/*
+ * public API
+ */
+
+sc_llist_t *
+sc_llist_create(void)
+{
+ sc_llist_t *list;
+
+ list = malloc(sizeof(*list));
+ if (! list)
+ return NULL;
+
+ pthread_rwlock_init(&list->lock, /* attr = */ NULL);
+
+ list->head = list->tail = NULL;
+ list->length = 0;
+ return list;
+} /* sc_llist_create */
+
+sc_llist_t *
+sc_llist_clone(sc_llist_t *list)
+{
+ sc_llist_t *clone;
+ sc_llist_elem_t *elem;
+
+ if (! list)
+ return NULL;
+
+ clone = sc_llist_create();
+ if (! clone)
+ return NULL;
+
+ if (! list->length) {
+ assert((! list->head) && (! list->tail));
+ return clone;
+ }
+
+ for (elem = list->head; elem; elem = elem->next) {
+ if (sc_llist_append(clone, elem->obj)) {
+ sc_llist_destroy(clone);
+ return NULL;
+ }
+ }
+ return clone;
+} /* sc_llist_clone */
+
+void
+sc_llist_destroy(sc_llist_t *list)
+{
+ sc_llist_elem_t *elem;
+
+ if (! list)
+ return;
+
+ pthread_rwlock_wrlock(&list->lock);
+
+ elem = list->head;
+ while (elem) {
+ sc_llist_elem_t *tmp = elem->next;
+
+ sc_object_deref(elem->obj);
+ free(elem);
+
+ elem = tmp;
+ }
+
+ list->head = list->tail = NULL;
+ list->length = 0;
+
+ pthread_rwlock_unlock(&list->lock);
+ pthread_rwlock_destroy(&list->lock);
+ free(list);
+} /* sc_llist_destroy */
+
+int
+sc_llist_append(sc_llist_t *list, sc_object_t *obj)
+{
+ int status;
+
+ if ((! list) || (! obj))
+ return -1;
+
+ pthread_rwlock_wrlock(&list->lock);
+ status = sc_llist_insert_after(list, list->tail, obj);
+ pthread_rwlock_unlock(&list->lock);
+ return status;
+} /* sc_llist_append */
+
+int
+sc_llist_insert(sc_llist_t *list, sc_object_t *obj, size_t index)
+{
+ sc_llist_elem_t *prev;
+ sc_llist_elem_t *next;
+
+ int status;
+
+ size_t i;
+
+ if ((! list) || (! obj) || (index > list->length))
+ return -1;
+
+ pthread_rwlock_wrlock(&list->lock);
+
+ prev = NULL;
+ next = list->head;
+
+ for (i = 0; i < index; ++i) {
+ prev = next;
+ next = next->next;
+ }
+ status = sc_llist_insert_after(list, prev, obj);
+ pthread_rwlock_unlock(&list->lock);
+ return status;
+} /* sc_llist_insert */
+
+int
+sc_llist_insert_sorted(sc_llist_t *list, sc_object_t *obj,
+ int (*compare)(const sc_object_t *, const sc_object_t *))
+{
+ sc_llist_elem_t *prev;
+ sc_llist_elem_t *next;
+
+ int status;
+
+ if ((! list) || (! obj) || (! compare))
+ return -1;
+
+ pthread_rwlock_wrlock(&list->lock);
+
+ prev = NULL;
+ next = list->head;
+
+ while (next) {
+ if (compare(obj, next->obj) < 0)
+ break;
+
+ prev = next;
+ next = next->next;
+ }
+ status = sc_llist_insert_after(list, prev, obj);
+ pthread_rwlock_unlock(&list->lock);
+ return status;
+} /* sc_llist_insert_sorted */
+
+sc_object_t *
+sc_llist_search(sc_llist_t *list, const sc_object_t *key,
+ int (*compare)(const sc_object_t *, const sc_object_t *))
+{
+ sc_llist_elem_t *elem;
+
+ if ((! list) || (! compare))
+ return NULL;
+
+ pthread_rwlock_rdlock(&list->lock);
+
+ for (elem = list->head; elem; elem = elem->next)
+ if (! compare(elem->obj, key))
+ break;
+
+ pthread_rwlock_unlock(&list->lock);
+
+ if (elem)
+ return elem->obj;
+ return NULL;
+} /* sc_llist_search */
+
+sc_object_t *
+sc_llist_shift(sc_llist_t *list)
+{
+ sc_object_t *obj;
+
+ if ((! list) || (! list->head))
+ return NULL;
+
+ pthread_rwlock_wrlock(&list->lock);
+ obj = sc_llist_remove_elem(list, list->head);
+ pthread_rwlock_unlock(&list->lock);
+ return obj;
+} /* sc_llist_shift */
+
+sc_llist_iter_t *
+sc_llist_get_iter(sc_llist_t *list)
+{
+ sc_llist_iter_t *iter;
+
+ if (! list)
+ return NULL;
+
+ iter = malloc(sizeof(*iter));
+ if (! iter)
+ return NULL;
+
+ pthread_rwlock_rdlock(&list->lock);
+
+ iter->list = list;
+ iter->elem = list->head;
+
+ /* XXX: keep lock until destroying the iterator? */
+ pthread_rwlock_unlock(&list->lock);
+ return iter;
+} /* sc_llist_get_iter */
+
+void
+sc_llist_iter_destroy(sc_llist_iter_t *iter)
+{
+ if (! iter)
+ return;
+
+ iter->list = NULL;
+ iter->elem = NULL;
+ free(iter);
+} /* sc_llist_iter_destroy */
+
+_Bool
+sc_llist_iter_has_next(sc_llist_iter_t *iter)
+{
+ if (! iter)
+ return 0;
+ return iter->elem != NULL;
+} /* sc_llist_iter_has_next */
+
+sc_object_t *
+sc_llist_iter_get_next(sc_llist_iter_t *iter)
+{
+ sc_object_t *obj;
+
+ if ((! iter) || (! iter->elem))
+ return NULL;
+
+ pthread_rwlock_rdlock(&iter->list->lock);
+
+ obj = iter->elem->obj;
+ iter->elem = iter->elem->next;
+
+ pthread_rwlock_unlock(&iter->list->lock);
+ return obj;
+} /* sc_llist_iter_get_next */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/utils/string.c b/src/utils/string.c
--- /dev/null
+++ b/src/utils/string.c
@@ -0,0 +1,63 @@
+/*
+ * syscollector - src/utils/string.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "utils/string.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/*
+ * public API
+ */
+
+char *
+sc_strerror(int errnum, char *strerrbuf, size_t buflen)
+{
+#if STRERROR_R_CHAR_P
+ {
+ char *tmp = strerror_r(errnum, strerrbuf, buflen);
+ if (*strerrbuf = '\0') {
+ if (tmp && (tmp != strerrbuf) && (*tmp != '\0'))
+ strncpy(strerrbuf, tmp, buflen);
+ else
+ snprintf(strerrbuf, buflen, "unknown error #%i "
+ "(strerror_r(3) did not return an error message)",
+ errnum);
+ }
+ }
+#else
+ if (strerror_r(errnum, strerrbuf, buflen))
+ snprintf(strerrbuf, buflen, "unknown error #%i "
+ "(strerror_r(3) failed)", errnum);
+#endif
+
+ strerrbuf[buflen - 1] = '\0';
+ return strerrbuf;
+} /* sc_strerror */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/utils/time.c b/src/utils/time.c
--- /dev/null
+++ b/src/utils/time.c
@@ -0,0 +1,80 @@
+/*
+ * syscollector - src/utils/time.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "utils/time.h"
+#include "utils/string.h"
+
+#include <time.h>
+
+#include <string.h>
+
+/*
+ * public API
+ */
+
+sc_time_t
+sc_gettime(void)
+{
+ struct timespec ts_now = { 0, 0 };
+
+ if (clock_gettime(CLOCK_REALTIME, &ts_now))
+ return 0;
+ return TIMESPEC_TO_SC_TIME(ts_now);
+} /* sc_gettime */
+
+int
+sc_sleep(sc_time_t reg, sc_time_t *rem)
+{
+ struct timespec ts_reg, ts_rem = { 0, 0 };
+ int status;
+
+ ts_reg.tv_sec = (time_t)SC_TIME_TO_SECS(reg);
+ ts_reg.tv_nsec = (long int)(reg % (sc_time_t)1000000000);
+
+ status = nanosleep(&ts_reg, &ts_rem);
+ if (rem)
+ *rem = TIMESPEC_TO_SC_TIME(ts_rem);
+ return status;
+} /* sc_sleep */
+
+size_t
+sc_strftime(char *s, size_t len, const char *format, sc_time_t t)
+{
+ time_t tstamp;
+ struct tm tm;
+
+ memset(&tm, 0, sizeof(tm));
+
+ tstamp = (time_t)SC_TIME_TO_SECS(t);
+ if (! localtime_r (&tstamp, &tm))
+ return 0;
+
+ return strftime(s, len, format, &tm);
+} /* sc_strftime */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/src/utils/unixsock.c b/src/utils/unixsock.c
--- /dev/null
+++ b/src/utils/unixsock.c
@@ -0,0 +1,189 @@
+/*
+ * syscollector - src/utils/unixsock.c
+ * Copyright (C) 2012 Sebastian 'tokkee' Harl <sh@tokkee.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 "utils/unixsock.h"
+#include "utils/string.h"
+
+#include <errno.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include <unistd.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+/*
+ * private data types
+ */
+
+struct sc_unixsock_client {
+ char *path;
+ FILE *fh;
+};
+
+/*
+ * public API
+ */
+
+sc_unixsock_client_t *
+sc_unixsock_client_create(const char *path)
+{
+ sc_unixsock_client_t *client;
+
+ if (! path)
+ return NULL;
+
+ client = malloc(sizeof(*client));
+ if (! client)
+ return NULL;
+ memset(client, 0, sizeof(*client));
+ client->fh = NULL;
+
+ client->path = strdup(path);
+ if (! client->path) {
+ sc_unixsock_client_destroy(client);
+ return NULL;
+ }
+ return client;
+} /* sc_unixsock_client_create */
+
+int
+sc_unixsock_client_connect(sc_unixsock_client_t *client)
+{
+ struct sockaddr_un sa;
+ int fd;
+
+ if ((! client) || (! client->path))
+ return -1;
+
+ memset(&sa, 0, sizeof(sa));
+
+ if (client->fh)
+ fclose(client->fh);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, /* protocol = */ 0);
+ if (fd < 0) {
+ char errbuf[1024];
+ fprintf(stderr, "unixsock: Failed to open socket: %s\n",
+ sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return -1;
+ }
+
+ sa.sun_family = AF_UNIX;
+ strncpy(sa.sun_path, client->path, sizeof(sa.sun_path));
+ sa.sun_path[sizeof(sa.sun_path) - 1] = '\0';
+
+ if (connect(fd, (struct sockaddr *)&sa, sizeof(sa))) {
+ char errbuf[1024];
+ fprintf(stderr, "unixsock: Failed to connect to %s: %s\n",
+ sa.sun_path, sc_strerror(errno, errbuf, sizeof(errbuf)));
+ close(fd);
+ return -1;
+ }
+
+ client->fh = fdopen(fd, "r+");
+ if (! client->fh) {
+ char errbuf[1024];
+ fprintf(stderr, "unixsock: Failed to open I/O stream for %s: %s\n",
+ sa.sun_path, sc_strerror(errno, errbuf, sizeof(errbuf)));
+ close(fd);
+ return -1;
+ }
+ return 0;
+} /* sc_unixsock_client_connect */
+
+int
+sc_unixsock_client_send(sc_unixsock_client_t *client, const char *msg)
+{
+ int status;
+
+ if ((! client) || (! client->fh))
+ return -1;
+
+ status = fprintf(client->fh, "%s\r\n", msg);
+ if (status < 0) {
+ char errbuf[1024];
+ fprintf(stderr, "unixsock: Failed to write to socket (%s): %s\n",
+ client->path, sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return status;
+ }
+ return status;
+} /* sc_unixsock_client_send */
+
+char *
+sc_unixsock_client_recv(sc_unixsock_client_t *client, char *buffer, size_t buflen)
+{
+ if ((! client) || (! client->fh) || (! buffer))
+ return NULL;
+
+ buffer = fgets(buffer, (int)buflen - 1, client->fh);
+ if ((! buffer) && (! feof(client->fh))) {
+ char errbuf[1024];
+ fprintf(stderr, "unixsock: Failed to read from socket (%s): %s\n",
+ client->path, sc_strerror(errno, errbuf, sizeof(errbuf)));
+ return buffer;
+ }
+ buffer[buflen - 1] = '\0';
+
+ buflen = strlen(buffer);
+ while ((buffer[buflen - 1] == '\n') || (buffer[buflen - 1] == '\r')) {
+ buffer[buflen - 1] = '\0';
+ --buflen;
+ }
+ return buffer;
+} /* sc_unixsock_client_recv */
+
+void
+sc_unixsock_client_destroy(sc_unixsock_client_t *client)
+{
+ if (! client)
+ return;
+
+ if (client->path)
+ free(client->path);
+ client->path = NULL;
+
+ if (client->fh)
+ fclose(client->fh);
+ client->fh = NULL;
+
+ free(client);
+} /* sc_unixsock_client_destroy */
+
+const char *
+sc_unixsock_client_path(sc_unixsock_client_t *client)
+{
+ if (! client)
+ return NULL;
+ return client->path;
+} /* sc_unixsock_client_path */
+
+/* vim: set tw=78 sw=4 ts=4 noexpandtab : */
+
diff --git a/version-gen.sh b/version-gen.sh
--- /dev/null
+++ b/version-gen.sh
@@ -0,0 +1,44 @@
+#! /bin/sh
+
+DEFAULT_VERSION="0.0.0.git"
+
+VERSION="$( git describe --tags 2> /dev/null \
+ | sed -e 's/syscollector-//' || true )"
+
+if test -z "$VERSION"; then
+ VERSION="$DEFAULT_VERSION"
+else
+ git update-index -q --refresh || true
+ if test -n "$( git diff-index --name-only HEAD || true )"; then
+ VERSION="$VERSION-dirty"
+ fi
+fi
+
+VERSION="$( echo "$VERSION" | sed -e 's/-/./g' )"
+if test "x`uname -s`" = "xAIX" || test "x`uname -s`" = "xSunOS" ; then
+ echo "$VERSION\c"
+else
+ echo -n "$VERSION"
+fi
+
+OLD_VERSION=""
+if test -e version; then
+ OLD_VERSION=$( sed -ne 's/^VERSION="\(.*\)"/\1/p' version )
+fi
+
+if test "$OLD_VERSION" != "$VERSION"; then
+ VERSION_MAJOR=$( echo $VERSION | cut -d'.' -f1 )
+ VERSION_MINOR=$( echo $VERSION | cut -d'.' -f2 )
+ VERSION_PATCH=$( echo $VERSION | cut -d'.' -f3 )
+ VERSION_EXTRA="\"$( echo $VERSION | cut -d'.' -f4- )\""
+ test -z "$VERSION_EXTRA" || VERSION_EXTRA=".$VERSION_EXTRA"
+ (
+ echo "VERSION=\"$VERSION\""
+ echo "VERSION_MAJOR=$VERSION_MAJOR"
+ echo "VERSION_MINOR=$VERSION_MINOR"
+ echo "VERSION_PATCH=$VERSION_PATCH"
+ echo "VERSION_EXTRA=\"$VERSION_EXTRA\""
+ echo "VERSION_STRING=\"$VERSION_MAJOR.$VERSION_MINOR.$VERSION_PATCH\""
+ ) > version
+fi
+