Code

Initial commit. sysdb-0.0.0
authorSebastian Harl <sh@tokkee.org>
Wed, 5 Dec 2012 15:10:48 +0000 (16:10 +0100)
committerSebastian 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

42 files changed:
.gitignore [new file with mode: 0644]
COPYING [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
README [new file with mode: 0644]
THANKS [new file with mode: 0644]
autogen.sh [new file with mode: 0755]
configure.ac [new file with mode: 0644]
doc/Makefile.am [new file with mode: 0644]
doc/syscollectord.1.txt [new file with mode: 0644]
m4/sc_plugin.m4 [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/backend/collectd.c [new file with mode: 0644]
src/core/object.c [new file with mode: 0644]
src/core/plugin.c [new file with mode: 0644]
src/core/store.c [new file with mode: 0644]
src/daemon/config.c [new file with mode: 0644]
src/daemon/syscollectord.c [new file with mode: 0644]
src/daemon/syscollectord.conf [new file with mode: 0644]
src/include/core/object.h [new file with mode: 0644]
src/include/core/plugin.h [new file with mode: 0644]
src/include/core/store.h [new file with mode: 0644]
src/include/syscollector.h.in [new file with mode: 0644]
src/include/utils/llist.h [new file with mode: 0644]
src/include/utils/string.h [new file with mode: 0644]
src/include/utils/time.h [new file with mode: 0644]
src/include/utils/unixsock.h [new file with mode: 0644]
src/liboconfig/COPYING [new file with mode: 0644]
src/liboconfig/Makefile.am [new file with mode: 0644]
src/liboconfig/aux_types.h [new file with mode: 0644]
src/liboconfig/oconfig.c [new file with mode: 0644]
src/liboconfig/oconfig.h [new file with mode: 0644]
src/liboconfig/parser.y [new file with mode: 0644]
src/liboconfig/scanner.l [new file with mode: 0644]
src/liboconfig/utils.c [new file with mode: 0644]
src/liboconfig/utils.h [new file with mode: 0644]
src/syscollector.c [new file with mode: 0644]
src/utils/llist.c [new file with mode: 0644]
src/utils/string.c [new file with mode: 0644]
src/utils/time.c [new file with mode: 0644]
src/utils/unixsock.c [new file with mode: 0644]
version-gen.sh [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..5edd76f
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..b90bd54
--- /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
new file mode 100644 (file)
index 0000000..23e5f25
--- /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
new file mode 100644 (file)
index 0000000..cfca249
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..20cbce9
--- /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
new file mode 100644 (file)
index 0000000..5424ef9
--- /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
new file mode 100755 (executable)
index 0000000..e4ffee0
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..fe9331d
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..b658384
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..70b1b38
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..bd55725
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..b2816eb
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..ad5cad1
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..711607a
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..a4c8c2d
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..9528a31
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..0b7cc77
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..effc615
--- /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
new file mode 100644 (file)
index 0000000..4fa379e
--- /dev/null
@@ -0,0 +1,2 @@
+LoadBackend "collectd"
+
diff --git a/src/include/core/object.h b/src/include/core/object.h
new file mode 100644 (file)
index 0000000..cd91086
--- /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
new file mode 100644 (file)
index 0000000..8d54089
--- /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
new file mode 100644 (file)
index 0000000..fe7eb75
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..ff35fa5
--- /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
new file mode 100644 (file)
index 0000000..c115bf2
--- /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
new file mode 100644 (file)
index 0000000..a52205b
--- /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
new file mode 100644 (file)
index 0000000..f67a46b
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..e51e4ac
--- /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
new file mode 100644 (file)
index 0000000..623b625
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..7603834
--- /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
new file mode 100644 (file)
index 0000000..25b81ab
--- /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
new file mode 100644 (file)
index 0000000..629775a
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..70fc623
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..19f58b2
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..9f0cd8e
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..c713610
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..5a2ae85
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..b07cf54
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..765743f
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..25d3e9e
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..d3192c3
--- /dev/null
@@ -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
new file mode 100644 (file)
index 0000000..1be17b9
--- /dev/null
@@ -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
new file mode 100755 (executable)
index 0000000..c2ff7c4
--- /dev/null
@@ -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
+