aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorValery Kartel <valery.kartel@gmail.com>2015-09-24 16:17:31 +0300
committerSören Tempel <soeren+git@soeren-tempel.net>2015-09-29 16:13:11 +0200
commitfa6505dc69a2a6945324744d8134de0b186be808 (patch)
tree51ed8fedb90f372a5b783b2a2a7d2af6f7f890b2
parent48cf8ee4d00068967848c3c9deb33b6c996c1697 (diff)
downloadaports-fa6505dc69a2a6945324744d8134de0b186be808.tar.bz2
aports-fa6505dc69a2a6945324744d8134de0b186be808.tar.xz
testing/csync2: upgrade to 2.0
-rw-r--r--testing/csync2/03-strlcpy_disable.patch21
-rw-r--r--testing/csync2/APKBUILD94
-rw-r--r--testing/csync2/csync2-1.34-librsync-1.0.0.patch35
-rw-r--r--testing/csync2/csync2.initd13
-rw-r--r--testing/csync2/csync2.post-install24
-rw-r--r--testing/csync2/csync2.post-upgrade6
-rw-r--r--testing/csync2/git.patch6640
-rw-r--r--testing/csync2/longlong-format.patch31
-rw-r--r--testing/csync2/rsync-strlcpy-disable.patch20
9 files changed, 132 insertions, 6752 deletions
diff --git a/testing/csync2/03-strlcpy_disable.patch b/testing/csync2/03-strlcpy_disable.patch
deleted file mode 100644
index f0e9d45451..0000000000
--- a/testing/csync2/03-strlcpy_disable.patch
+++ /dev/null
@@ -1,21 +0,0 @@
---- old/rsync.c
-+++ new/rsync.c
-@@ -49,6 +49,9 @@
- *
- * @return index of the terminating byte.
- **/
-+
-+/* disabled **
-+
- static size_t strlcpy(char *d, const char *s, size_t bufsize)
- {
- size_t len = strlen(s);
-@@ -61,7 +64,7 @@
- }
- return ret;
- }
--
-+*/
-
- /* This has been taken from rsync sources: receiver.c */
-
diff --git a/testing/csync2/APKBUILD b/testing/csync2/APKBUILD
index f26da55cbc..5b32bad425 100644
--- a/testing/csync2/APKBUILD
+++ b/testing/csync2/APKBUILD
@@ -1,83 +1,65 @@
-# Maintainer: Natanael Copa <ncopa@alpinelinux.org>
pkgname=csync2
-pkgver=1.34_git20111201
-_ver=${pkgver%_git*}
-pkgrel=2
+pkgver=2.0
+pkgrel=0
pkgdesc="Cluster synchronization tool"
url="http://oss.linbit.com/csync2/"
arch="all"
license="GPL-2+"
-depends=
-makedepends="librsync-dev gnutls-dev sqlite-dev autoconf automake bison flex"
-install=
-subpackages="$pkgname-doc"
-source="http://oss.linbit.com/csync2/csync2-${_ver}.tar.gz
- git.patch
- 03-strlcpy_disable.patch
- csync2-1.34-librsync-1.0.0.patch
- "
-# 01-csync2-sqlite3.patch
-# 02-csync2-1.34-gnutls_pkgconfig.patch
+makedepends="librsync-dev gnutls-dev sqlite-dev mysql-dev postgresql-dev"
+subpackages="$pkgname-compare $pkgname-doc"
+source="http://oss.linbit.com/$pkgname/$pkgname-$pkgver.tar.gz
+ longlong-format.patch
+ rsync-strlcpy-disable.patch
+ $pkgname.initd"
+install="$pkgname.post-install $pkgname.post-upgrade"
-_builddir="$srcdir"/$pkgname-$_ver
-_giturl="http://git.linbit.com/csync2.git"
+_builddir="$srcdir"/$pkgname-$pkgver
-
-snapshot() {
- local _pkg=$pkgname-$pkgver.tar.gz
- mkdir -p "$srcdir"
- cd "$srcdir"
- msg "Creating snapshot: $_pkg"
- rm -rf ${_giturl##*/}
- git clone --bare $_giturl || return 1
- git --git-dir ${_giturl##*/} archive -o $_pkg \
- --prefix=$pkgname-$pkgver/ HEAD \
- || return 1
-
- msg "New snapshot: $_pkg"
- if [ -n "$_upload" ]; then
- msg "Uploading to $_upload"
- scp $_pkg $_upload || return 1
- abump $pkgname-$_tag
- fi
- #abuild checksum
-}
-
-prepare() {
+prepare() {
cd "$_builddir"
for i in $source; do
case $i in
*.patch) msg $i; patch -p1 -i "$srcdir"/$i || return 1;;
esac
done
- autoreconf --install --force
}
-
+
build() {
cd "$_builddir"
./configure --prefix=/usr \
- --sysconfdir=/etc \
+ --sysconfdir=/etc/$pkgname \
--localstatedir=/var \
+ --docdir=/usr/share/doc/$pkgname \
--mandir=/usr/share/man \
- --infodir=/usr/share/info \
+ --enable-postgres \
+ --enable-mysql \
|| return 1
make || return 1
}
package() {
cd "$_builddir"
- make DESTDIR="$pkgdir" install
+ make DESTDIR="$pkgdir" install || return 1
+ install -m755 -D "$srcdir"/$pkgname.initd "$pkgdir"/etc/init.d/$pkgname
+ cp doc/* "$pkgdir"/usr/share/doc/$pkgname/
}
-md5sums="efc8a3548996b79cef2ad76af5e93cd8 csync2-1.34.tar.gz
-bd4957d25e984518d929ae4036bf0ae9 git.patch
-e6d8aaff70cf847b11873cd1cfaaa8f6 03-strlcpy_disable.patch
-943227a4ca4bdd5ec37a315dba1844d3 csync2-1.34-librsync-1.0.0.patch"
-sha256sums="32b250dd4a0353f71015c5c3961174b975dd5e799e4a084e8f6d00792bd8c833 csync2-1.34.tar.gz
-7174ff0cb464b8f74c3816bfe3c5a89e9b876b50dcd6a7440b7488e3561668d7 git.patch
-f4a44ca3f3a33a413a96ce837d5569c3d802bce4a6a960593b014d8dcbfbc136 03-strlcpy_disable.patch
-320ac974d6bbebddea5bc08b92c944189733742e2db8c3580e193ca9a2b934ee csync2-1.34-librsync-1.0.0.patch"
-sha512sums="a24154446740f3362c10a23d526c29e96292f2ad56a9ed2f11ca0a6d7afed3730d0e96f8ebc2c6cdcc17031aa0e368f6ba93084ad3cf5f4828fbc279e6f004d8 csync2-1.34.tar.gz
-ff47b30e86f6266b4be85a47db52409169597d97e4ec086653af3341978cce5e477054e5746f3824aed1d8c7949e5f8451b2bd675442a9a2c9c5c519918438a9 git.patch
-1b82b25acfb988cd8f92117f6ea4bc22247648448751adc0958dae0ade3c1eb065ad15f7104e4860ea2f08a91a368d68e3959ef5dfa278c254857382ebb64625 03-strlcpy_disable.patch
-f44e1df35aefff2900a5983fd1e368864396757e1fcfd6e3f5d39a55c1bad6e42e8bca5f0e16aa641dff3b62a27d7d5413dc907bcd19b9d4de068fd7be7214b0 csync2-1.34-librsync-1.0.0.patch"
+compare() {
+ subpkgdesc="csync2 compare script"
+ arch="noarch"
+ depents="bash perl-digest-md5"
+ mkdir -p "$subpkgdir"/usr/sbin
+ mv "$pkgdir"/usr/sbin/$pkgname-compare "$subpkgdir"/usr/sbin/
+}
+md5sums="4069fc9e86e8508c392fe2862059eb91 csync2-2.0.tar.gz
+8fc73254db29316f9125761d18f1ed46 longlong-format.patch
+5908c4106d4866ab1c1f88f4c9482c9f rsync-strlcpy-disable.patch
+05df8f2b877e7d8ca838df9e71bf2c65 csync2.initd"
+sha256sums="11cb37380fb185bce0c22b804fec9b01c385d9d83cc528cfd48d748453834fa2 csync2-2.0.tar.gz
+afb1317987cc4b81908bc437269162c7af23b34e6842306483da5b53efce2db3 longlong-format.patch
+339c7f5d91ff7ed3e63de5a5e39cf17efd7533f9c460eb81db76003acb164017 rsync-strlcpy-disable.patch
+77f6baf688fdb6938846e9f6e0e945d4085d2f0002661ff335e5c8ac39f0069a csync2.initd"
+sha512sums="f91fd222f67affe9634471d341b43ff67854a6ed25b620301a454e98a79a9fb80b2a66eb8713546758fd08300d52751e5ca7472c696daa20ee11779b87a830f8 csync2-2.0.tar.gz
+38a17cbf23cbccd4996ad1af049851e33179586e619e3f3edbfacbaa36662e44d916839acd59b1cfc67b3af9042c3258068ec9e5b57f7e26c00a41a0e6f0e148 longlong-format.patch
+c44ad7e7fb7093f66fd9582b5309e3929dfdf7b76ef4d234d346fa25158f088b9436db9a1989d036d739570459867210d053af62125facb66e4f5202448c7bea rsync-strlcpy-disable.patch
+96e5fb1a8865981a014f368b3a644659d048183534a72332718aa815fdf28180e758005e8ae7c438bb8b36134719c2597dd44fc09c85ba13e150f6d396d6b25b csync2.initd"
diff --git a/testing/csync2/csync2-1.34-librsync-1.0.0.patch b/testing/csync2/csync2-1.34-librsync-1.0.0.patch
deleted file mode 100644
index d46653d455..0000000000
--- a/testing/csync2/csync2-1.34-librsync-1.0.0.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-Patch by Robert Scheck <robert@fedoraproject.org> for csync2 <= 1.34 based on the
-patch by Roman Tereshonkov and Kari Hautio to avoid a build failure with librsync
->= 1.0.0 (which is a security bugfix release). The discussion and solution finding
-can be found at https://bugs.launchpad.net/duplicity/+bug/1416344 (for duplicity).
-
---- csync2-1.34/rsync.c 2007-07-24 23:04:18.000000000 +0200
-+++ csync2-1.34/rsync.c.librsync-1.0.0 2015-03-02 00:18:43.000000000 +0100
-@@ -134,8 +134,13 @@
-
- if ( isreg ) {
- csync_debug(3, "Running rs_sig_file() from librsync....\n");
-+#ifdef RS_DEFAULT_STRONG_LEN
- result = rs_sig_file(basis_file, sig_file,
- RS_DEFAULT_BLOCK_LEN, RS_DEFAULT_STRONG_LEN, &stats);
-+#else /* librsync >= 1.0.0 */
-+ result = rs_sig_file(basis_file, sig_file,
-+ RS_DEFAULT_BLOCK_LEN, 8, RS_MD4_SIG_MAGIC, &stats);
-+#endif
- if (result != RS_DONE) {
- csync_debug(0, "Internal error from rsync library!\n");
- goto error;
-@@ -216,8 +221,13 @@
- if ( !basis_file ) basis_file = fopen("/dev/null", "rb");
-
- csync_debug(3, "Running rs_sig_file() from librsync..\n");
-+#ifdef RS_DEFAULT_STRONG_LEN
- result = rs_sig_file(basis_file, sig_file,
- RS_DEFAULT_BLOCK_LEN, RS_DEFAULT_STRONG_LEN, &stats);
-+#else /* librsync >= 1.0.0 */
-+ result = rs_sig_file(basis_file, sig_file,
-+ RS_DEFAULT_BLOCK_LEN, 8, RS_MD4_SIG_MAGIC, &stats);
-+#endif
- if (result != RS_DONE)
- csync_fatal("Got an error from librsync, too bad!\n");
-
diff --git a/testing/csync2/csync2.initd b/testing/csync2/csync2.initd
new file mode 100644
index 0000000000..0caa339d61
--- /dev/null
+++ b/testing/csync2/csync2.initd
@@ -0,0 +1,13 @@
+#!/sbin/openrc-run
+
+name="$SVCNAME"
+pidfile="/var/run/$SVCNAME.pid"
+command="/usr/sbin/$SVCNAME"
+command_args="-ii"
+command_background="yes"
+required_files="/etc/csync2/csync2.cfg /etc/csync2/csync2_ssl_key.pem /etc/csync2/csync2_ssl_cert.pem"
+
+depend() {
+ need net localmount
+ after firewall
+}
diff --git a/testing/csync2/csync2.post-install b/testing/csync2/csync2.post-install
new file mode 100644
index 0000000000..3c2ecc1ecf
--- /dev/null
+++ b/testing/csync2/csync2.post-install
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+SSL="/etc/csync2/csync2_ssl_"
+SERV="/etc/services"
+INETD="/etc/inetd.conf"
+
+if [ ! -f ${SSL}key.pem -o ! -f ${SSL}cert.pem ]; then
+ openssl genrsa -out ${SSL}key.pem 1024 &> /dev/null
+ yes '' | openssl req -new -key ${SSL}key.pem -out ${SSL}cert.csr &> /dev/null
+ openssl x509 -req -days 3600 -in ${SSL}cert.csr -out ${SSL}cert.pem -signkey ${SSL}key.pem &> /dev/null
+ rm ${SSL}cert.csr
+fi
+
+if [ ! -f ${SERV} ]; then
+ touch ${SERV}
+fi
+grep -q csync2 ${SERV} || echo -e "csync2\t\t30865/tcp" >> ${SERV}
+
+if [ ! -f ${INETD} ]; then
+ touch ${INETD}
+fi
+grep -q csync2 ${INETD} || echo "csync2 stream tcp nowait root /usr/sbin/csync2 csync2 -i" >> ${INETD}
+
+exit 0
diff --git a/testing/csync2/csync2.post-upgrade b/testing/csync2/csync2.post-upgrade
new file mode 100644
index 0000000000..26f8e45124
--- /dev/null
+++ b/testing/csync2/csync2.post-upgrade
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+mv /etc/csync2.cfg /etc/csync2/ &> /dev/null
+mv /etc/csync2_ssl_*.pem /etc/csync2/ &> /dev/null
+
+exit 0
diff --git a/testing/csync2/git.patch b/testing/csync2/git.patch
deleted file mode 100644
index 5d9b7094b6..0000000000
--- a/testing/csync2/git.patch
+++ /dev/null
@@ -1,6640 +0,0 @@
-diff --git a/AUTHORS b/AUTHORS
-index 74faa05..45ed0d7 100644
---- a/AUTHORS
-+++ b/AUTHORS
-@@ -1 +1,8 @@
-+LINBIT Information Technologies GmbH <http://www.linbit.com>
- Clifford Wolf <clifford@clifford.at>
-+
-+With contributions from:
-+
-+Lars Ellenberg <lars.ellenberg@linbit.at>
-+Johannes Thoma <johannes.thoma@gmx.at>
-+Dennis Schafroth <dennis@schafroth.dk>
-diff --git a/ChangeLog b/ChangeLog
-index 52fa2a2..9d9f791 100644
---- a/ChangeLog
-+++ b/ChangeLog
-@@ -1,2 +1,2 @@
- Please fetch the ChangeLog directly from the subversion repository:
--svn log -v http://svn.clifford.at/csync2/
-+svn log -v http://svn.linbit.com/csync2/
-diff --git a/Makefile.am b/Makefile.am
-index e3ec933..adbf68d 100644
---- a/Makefile.am
-+++ b/Makefile.am
-@@ -23,16 +23,27 @@ man_MANS = csync2.1
-
- csync2_SOURCES = action.c cfgfile_parser.y cfgfile_scanner.l check.c \
- checktxt.c csync2.c daemon.c db.c error.c getrealfn.c \
-- groups.c rsync.c update.c urlencode.c conn.c prefixsubst.c
-+ groups.c rsync.c update.c urlencode.c conn.c prefixsubst.c \
-+ db_api.c db_sqlite.c db_sqlite2.c db_mysql.c db_postgres.c \
-+ csync2.h db_api.h db_mysql.h db_postgres.h db_sqlite.h db_sqlite2.h dl.h \
-+ csync2-compare \
-+ csync2.1
-+
-+EXTRA_DIST = csync2.cfg csync2.xinetd
-
- AM_YFLAGS = -d
- BUILT_SOURCES = cfgfile_parser.h
-+LIBS += -ldl
- CLEANFILES = cfgfile_parser.c cfgfile_parser.h cfgfile_scanner.c \
-- private_librsync private_libsqlite config.log \
-- config.status config.h .deps/*.Po stamp-h1 Makefile
-+ private_librsync private_libsqlite config.log
-+
-+DISTCLEANFILES = config.status config.h .deps/*.Po stamp-h1 Makefile Makefile.in configure
-+
-+dist-clean-local:
-+ rm -rf autom4te.cache
-
--AM_CFLAGS=
--AM_LDFLAGS=
-+AM_CFLAGS=$(LIBGNUTLS_CFLAGS)
-+AM_LDFLAGS=$(LIBGNUTLS_LIBS)
-
- if PRIVATE_LIBRSYNC
- BUILT_SOURCES += private_librsync
-@@ -41,13 +52,6 @@ if PRIVATE_LIBRSYNC
- LIBS += -lprivatersync
- endif
-
--if PRIVATE_LIBSQLITE
-- BUILT_SOURCES += private_libsqlite
-- AM_CFLAGS += -I$(shell test -f libsqlite.dir && cat libsqlite.dir || echo ==libsqlite==)
-- AM_LDFLAGS += -L$(shell test -f libsqlite.dir && cat libsqlite.dir || echo ==libsqlite==)
-- LIBS += -lprivatesqlite
--endif
--
- AM_CPPFLAGS = -D'DBDIR="$(localstatedir)/lib/csync2"'
- AM_CPPFLAGS += -D'ETCDIR="$(sysconfdir)"'
-
-diff --git a/README b/README
-index ed6eb6b..7dbbae1 100644
---- a/README
-+++ b/README
-@@ -12,7 +12,7 @@ better have a look at Unison (http://www.cis.upenn.edu/~bcpierce/unison/)
- too.
-
- See http://oss.linbit.com/ for more information on csync2. The csync2
--subversion tree can be found at http://svn.clifford.at/csync2/.
-+subversion tree can be found at http://svn.linbit.com/csync2/.
-
-
- Copyright
-@@ -76,3 +76,25 @@ There is a csync2 mailing list:
- It is recommended to subscribe to this list if you are using csync2 in
- production environments.
-
-+Building csync2
-+===============
-+
-+You'll need the GNU autotools and a compiler toolchain (gcc) for
-+building csync2.
-+
-+First, run the autogen.sh script:
-+
-+karin$ ./autogen.sh
-+
-+Then run configure, use ./configure --help for more options:
-+
-+karin$ ./configure
-+
-+Then run make:
-+
-+karin$ make
-+
-+csync2 should be built now. Direct any questions to the csync2 mailing list
-+(see above).
-+
-+- Johannes
-diff --git a/TODO b/TODO
-index 6c02fc3..0ee83ff 100644
---- a/TODO
-+++ b/TODO
-@@ -1 +1,74 @@
- Universal peace and a good attitude for everyone.
-+
-+Check for mysql/mysql.h to exist in configure.
-+ Done
-+
-+DB abstraction: check for installed databases on configure
-+ and enable/disable them for compilation.
-+
-+Create MySQL database if it doesn't exist.
-+
-+Implement table creation with schema support.
-+ We don't have a schema table yet, add it when it is needed.
-+
-+Have check return value for asprintf () .. have a macro that does a csync_fatal
-+ if there is no memory.
-+
-+Make database configurable.
-+
-+From Dennis:
-+Filename column is too short, but this is due to the fact that mysql 5 only
-+supports keys length of max 1000 bytes.
-+So the filename+peername must be below 333 UTF characters (since mysql looks at
-+worst-case when generating the tables).
-+ Sort of fixed. Fields are 4096 bytes now (highest MAXPATHLEN of all
-+ supported platforms) but only the first 1000 chars are unique.
-+
-+sqlite3:// url not working
-+ It works but it needs an extra slash like in sqlite3:///var/lib/...
-+ Now have a howto if slash is missing and database file is not found.
-+
-+-a should be stronger than configured database in /etc/csync2.cfg
-+ Works now.
-+
-+test long filenames with mysql
-+ Work now
-+
-+From Dennis:
-+Weird characters in filename cuts off the filename at the character. I have a
-+danish letter (å encoded in iso-8859-1: \370) still present in my
-+now UTF-8 filesystem names.
-+ Couldn't reproduce tried with German umlauts.
-+
-+---------------------------------------------------------------------------
-+
-+Test schema support for SQLite 2.
-+
-+Have command to pipe connection through (for SSH support for example)
-+
-+From Gerhard Rieger:
-+If there are more than one node to sync with print nodes that are not reachable.
-+ Done, test it
-+
-+ Segfault when syncing a file where one side is a directory and the other one
-+ is a link.
-+
-+postgres support
-+
-+dl_open for all sql related calls
-+ we don't want to depend on libmysql/libsqlite/whatever on install.
-+ TODO: how to express that we need at least one sql client library in Debian/RPM
-+
-+Performance tests: when does it make sense to use mysql instead of sqlite?
-+
-+Have schema version table.
-+
-+Compile even when there is no libsqlite (mysql support only)
-+
-+From Martin: Provide up-to-date packages.
-+ Resuse build.sh script from drbd-proxy.
-+
-+Build packages for all supported distros.
-+
-+If include <dir> is missing error message is Permission denied, which is
-+ irritating.
-diff --git a/action.c b/action.c
-index 438db5c..9ac8126 100644
---- a/action.c
-+++ b/action.c
-@@ -38,14 +38,18 @@ void csync_schedule_commands(const char *filename, int islocal)
-
- while ( (g=csync_find_next(g, filename)) ) {
- for (a=g->action; a; a=a->next) {
-+ if ( !islocal && a->do_local_only )
-+ continue;
- if ( islocal && !a->do_local )
- continue;
- if (!a->pattern)
- goto found_matching_pattern;
-- for (p=a->pattern; p; p=p->next)
-+ for (p=a->pattern; p; p=p->next) {
-+ int fnm_pathname = p->star_matches_slashes ? 0 : FNM_PATHNAME;
- if ( !fnmatch(p->pattern, filename,
-- FNM_LEADING_DIR|FNM_PATHNAME) )
-+ FNM_LEADING_DIR|fnm_pathname) )
- goto found_matching_pattern;
-+ }
- continue;
- found_matching_pattern:
- for (c=a->command; c; c=c->next)
-@@ -69,7 +73,7 @@ void csync_run_single_command(const char *command, const char *logfile)
- "SELECT filename from action WHERE command = '%s' "
- "and logfile = '%s'", command, logfile)
- {
-- textlist_add(&tl, SQL_V[0], 0);
-+ textlist_add(&tl, SQL_V(0), 0);
- } SQL_END;
-
- mark = strstr(command_clr, "%%");
-@@ -107,7 +111,7 @@ void csync_run_single_command(const char *command, const char *logfile)
- /* 1 */ open(logfile_clr, O_WRONLY|O_CREAT|O_APPEND, 0666);
- /* 2 */ open(logfile_clr, O_WRONLY|O_CREAT|O_APPEND, 0666);
-
-- execl("/bin/sh", "sh", "-c", real_command, 0);
-+ execl("/bin/sh", "sh", "-c", real_command, NULL);
- _exit(127);
- }
-
-@@ -130,7 +134,7 @@ void csync_run_commands()
- SQL_BEGIN("Checking for sceduled commands",
- "SELECT command, logfile FROM action GROUP BY command, logfile")
- {
-- textlist_add2(&tl, SQL_V[0], SQL_V[1], 0);
-+ textlist_add2(&tl, SQL_V(0), SQL_V(1), 0);
- } SQL_END;
-
- for (t = tl; t != 0; t = t->next)
-diff --git a/autogen.sh b/autogen.sh
-index df9e797..85663ac 100755
---- a/autogen.sh
-+++ b/autogen.sh
-@@ -18,9 +18,9 @@
- # along with this program; if not, write to the Free Software
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
--aclocal-1.7
-+aclocal
- autoheader
--automake-1.7 --add-missing --copy
-+automake --add-missing --copy
- autoconf
-
- if [ "$1" = clean ]; then
-@@ -32,5 +32,13 @@ if [ "$1" = clean ]; then
- rm -rf config.guess config.sub
- rm -rf cygwin/librsync-0.9.7.tar.gz
- rm -rf cygwin/sqlite-2.8.16.tar.gz
-+else
-+ ./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc
-+
-+ echo ""
-+ echo "Configured as"
-+ echo "./configure --prefix=/usr --localstatedir=/var --sysconfdir=/etc"
-+ echo ""
-+ echo "reconfigure, if you want it different"
- fi
-
-diff --git a/cfgfile_parser.y b/cfgfile_parser.y
-index 776bbcf..7f493ab 100644
---- a/cfgfile_parser.y
-+++ b/cfgfile_parser.y
-@@ -33,6 +33,8 @@ struct csync_nossl *csync_nossl = 0;
- int csync_ignore_uid = 0;
- int csync_ignore_gid = 0;
- int csync_ignore_mod = 0;
-+unsigned csync_lock_timeout = 12;
-+char *csync_tempdir = NULL;
-
- #ifdef __CYGWIN__
- int csync_lowercyg_disable = 0;
-@@ -51,11 +53,12 @@ void yyerror(char *text)
- static void new_group(char *name)
- {
- int static autonum = 1;
-+ int rc;
- struct csync_group *t =
- calloc(sizeof(struct csync_group), 1);
-
- if (name == 0)
-- asprintf(&name, "group_%d", autonum++);
-+ rc = asprintf(&name, "group_%d", autonum++);
-
- t->next = csync_group;
- t->auto_method = -1;
-@@ -106,12 +109,17 @@ static void add_patt(int patterntype, char *pattern)
- }
- #endif
-
-+ /* strip trailing slashes from pattern */
- for (i=strlen(pattern)-1; i>0; i--)
- if (pattern[i] == '/')
- pattern[i] = 0;
- else
- break;
-
-+ /* if you use ** at least once anywhere in the pattern,
-+ * _all_ stars in the pattern, even single ones,
-+ * will match slashes. */
-+ t->star_matches_slashes = !!strstr(pattern, "**");
- t->isinclude = patterntype >= 1;
- t->iscompare = patterntype >= 2;
- t->pattern = pattern;
-@@ -280,6 +288,7 @@ static void add_action_pattern(const char *pattern)
- {
- struct csync_group_action_pattern *t =
- calloc(sizeof(struct csync_group_action_pattern), 1);
-+ t->star_matches_slashes = !!strstr(pattern, "**");
- t->pattern = pattern;
- t->next = csync_group->action->pattern;
- csync_group->action->pattern = t;
-@@ -304,6 +313,28 @@ static void set_action_dolocal()
- csync_group->action->do_local = 1;
- }
-
-+static void set_action_dolocal_only()
-+{
-+ csync_group->action->do_local = 1;
-+ csync_group->action->do_local_only = 1;
-+}
-+
-+static void set_lock_timeout(const char *timeout)
-+{
-+ csync_lock_timeout = atoi(timeout);
-+}
-+
-+static void set_tempdir(const char *tempdir)
-+{
-+ csync_tempdir = strdup(tempdir);
-+}
-+
-+static void set_database(const char *filename)
-+{
-+ if (!csync_database)
-+ csync_database = strdup(filename);
-+}
-+
- static void new_prefix(const char *pname)
- {
- struct csync_prefix *p =
-@@ -392,10 +423,12 @@ static void disable_cygwin_lowercase_hack()
- }
-
- %token TK_BLOCK_BEGIN TK_BLOCK_END TK_STEND TK_AT TK_AUTO
--%token TK_NOSSL TK_IGNORE TK_GROUP TK_HOST TK_EXCL TK_INCL TK_COMP TK_KEY
-+%token TK_NOSSL TK_IGNORE TK_GROUP TK_HOST TK_EXCL TK_INCL TK_COMP TK_KEY TK_DATABASE
- %token TK_ACTION TK_PATTERN TK_EXEC TK_DOLOCAL TK_LOGFILE TK_NOCYGLOWER
- %token TK_PREFIX TK_ON TK_COLON TK_POPEN TK_PCLOSE
--%token TK_BAK_DIR TK_BAK_GEN
-+%token TK_BAK_DIR TK_BAK_GEN TK_DOLOCALONLY
-+%token TK_TEMPDIR
-+%token TK_LOCK_TIMEOUT
- %token <txt> TK_STRING
-
- %%
-@@ -413,9 +446,15 @@ block:
- { }
- | TK_NOSSL TK_STRING TK_STRING TK_STEND
- { new_nossl($2, $3); }
-+| TK_DATABASE TK_STRING TK_STEND
-+ { set_database($2); }
-+| TK_TEMPDIR TK_STRING TK_STEND
-+ { set_tempdir($2); }
- | TK_IGNORE ignore_list TK_STEND
- | TK_NOCYGLOWER TK_STEND
- { disable_cygwin_lowercase_hack(); }
-+| TK_LOCK_TIMEOUT TK_STRING TK_STEND
-+ { set_lock_timeout($2); }
- ;
-
- ignore_list:
-@@ -517,6 +556,8 @@ action_stmt:
- { set_action_logfile($2); }
- | TK_DOLOCAL
- { set_action_dolocal(); }
-+| TK_DOLOCALONLY
-+ { set_action_dolocal_only(); }
- ;
-
- action_pattern_list:
-diff --git a/cfgfile_scanner.l b/cfgfile_scanner.l
-index 77daf5f..5e93f7c 100644
---- a/cfgfile_scanner.l
-+++ b/cfgfile_scanner.l
-@@ -25,9 +25,13 @@
- #define MAX_INCLUDE_DEPTH 10
- YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
- int include_stack_ptr = 0;
-+
-+#define YY_NO_INPUT 1
-+#define YY_NO_UNPUT 1
- %}
-
- %option noyywrap yylineno
-+%option nounput
- %x STRING INCL
-
- %%
-@@ -42,6 +46,7 @@ int include_stack_ptr = 0;
-
- "nossl" { return TK_NOSSL; }
- "ignore" { return TK_IGNORE; }
-+"database" { return TK_DATABASE; }
-
- "group" { return TK_GROUP; }
- "host" { return TK_HOST; }
-@@ -56,10 +61,13 @@ int include_stack_ptr = 0;
- "exec" { return TK_EXEC; }
- "logfile" { return TK_LOGFILE; }
- "do-local" { return TK_DOLOCAL; }
-+"do-local-only" { return TK_DOLOCALONLY; }
-
- "prefix" { return TK_PREFIX; }
- "on" { return TK_ON; }
-
-+"lock-timeout" { return TK_LOCK_TIMEOUT; }
-+"tempdir" { return TK_TEMPDIR; }
- "backup-directory" { return TK_BAK_DIR; }
- "backup-generations" { return TK_BAK_GEN; }
-
-diff --git a/check.c b/check.c
-index 360abd3..c5b9f32 100644
---- a/check.c
-+++ b/check.c
-@@ -99,15 +99,20 @@ void csync_mark(const char *file, const char *thispeer, const char *peerfilter)
-
- csync_debug(1, "Marking file as dirty: %s\n", file);
- for (pl_idx=0; pl[pl_idx].peername; pl_idx++)
-- if (!peerfilter || !strcmp(peerfilter, pl[pl_idx].peername))
-+ if (!peerfilter || !strcmp(peerfilter, pl[pl_idx].peername)) {
-+ SQL("Deleting old dirty file entries",
-+ "DELETE FROM dirty WHERE filename = '%s' AND peername = '%s'",
-+ url_encode(file),
-+ url_encode(pl[pl_idx].peername));
-+
- SQL("Marking File Dirty",
-- "%s INTO dirty (filename, force, myname, peername) "
-+ "INSERT INTO dirty (filename, forced, myname, peername) "
- "VALUES ('%s', %s, '%s', '%s')",
-- csync_new_force ? "REPLACE" : "INSERT",
- url_encode(file),
- csync_new_force ? "1" : "0",
- url_encode(pl[pl_idx].myname),
- url_encode(pl[pl_idx].peername));
-+ }
-
- free(pl);
- }
-@@ -122,21 +127,83 @@ int csync_check_pure(const char *filename)
- if (!csync_lowercyg_disable)
- return 0;
- #endif
--
- struct stat sbuf;
-- int i=0;
-+ int dir_len = 0;
-+ int i;
-+ int same_len;
-+
-+ /* single entry last query cache
-+ * to speed up checks from deep subdirs */
-+ static struct {
-+ /* store inclusive trailing slash for prefix match */
-+ char *path;
-+ /* strlen(path) */
-+ int len;
-+ /* cached return value */
-+ int has_symlink;
-+ } cached;
-+
-+ for (i = 0; filename[i]; i++)
-+ if (filename[i] == '/')
-+ dir_len = i+1;
-+
-+ if (dir_len <= 1) /* '/' a symlink? hardly. */
-+ return 0;
-+
-+ /* identical prefix part */
-+ for (i = 0; i < dir_len && i < cached.len; i++)
-+ if (filename[i] != cached.path[i])
-+ break;
-+
-+ /* backtrack to slash */
-+ for (--i; i >= 0 && cached.path[i] != '/'; --i);
-+ ;
-
-- while (filename[i]) i++;
-+ same_len = i+1;
-+
-+ csync_debug(3, " check: %s %u, %s %u, %u.\n", filename, dir_len, cached.path, cached.len, same_len);
-+ /* exact match? */
-+ if (dir_len == same_len && same_len == cached.len)
-+ return cached.has_symlink;
-
- { /* new block for myfilename[] */
-- char myfilename[i+1];
-- memcpy(myfilename, filename, i+1);
-- while (1) {
-- while (myfilename[i] != '/')
-- if (--i <= 0) return 0;
-+ char myfilename[dir_len+1];
-+ char *to_be_cached;
-+ int has_symlink = 0;
-+ memcpy(myfilename, filename, dir_len);
-+ myfilename[dir_len] = '\0';
-+ to_be_cached = strdup(myfilename);
-+ i = dir_len-1;
-+ while (i) {
-+ for (; i && myfilename[i] != '/'; --i)
-+ ;
-+
-+ if (i <= 1)
-+ break;
-+
-+ if (i+1 == same_len) {
-+ if (same_len == cached.len) {
-+ /* exact match */
-+ has_symlink = cached.has_symlink;
-+ break;
-+ } else if (!cached.has_symlink)
-+ /* prefix of something 'pure' */
-+ break;
-+ }
-+
- myfilename[i]=0;
-- if ( lstat_strict(prefixsubst(myfilename), &sbuf) || S_ISLNK(sbuf.st_mode) ) return 1;
-+ if (lstat_strict(prefixsubst(myfilename), &sbuf) || S_ISLNK(sbuf.st_mode)) {
-+ has_symlink = 1;
-+ break;
-+ }
- }
-+ if (to_be_cached) { /* strdup can fail. So what. */
-+ free(cached.path);
-+ cached.path = to_be_cached;
-+ cached.len = dir_len;
-+ cached.has_symlink = has_symlink;
-+ }
-+ return has_symlink;
- }
- }
-
-@@ -148,18 +215,22 @@ void csync_check_del(const char *file, int recursive, int init_run)
-
- if ( recursive ) {
- if ( !strcmp(file, "/") )
-- asprintf(&where_rec, "or 1");
-+ ASPRINTF(&where_rec, "OR 1=1");
- else
-- asprintf(&where_rec, "or (filename > '%s/' "
-- "and filename < '%s0')",
-- url_encode(file), url_encode(file));
-+ ASPRINTF(&where_rec, "UNION ALL SELECT filename from file where filename > '%s/' "
-+ "and filename < '%s0'",
-+ url_encode(file), url_encode(file));
- }
-
- SQL_BEGIN("Checking for removed files",
- "SELECT filename from file where "
- "filename = '%s' %s ORDER BY filename", url_encode(file), where_rec)
- {
-- const char *filename = url_decode(SQL_V[0]);
-+ const char *filename = url_decode(SQL_V(0));
-+
-+ if (!csync_match_file(filename))
-+ continue;
-+
- if ( lstat_strict(prefixsubst(filename), &st) != 0 || csync_check_pure(filename) )
- textlist_add(&tl, filename, 0);
- } SQL_END;
-@@ -231,7 +302,7 @@ int csync_check_mod(const char *file, int recursive, int ignnoent, int init_run)
- "filename = '%s'", url_encode(file))
- {
- if ( !csync_cmpchecktxt(checktxt,
-- url_decode(SQL_V[0])) ) {
-+ url_decode(SQL_V(0))) ) {
- csync_debug(2, "File has changed: %s\n", file);
- this_is_dirty = 1;
- }
-@@ -243,6 +314,10 @@ int csync_check_mod(const char *file, int recursive, int ignnoent, int init_run)
- } SQL_END;
-
- if ( this_is_dirty && !csync_compare_mode ) {
-+ SQL("Deleting old file entry",
-+ "DELETE FROM file WHERE filename = '%s'",
-+ url_encode(file));
-+
- SQL("Adding or updating file entry",
- "INSERT INTO file (filename, checktxt) "
- "VALUES ('%s', '%s')",
-diff --git a/configure.ac b/configure.ac
-index 6ec6136..8989a33 100644
---- a/configure.ac
-+++ b/configure.ac
-@@ -17,15 +17,15 @@
- # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- # Process this file with autoconf to produce a configure script.
--AC_INIT(csync2, 1.34, clifford@clifford.at)
-+AC_INIT(csync2, 2.0rc1, csync2@lists.linbit.com)
- AM_INIT_AUTOMAKE
-
- AC_CONFIG_SRCDIR(csync2.c)
- AM_CONFIG_HEADER(config.h)
-
- # Use /etc and /var instead of $prefix/...
--test "$localstatedir" = '${prefix}/var' && localstatedir=/var
--test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
-+# test "$localstatedir" = '${prefix}/var' && localstatedir=/var
-+# test "$sysconfdir" = '${prefix}/etc' && sysconfdir=/etc
-
- # Checks for programs.
- AC_PROG_CC
-@@ -33,6 +33,9 @@ AC_PROG_INSTALL
- AC_PROG_YACC
- AM_PROG_LEX
-
-+# check for large file support
-+AC_SYS_LARGEFILE
-+
- # Check for librsync.
- AC_ARG_WITH([librsync-source],
- AS_HELP_STRING([--with-librsync-source=source-tar-file],
-@@ -42,35 +45,78 @@ AC_ARG_WITH([librsync-source],
- )
- AM_CONDITIONAL([PRIVATE_LIBRSYNC], [test -n "$librsync_source_file"])
-
--# Check for libsqlite.
--AC_ARG_WITH([libsqlite-source],
-- AS_HELP_STRING([--with-libsqlite-source=source-tar-file],
-- [build this libsqlite and link statically against it (hack! hack!)]),
-- AC_SUBST([libsqlite_source_file], $withval),
-- AC_CHECK_LIB([sqlite], [sqlite_exec], , [AC_MSG_ERROR(libsqlite is required)])
--)
--AM_CONDITIONAL([PRIVATE_LIBSQLITE], [test -n "$libsqlite_source_file"])
-+AC_ARG_ENABLE([sqlite],
-+ [AC_HELP_STRING([--enable-sqlite],
-+ [enable/disable sqlite 2 support (default is disabled)])],
-+ [], [ enable_sqlite=no ])
-+
-+if test "$enable_sqlite" == yes
-+then
-+ AC_CHECK_HEADERS([sqlite.h], , [AC_MSG_ERROR([[SQLite header not found; install libsqlite-dev and dependencies for SQLite 2 support]])])
-+
-+ AC_DEFINE([HAVE_SQLITE], 1, [Define if sqlite 2 support is wanted])
-+fi
-+
-+AC_ARG_ENABLE([sqlite3],
-+ [AC_HELP_STRING([--disable-sqlite3],
-+ [enable/disable sqlite3 support (default is enabled)])],
-+ [], [ enable_sqlite3=yes ])
-+
-+if test "$enable_sqlite3" == yes
-+then
-+ AC_CHECK_HEADERS([sqlite3.h], , [AC_MSG_ERROR([[SQLite header not found; install libsqlite3-dev and dependencies for SQLite 3 support]])])
-+
-+ AC_DEFINE([HAVE_SQLITE3], 1, [Define if sqlite3 support is wanted])
-+fi
-
- AC_ARG_ENABLE([gnutls],
-- [AC_HELP_STRING([--disable-gnutls],
-- [enable/disable GNU TLS support (default is enabled)])],
-+ [AS_HELP_STRING([--disable-gnutls],[enable/disable GNU TLS support (default is enabled)])],
- [], [ enable_gnutls=yes ])
-
- if test "$enable_gnutls" != no
- then
-+ PKG_PROG_PKG_CONFIG
-+ PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= 2.6.0], [
-+ AC_DEFINE([HAVE_LIBGNUTLS], 1, [Define to 1 when using GNU TLS library])
-+ ])
-+fi
-
-- # Check for gnuTLS.
-- AM_PATH_LIBGNUTLS(1.0.0, , [ AC_MSG_ERROR([[gnutls not found; install gnutls, gnutls-openssl and libtasn1 packages for your system or run configure with --disable-gnutls]]) ])
-+AC_ARG_ENABLE([mysql],
-+ [AC_HELP_STRING([--enable-mysql],
-+ [enable/disable MySQL support (default is disabled)])],
-+ [], [ enable_mysql=no ])
-
-+AC_ARG_ENABLE([postgres],
-+ [AC_HELP_STRING([--enable-postgres],
-+ [enable/disable Postgres support (default is disabled)])],
-+ [], [ enable_postgres=no ])
-+
-+if test "$enable_mysql" == yes
-+then
-+ # Check for mysql.
- # This is a bloody hack for fedora core
-- CFLAGS="$CFLAGS $LIBGNUTLS_CFLAGS"
-- LIBS="$LIBS $LIBGNUTLS_LIBS -ltasn1"
-+ CFLAGS="$CFLAGS `mysql_config --cflags`"
-+
-+ # Check MySQL development header
-+ AC_CHECK_HEADERS([mysql/mysql.h], , [AC_MSG_ERROR([[mysql header not found; install mysql-devel and dependencies for MySQL Support]])])
-
-- # Check gnuTLS SSL compatibility lib.
-- AC_CHECK_LIB([gnutls-openssl], [SSL_new], , [AC_MSG_ERROR([[gnutls-openssl not found; install gnutls, gnutls-openssl and libtasn1 packages for your system or run configure with --disable-gnutls]])])
-+ AC_DEFINE([HAVE_MYSQL], 1, [Define if mysql support is wanted])
-+fi
-+
-+if test "$enable_postgres" == yes
-+then
-+ AC_CHECK_HEADERS([postgresql/libpq-fe.h], , [AC_MSG_ERROR([[postgres header not found; install libpq-dev and dependencies for Postgres support]])])
-
-+ AC_DEFINE([HAVE_POSTGRES], 1, [Define if postgres support is wanted])
-+fi
-+
-+# at least one db backend must be configured.
-+
-+if test "$enable_postgres" != yes && test "$enable_mysql" != yes &&
-+ test "$enable_sqlite3" != yes && test "$enable_sqlite" != yes
-+then
-+ AC_MSG_ERROR([No database backend configured. Please enable either sqlite, sqlite3, mysql or postgres.])
- fi
-
- AC_CONFIG_FILES([Makefile])
- AC_OUTPUT
--
-diff --git a/conn.c b/conn.c
-index 6f8dfdc..8dda10d 100644
---- a/conn.c
-+++ b/conn.c
-@@ -30,52 +30,77 @@
- #include <netdb.h>
- #include <errno.h>
-
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- # include <gnutls/gnutls.h>
--# include <gnutls/openssl.h>
-+# include <gnutls/x509.h>
- #endif
-
- int conn_fd_in = -1;
- int conn_fd_out = -1;
- int conn_clisok = 0;
-
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- int csync_conn_usessl = 0;
-
--SSL_METHOD *conn_ssl_meth;
--SSL_CTX *conn_ssl_ctx;
--SSL *conn_ssl;
-+static gnutls_session_t conn_tls_session;
-+static gnutls_certificate_credentials_t conn_x509_cred;
- #endif
-
-+
-+/* getaddrinfo stuff mostly copied from its manpage */
-+int conn_connect(const char *peername)
-+{
-+ struct addrinfo hints;
-+ struct addrinfo *result, *rp;
-+ int sfd, s;
-+
-+ /* Obtain address(es) matching host/port */
-+ memset(&hints, 0, sizeof(struct addrinfo));
-+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
-+ hints.ai_socktype = SOCK_STREAM;
-+ hints.ai_flags = 0;
-+ hints.ai_protocol = 0; /* Any protocol */
-+
-+ s = getaddrinfo(peername, csync_port, &hints, &result);
-+ if (s != 0) {
-+ csync_debug(1, "Cannot resolve peername, getaddrinfo: %s\n", gai_strerror(s));
-+ return -1;
-+ }
-+
-+ /* getaddrinfo() returns a list of address structures.
-+ Try each address until we successfully connect(2).
-+ If socket(2) (or connect(2)) fails, we (close the socket
-+ and) try the next address. */
-+
-+ for (rp = result; rp != NULL; rp = rp->ai_next) {
-+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-+ if (sfd == -1)
-+ continue;
-+
-+ if (connect(sfd, rp->ai_addr, rp->ai_addrlen) != -1)
-+ break; /* Success */
-+
-+ close(sfd);
-+ }
-+ freeaddrinfo(result); /* No longer needed */
-+
-+ if (rp == NULL) /* No address succeeded */
-+ return -1;
-+
-+ return sfd;
-+}
-+
- int conn_open(const char *peername)
- {
-- struct sockaddr_in sin;
-- struct hostent *hp;
- int on = 1;
-
-- hp = gethostbyname(peername);
-- if ( ! hp ) {
-- csync_debug(1, "Can't resolve peername.\n");
-- return -1;
-- }
--
-- conn_fd_in = socket(hp->h_addrtype, SOCK_STREAM, 0);
-+ conn_fd_in = conn_connect(peername);
- if (conn_fd_in < 0) {
- csync_debug(1, "Can't create socket.\n");
- return -1;
- }
-
-- sin.sin_family = hp->h_addrtype;
-- bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
-- sin.sin_port = htons(csync_port);
--
-- if (connect(conn_fd_in, (struct sockaddr *)&sin, sizeof (sin)) < 0) {
-- csync_debug(1, "Can't connect to remote host.\n");
-- close(conn_fd_in); conn_fd_in = -1;
-- return -1;
-- }
--
-- if (setsockopt(conn_fd_in, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) ) < 0 ) {
-+ if (setsockopt(conn_fd_in, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on) ) < 0) {
- csync_debug(1, "Can't set TCP_NODELAY option on TCP socket.\n");
- close(conn_fd_in); conn_fd_in = -1;
- return -1;
-@@ -83,10 +108,9 @@ int conn_open(const char *peername)
-
- conn_fd_out = conn_fd_in;
- conn_clisok = 1;
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- csync_conn_usessl = 0;
- #endif
--
- return 0;
- }
-
-@@ -97,12 +121,13 @@ int conn_set(int infd, int outfd)
- conn_fd_in = infd;
- conn_fd_out = outfd;
- conn_clisok = 1;
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- csync_conn_usessl = 0;
- #endif
-
- // when running in server mode, this has been done already
- // in csync2.c with more restrictive error handling..
-+ // FIXME don't even try in "ssh" mode
- if ( setsockopt(conn_fd_out, IPPROTO_TCP, TCP_NODELAY, &on, (socklen_t) sizeof(on)) < 0 )
- csync_debug(1, "Can't set TCP_NODELAY option on TCP socket.\n");
-
-@@ -110,43 +135,106 @@ int conn_set(int infd, int outfd)
- }
-
-
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
-+
-+static void ssl_log(int level, const char* msg)
-+{ csync_debug(level, "%s", msg); }
-
--char *ssl_keyfile = ETCDIR "/csync2_ssl_key.pem";
--char *ssl_certfile = ETCDIR "/csync2_ssl_cert.pem";
-+static const char *ssl_keyfile = ETCDIR "/csync2_ssl_key.pem";
-+static const char *ssl_certfile = ETCDIR "/csync2_ssl_cert.pem";
-
- int conn_activate_ssl(int server_role)
- {
-- static int sslinit = 0;
-+ gnutls_alert_description_t alrt;
-+ int err;
-
- if (csync_conn_usessl)
- return 0;
-
-- if (!sslinit) {
-- SSL_load_error_strings();
-- SSL_library_init();
-- sslinit=1;
-- }
-+ gnutls_global_init();
-+ gnutls_global_set_log_function(ssl_log);
-+ gnutls_global_set_log_level(10);
-
-- conn_ssl_meth = (server_role ? SSLv23_server_method : SSLv23_client_method)();
-- conn_ssl_ctx = SSL_CTX_new(conn_ssl_meth);
-+ gnutls_certificate_allocate_credentials(&conn_x509_cred);
-
-- if (SSL_CTX_use_PrivateKey_file(conn_ssl_ctx, ssl_keyfile, SSL_FILETYPE_PEM) <= 0)
-- csync_fatal("SSL: failed to use key file %s.\n", ssl_keyfile);
-+ err = gnutls_certificate_set_x509_key_file(conn_x509_cred, ssl_certfile, ssl_keyfile, GNUTLS_X509_FMT_PEM);
-+ if(err != GNUTLS_E_SUCCESS) {
-+ gnutls_certificate_free_credentials(conn_x509_cred);
-+ gnutls_global_deinit();
-
-- if (SSL_CTX_use_certificate_file(conn_ssl_ctx, ssl_certfile, SSL_FILETYPE_PEM) <= 0)
-- csync_fatal("SSL: failed to use certificate file %s.\n", ssl_certfile);
-+ csync_fatal(
-+ "SSL: failed to use key file %s and/or certificate file %s: %s (%s)\n",
-+ ssl_keyfile,
-+ ssl_certfile,
-+ gnutls_strerror(err),
-+ gnutls_strerror_name(err)
-+ );
-+ }
-
-- if (! (conn_ssl = SSL_new(conn_ssl_ctx)) )
-- csync_fatal("Creating a new SSL handle failed.\n");
-+ if(server_role) {
-+ gnutls_certificate_free_cas(conn_x509_cred);
-
-- gnutls_certificate_server_set_request(conn_ssl->gnutls_state, GNUTLS_CERT_REQUIRE);
-+ if(gnutls_certificate_set_x509_trust_file(conn_x509_cred, ssl_certfile, GNUTLS_X509_FMT_PEM) < 1) {
-+ gnutls_certificate_free_credentials(conn_x509_cred);
-+ gnutls_global_deinit();
-
-- SSL_set_rfd(conn_ssl, conn_fd_in);
-- SSL_set_wfd(conn_ssl, conn_fd_out);
-+ csync_fatal(
-+ "SSL: failed to use certificate file %s as CA.\n",
-+ ssl_certfile
-+ );
-+ }
-+ } else
-+ gnutls_certificate_free_ca_names(conn_x509_cred);
-+
-+ gnutls_init(&conn_tls_session, (server_role ? GNUTLS_SERVER : GNUTLS_CLIENT));
-+ gnutls_priority_set_direct(conn_tls_session, "PERFORMANCE", NULL);
-+ gnutls_credentials_set(conn_tls_session, GNUTLS_CRD_CERTIFICATE, conn_x509_cred);
-
-- if ( (server_role ? SSL_accept : SSL_connect)(conn_ssl) < 1 )
-- csync_fatal("Establishing SSL connection failed.\n");
-+ if(server_role) {
-+ gnutls_certificate_send_x509_rdn_sequence(conn_tls_session, 0);
-+ gnutls_certificate_server_set_request(conn_tls_session, GNUTLS_CERT_REQUIRE);
-+ }
-+
-+ gnutls_transport_set_ptr2(
-+ conn_tls_session,
-+ (gnutls_transport_ptr_t)conn_fd_in,
-+ (gnutls_transport_ptr_t)conn_fd_out
-+ );
-+
-+ err = gnutls_handshake(conn_tls_session);
-+ switch(err) {
-+ case GNUTLS_E_SUCCESS:
-+ break;
-+
-+ case GNUTLS_E_WARNING_ALERT_RECEIVED:
-+ alrt = gnutls_alert_get(conn_tls_session);
-+ fprintf(
-+ csync_debug_out,
-+ "SSL: warning alert received from peer: %d (%s).\n",
-+ alrt, gnutls_alert_get_name(alrt)
-+ );
-+ break;
-+
-+ case GNUTLS_E_FATAL_ALERT_RECEIVED:
-+ alrt = gnutls_alert_get(conn_tls_session);
-+ fprintf(
-+ csync_debug_out,
-+ "SSL: fatal alert received from peer: %d (%s).\n",
-+ alrt, gnutls_alert_get_name(alrt)
-+ );
-+
-+ default:
-+ gnutls_bye(conn_tls_session, GNUTLS_SHUT_RDWR);
-+ gnutls_deinit(conn_tls_session);
-+ gnutls_certificate_free_credentials(conn_x509_cred);
-+ gnutls_global_deinit();
-+
-+ csync_fatal(
-+ "SSL: handshake failed: %s (%s)\n",
-+ gnutls_strerror(err),
-+ gnutls_strerror_name(err)
-+ );
-+ }
-
- csync_conn_usessl = 1;
-
-@@ -155,15 +243,15 @@ int conn_activate_ssl(int server_role)
-
- int conn_check_peer_cert(const char *peername, int callfatal)
- {
-- const X509 *peercert;
-+ const gnutls_datum_t *peercerts;
-+ unsigned npeercerts;
- int i, cert_is_ok = -1;
-
- if (!csync_conn_usessl)
- return 1;
-
-- peercert = SSL_get_peer_certificate(conn_ssl);
--
-- if (!peercert || peercert->size <= 0) {
-+ peercerts = gnutls_certificate_get_peers(conn_tls_session, &npeercerts);
-+ if(peercerts == NULL || npeercerts == 0) {
- if (callfatal)
- csync_fatal("Peer did not provide an SSL X509 cetrificate.\n");
- csync_debug(1, "Peer did not provide an SSL X509 cetrificate.\n");
-@@ -171,17 +259,17 @@ int conn_check_peer_cert(const char *peername, int callfatal)
- }
-
- {
-- char certdata[peercert->size*2 + 1];
-+ char certdata[2*peercerts[0].size + 1];
-
-- for (i=0; i<peercert->size; i++)
-- sprintf(certdata+i*2, "%02X", peercert->data[i]);
-- certdata[peercert->size*2] = 0;
-+ for (i=0; i<peercerts[0].size; i++)
-+ sprintf(&certdata[2*i], "%02X", peercerts[0].data[i]);
-+ certdata[2*i] = 0;
-
- SQL_BEGIN("Checking peer x509 certificate.",
- "SELECT certdata FROM x509_cert WHERE peername = '%s'",
- url_encode(peername))
- {
-- if (!strcmp(SQL_V[0], certdata))
-+ if (!strcmp(SQL_V(0), certdata))
- cert_is_ok = 1;
- else
- cert_is_ok = 0;
-@@ -215,14 +303,19 @@ int conn_check_peer_cert(const char *peername, int callfatal)
- return 1;
- }
-
--#endif /* HAVE_LIBGNUTLS_OPENSSL */
-+#endif /* HAVE_LIBGNUTLS */
-
- int conn_close()
- {
- if ( !conn_clisok ) return -1;
-
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-- if ( csync_conn_usessl ) SSL_free(conn_ssl);
-+#ifdef HAVE_LIBGNUTLS
-+ if ( csync_conn_usessl ) {
-+ gnutls_bye(conn_tls_session, GNUTLS_SHUT_RDWR);
-+ gnutls_deinit(conn_tls_session);
-+ gnutls_certificate_free_credentials(conn_x509_cred);
-+ gnutls_global_deinit();
-+ }
- #endif
-
- if ( conn_fd_in != conn_fd_out) close(conn_fd_in);
-@@ -237,9 +330,9 @@ int conn_close()
-
- static inline int READ(void *buf, size_t count)
- {
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- if (csync_conn_usessl)
-- return SSL_read(conn_ssl, buf, count);
-+ return gnutls_record_recv(conn_tls_session, buf, count);
- else
- #endif
- return read(conn_fd_in, buf, count);
-@@ -249,9 +342,9 @@ static inline int WRITE(const void *buf, size_t count)
- {
- static int n, total;
-
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- if (csync_conn_usessl)
-- return SSL_write(conn_ssl, buf, count);
-+ return gnutls_record_send(conn_tls_session, buf, count);
- else
- #endif
- {
-@@ -302,7 +395,7 @@ int conn_raw_read(void *buf, size_t count)
- return 0;
- }
-
--void conn_debug(const char *name, const unsigned char*buf, size_t count)
-+void conn_debug(const char *name, const char*buf, size_t count)
- {
- int i;
-
-@@ -365,9 +458,9 @@ void conn_printf(const char *fmt, ...)
- conn_write(buffer, size);
- }
-
--int conn_gets(char *s, int size)
-+size_t conn_gets(char *s, size_t size)
- {
-- int i=0;
-+ size_t i=0;
-
- while (i<size-1) {
- int rc = conn_raw_read(s+i, 1);
-diff --git a/contrib/csync2id.pl b/contrib/csync2id.pl
-new file mode 100644
-index 0000000..1b3a5fa
---- /dev/null
-+++ b/contrib/csync2id.pl
-@@ -0,0 +1,359 @@
-+#!/usr/bin/perl -w
-+# Copyright: telegraaf (NL)
-+# Author: ard@telegraafnet.nl
-+# License: GPL v2 or higher
-+use strict;
-+use Linux::Inotify2;
-+use Data::Dumper;
-+use File::Find;
-+use POSIX qw(uname :sys_wait_h);
-+use Sys::Syslog;
-+use Net::Server::Daemonize qw(daemonize);
-+use IO::Select;
-+use Fcntl;
-+
-+
-+my $program="csync2id";
-+my $daemonize=1;
-+my $usesyslog=1;
-+my $pidfile='/var/run/csync2id.pid';
-+my $pidfileboot='/var/run/csync2id.boot.pid';
-+
-+################################################################################
-+# Config items
-+# Overridden by /etc/csync2id.cfg
-+# Normal config in /etc/csync2id.cfg:
-+#
-+# @::dirs=qw( /data1 /data2 );
-+# 1;
-+#
-+# csyncdirhint: preferred hint command for directories (a single directory name
-+# will be added)
-+# csyncfilehint: preferred hint command for files (at most $x filenames will be appended)
-+# csynccheck: preferred command scheduled right after the hint, or after a timeout
-+# csyncupdate: preferred command scheduled right after the check
-+# debug: log debug lines
-+# statsdir: file to log the number of watched directories
-+# statchanges: file to log the number of file change events
-+# statsretry: file to log the number of retries needed so far for the hint
-+# dirs: an array of directories which need to be watched recursively
-+################################################################################
-+
-+$::csynchintmaxargs=20;
-+@::csyncdirhint=("/usr/sbin/csync2", "-B","-A","-rh");
-+@::csyncfilehint=("/usr/sbin/csync2", "-B","-A","-h");
-+@::csynccheck=("/usr/sbin/csync2", "-B","-A","-c");
-+@::csyncupdate=("/usr/sbin/csync2", "-B","-A","-u");
-+$::debug=3;
-+$::statsdir="/dev/shm/csyncstats/dirs";
-+$::statschanges="/dev/shm/csyncstats/changes";
-+$::statsretry="/dev/shm/csyncstats/retry";
-+@::dirs=();
-+require "/etc/csync2id.cfg";
-+
-+$daemonize && daemonize(0,0,$pidfileboot);
-+$usesyslog && openlog("$program",'pid','daemon');
-+
-+use constant { LOGERR => 0, LOGWARN => 1, LOGINFO =>2, LOGDEBUG=>3,LOGSLOTS=>256 };
-+my %prios=( 0 => 'err', 1 => 'warning', 2 => 'info', default => 'debug' );
-+sub logger {
-+ my($level,@args)=@_;
-+ my ($prio)=$prios{$level}||$prios{'default'}; # :$prios{'default'};
-+ if($usesyslog) {
-+ syslog($prio,@args) if (($level<= LOGDEBUG && $level<=$::debug)||($::debug>=LOGDEBUG && $level&$::debug))
-+ } else {
-+ print "LOG: $prio ";
-+ print(@args);
-+ print "\n";
-+ }
-+}
-+
-+logger(LOGDEBUG,Dumper(\@::dirs));
-+
-+
-+my $inotify = new Linux::Inotify2 or ( logger(LOGERR, "Unable to create new inotify object: $!") && die("inotify") );
-+
-+# For stats
-+my $globaldirs=0;
-+my $globalevents=0;
-+my $globalhintretry=0;
-+
-+sub logstatsline {
-+ my ($file,$line)=@_;
-+# open STATS,"> $file";
-+# print STATS $line;
-+# close STATS;
-+}
-+
-+
-+#package Runner;
-+################################################################################
-+# Process runner
-+# Runs processes and keep status
-+# API:
-+# runstatus: current status of a runslot (running/idle)
-+# exitstatus: last status of an exec
-+# slotrun: forkexec a new command with a callback when it's finished for a specific slot
-+# Helpers:
-+# reaper is the SIGCHLD handler
-+# checkchildren should be called after syscalls which exited with E_INTR, and
-+# calls the specific callbacks.
-+################################################################################
-+use constant { RUN_IDLE => 0, RUN_RUNNING => 1, RUN_REAPED =>2 };
-+my %slotstatus;
-+my %slotexitstatus;
-+my %slotcommandline;
-+my %slotcallback;
-+my %slotpid2slot;
-+my %slotstarttime;
-+
-+# pid queue for reaper
-+# Every pid (key) contains a waitforpid exit status as value.
-+my %slotpidreaped;
-+
-+sub runstatus {
-+ my ($slot)=@_;
-+ return($slotstatus{$slot}) if exists($slotstatus{$slot});
-+ return RUN_IDLE;
-+}
-+sub slotrun {
-+ my ($slot,$callback,$commandline)=(@_);
-+ $SIG{CHLD} = \&reaper;
-+ if(runstatus($slot)!=RUN_IDLE) {
-+ logger(LOGDEBUG,"SlotRun: Asked to run for $slot, but $slot != RUN_IDLE");
-+ return -1;
-+ }
-+ $slotcommandline{$slot}=$commandline;
-+ $slotcallback{$slot}=$callback;
-+ $slotstatus{$slot}=RUN_RUNNING;
-+ $slotstarttime{$slot}=time();
-+ my $pid=fork();
-+ if(!$pid) {
-+ # We know that exec should not return. Now tell the perl interpreter that we know.
-+ {
-+ exec(@$commandline);
-+ }
-+ logger(LOGWARN,"SlotRun: $slot Exec failed: ".join(' ','>', @$commandline,'<'));
-+ # If we can't exec, we don't really know why, and we don't want to go busy fork execing
-+ # Give a fork exec grace by waiting
-+ sleep 1;
-+ exit 1;
-+ }
-+ logger(LOGDEBUG,"SlotRun: $slot # ".$pid.": run".join(' ','>', @$commandline,'<'));
-+ $slotpid2slot{$pid}=$slot;
-+}
-+sub exitstatus {
-+ my ($slot)=@_;
-+ return($slotexitstatus{$slot}) if exists($slotexitstatus{$slot});
-+ return -1;
-+}
-+sub reaper {
-+}
-+
-+sub checkchildren {
-+ if($::debug==LOGSLOTS) {
-+ while(my ($slot,$status) = each %slotstatus) {
-+ logger(LOGDEBUG,"SlotRun: $slot status $status time: ".($status?(time()-$slotstarttime{$slot}):'x'));
-+ };
-+ }
-+ while() {
-+ my ($pid)=waitpid(-1,&WNOHANG);
-+ if($pid<=0) {
-+ last;
-+ }
-+ my $status=$?;
-+ if (WIFEXITED($status)||WIFSIGNALED($status) && exists($slotpid2slot{$pid})) {
-+ my $slot=$slotpid2slot{$pid};
-+ delete($slotpid2slot{$pid});
-+ $slotstatus{$slot}=RUN_IDLE;
-+ $slotexitstatus{$slot}=$status;
-+ logger(LOGDEBUG, "SlotRun: $slot $pid exited with $status == ".WEXITSTATUS($status).".\n");
-+ # Callback determines if we run again or not.
-+ $slotcallback{$slot}->($slot,$slotexitstatus{$slot},$slotcommandline{$slot});
-+ } else {
-+ logger(LOGDEBUG, "SlotRun: Unknown process $pid change state.\n");
-+ }
-+ }
-+}
-+
-+
-+
-+
-+
-+
-+################################################################################
-+# CSYNC RUNNERS
-+# groups queued hints into single csync commands
-+# run csync update and check commands
-+################################################################################
-+
-+# use constant { CSYNCHINT => 0 , CSYNCCHECK=>1 , CSYNCUPDATE=>2 };
-+my @hintfifo;
-+
-+sub updateCallback {
-+ my ($slot,$exitstatus,$command)=@_;
-+ if($exitstatus) {
-+ logger(LOGWARN,"Updater got ".$exitstatus.", NOT retrying run:".join(' ','>',@$command,'<'));
-+ }
-+}
-+sub runupdater {
-+ if(runstatus('csupdate') == RUN_IDLE) {
-+ slotrun('csupdate',\&updateCallback,\@::csyncupdate);
-+ }
-+}
-+
-+sub checkerCallback {
-+ my ($slot,$exitstatus,$command)=@_;
-+ if($exitstatus) {
-+ logger(LOGWARN,"Checker got ".$exitstatus.", NOT retrying run:".join(' ','>',@$command,'<'));
-+ }
-+ runupdater();
-+}
-+sub runchecker {
-+ if(runstatus('cscheck') == RUN_IDLE) {
-+ slotrun('cscheck',\&checkerCallback,\@::csynccheck);
-+ }
-+}
-+sub hinterCallback {
-+ my ($slot,$exitstatus,$command)=@_;
-+ if($exitstatus) {
-+ logger(LOGWARN,"Hinter got ".$exitstatus.", retrying run:".join(' ','>',@$command,'<'));
-+ $globalhintretry++;
-+ logstatsline($::statsretry,$globalhintretry);
-+ slotrun($slot,\&hinterCallback,$command);
-+ } else {
-+ runchecker();
-+ }
-+}
-+sub givehints {
-+ if(runstatus('cshint') == RUN_IDLE && @hintfifo) {
-+ # PREPARE JOB
-+ # Directories should be treated with care, one at a time.
-+ my @hintcommand;
-+ if($hintfifo[0]->{'recurse'}) {
-+ my $filename=$hintfifo[0]->{'filename'};
-+ @hintcommand=(@::csyncdirhint,$filename);
-+ shift(@hintfifo) while (@hintfifo && $filename eq $hintfifo[0]->{'filename'} );
-+ } else {
-+ # Files can be bulked, until the next directory
-+ my $nrargs=0;
-+ @hintcommand=(@::csyncfilehint);
-+ while($nrargs < $::csynchintmaxargs && @hintfifo && !$hintfifo[0]->{'recurse'}) {
-+ my $filename=$hintfifo[0]->{'filename'};
-+ push(@hintcommand,$filename);
-+ shift(@hintfifo) while (@hintfifo && $filename eq $hintfifo[0]->{'filename'} );
-+ $nrargs++;
-+ }
-+ }
-+ slotrun('cshint',\&hinterCallback,\@hintcommand);
-+ }
-+}
-+
-+################################################################################
-+# Subtree parser
-+# Adds subtrees to an existing watch
-+# globals: $globaldirs for stats.
-+# Logs to logger
-+################################################################################
-+sub watchtree {
-+ my ($inotifier,$tree,$inotifyflags) = @_;
-+ $inotifier->watch ($tree, $inotifyflags);
-+ $globaldirs++;
-+ find(
-+ sub {
-+ if(! m/^\.\.?$/) {
-+ my ($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_) ;
-+ if(-d _ ) {
-+ if ($nlink==2) {
-+ $File::Find::prune = 1;
-+ }
-+ $inotifier->watch ($File::Find::dir.'/'.$_, $inotifyflags) or die("WatchTree: watch creation failed (maybe increase the number of watches?)");
-+ $globaldirs++;
-+ logger(LOGDEBUG,"WatchTree: directory ". $globaldirs." ".$File::Find::dir.'/'.$_);
-+ }
-+ }
-+ },
-+ $tree
-+ );
-+ logstatsline($::statsdir,$globaldirs);
-+}
-+
-+
-+################################################################################
-+# Main
-+#
-+logger(LOGINFO, 'Main: Starting $Id: csync2id.pl,v 1.18 2008/12/24 15:34:19 ard Exp $');
-+# Start watching the directories
-+logger(LOGINFO, "Main: traversing directories");
-+eval {
-+ watchtree($inotify,$_,IN_MOVE|IN_DELETE|IN_CLOSE_WRITE|IN_ATTRIB|IN_CREATE) foreach(@::dirs)
-+};
-+if($@) {
-+ logger(LOGERR,"Main: $@");
-+ exit(2);
-+}
-+logger(LOGINFO,"Main: ready for events");
-+
-+# Kill other daemon because we are ready
-+if($daemonize) {
-+ if ( -e $pidfile ) {
-+ my $thepid;
-+ @ARGV=($pidfile);
-+ $thepid=<>;
-+ logger(LOGINFO, "Main: about to kill previous incarnation $thepid");
-+ kill(15,$thepid);
-+ sleep 0.5;
-+ }
-+ rename($pidfileboot,$pidfile);
-+}
-+
-+# Main loop
-+$inotify->blocking(O_NONBLOCK);
-+my $timeout=20;
-+while () {
-+ #my ($rhset,$dummy,$dummy,$timeleft)=IO::Select->select($selectset, undef, undef, 60);
-+ my $nfound;
-+ my $rin='';
-+ vec($rin,$inotify->fileno,1)=1;
-+ ($nfound,$timeout)=select($rin, undef, undef, $timeout);
-+ logger(LOGDEBUG,"Main: nrfds: $nfound timeleft: $timeout\n");
-+ if(!$timeout) {
-+ $timeout=20;
-+ logger(LOGDEBUG, "Main: timeout->check and update");
-+ runchecker();
-+ runupdater();
-+ #
-+ }
-+ if($nfound>0) {
-+ my @events = $inotify->read;
-+ unless (@events > 0) {
-+ logger(LOGWARN,"Main: Zero events, must be a something weird");
-+ }
-+ foreach(@events) {
-+ if($_->IN_Q_OVERFLOW) {
-+ logger(LOGERR,"Main: FATAL:inotify queue overflow: csync2id was to slow to handle events");
-+ }
-+ if( $_->IN_ISDIR) {
-+ my $recurse=0;
-+ # We want to recurse only for new, renamed or deleted directories
-+ $recurse=$_->IN_DELETE||$_->IN_CREATE||$_->IN_MOVED_TO||$_->IN_MOVED_FROM;
-+ eval watchtree($inotify,$_->fullname,IN_MOVE|IN_DELETE|IN_CLOSE_WRITE|IN_ATTRIB|IN_CREATE) if $_->IN_CREATE||$_->IN_MOVED_TO;
-+ if($@) {
-+ logger(LOGINFO,"$@");
-+ exit(3);
-+ }
-+ push(@hintfifo,{ "filename" => $_->fullname , "recurse" => $recurse });
-+ logger(LOGDEBUG,"Main: dir: ".$_->mask." ".$recurse." ".$_->fullname);
-+ } else {
-+ # Accumulate single file events:
-+ next if(@hintfifo && $hintfifo[-1]->{"filename"} eq $_->fullname);
-+ push(@hintfifo,{ "filename" => $_->fullname , "recurse" => 0 });
-+ logger(LOGDEBUG,"Main: file: ".$_->mask," ".$_->fullname);
-+ }
-+ $globalevents++;
-+ }
-+ }
-+ checkchildren();
-+ givehints();
-+ logstatsline($::statschanges,$globalevents);
-+}
-diff --git a/copycheck.sh b/copycheck.sh
-new file mode 100755
-index 0000000..d4fe7d5
---- /dev/null
-+++ b/copycheck.sh
-@@ -0,0 +1,37 @@
-+#!/bin/bash
-+
-+errors=0
-+ignrev="r364"
-+
-+check() {
-+ if ! svn st $1 | grep -q '^?'; then
-+ years="2003 2004 2005 2006 2007 2008"
-+ for y in `svn log $1 | grep '^r[0-9]' | egrep -v "^($ignrev)" | sed 's,.* \(200.\)-.*,\1,' | sort -u`
-+ do
-+ years=`echo $years | sed "s,$y,,"`
-+ if ! grep -q "\*.*Copyright.*$y" $1; then
-+ echo "Missing $y in $1."
-+ (( errors++ ))
-+ fi
-+ done
-+ for y in $years
-+ do
-+ if grep -q "\*.*Copyright.*$y" $1; then
-+ echo "Bogus $y in $1."
-+ (( errors++ ))
-+ fi
-+ done
-+ fi
-+}
-+
-+for f in `grep -rl '\*.*Copyright' . | grep -v '/\.svn/'` ; do
-+ check $f
-+done
-+
-+if [ $errors -ne 0 ]; then
-+ echo "Found $errors errors."
-+ exit 1
-+fi
-+
-+exit 0
-+
-diff --git a/csync2-postgres.sql b/csync2-postgres.sql
-new file mode 100644
-index 0000000..c8975c1
---- /dev/null
-+++ b/csync2-postgres.sql
-@@ -0,0 +1,56 @@
-+--
-+-- Table structure for table action
-+--
-+
-+DROP TABLE IF EXISTS action;
-+CREATE TABLE action (
-+ filename varchar(255) DEFAULT NULL,
-+ command text,
-+ logfile text,
-+ UNIQUE (filename,command)
-+);
-+
-+--
-+-- Table structure for table dirty
-+--
-+
-+DROP TABLE IF EXISTS dirty;
-+CREATE TABLE dirty (
-+ filename varchar(200) DEFAULT NULL,
-+ forced int DEFAULT NULL,
-+ myname varchar(100) DEFAULT NULL,
-+ peername varchar(100) DEFAULT NULL,
-+ UNIQUE (filename,peername)
-+);
-+
-+--
-+-- Table structure for table file
-+--
-+
-+DROP TABLE IF EXISTS file;
-+CREATE TABLE file (
-+ filename varchar(200) DEFAULT NULL,
-+ checktxt varchar(200) DEFAULT NULL,
-+ UNIQUE (filename)
-+);
-+
-+--
-+-- Table structure for table hint
-+--
-+
-+DROP TABLE IF EXISTS hint;
-+CREATE TABLE hint (
-+ filename varchar(255) DEFAULT NULL,
-+ recursive int DEFAULT NULL
-+);
-+
-+--
-+-- Table structure for table x509_cert
-+--
-+
-+DROP TABLE IF EXISTS x509_cert;
-+CREATE TABLE x509_cert (
-+ peername varchar(255) DEFAULT NULL,
-+ certdata varchar(255) DEFAULT NULL,
-+ UNIQUE (peername)
-+);
-diff --git a/csync2.c b/csync2.c
-index 88fefa2..889be05 100644
---- a/csync2.c
-+++ b/csync2.c
-@@ -36,18 +36,25 @@
- #include <errno.h>
- #include <signal.h>
- #include <ctype.h>
-+#include <syslog.h>
-+#include "db_api.h"
-+#include <netdb.h>
-
- #ifdef REAL_DBDIR
- # undef DBDIR
- # define DBDIR REAL_DBDIR
- #endif
-
--static char *file_database = 0;
-+char *csync_database = 0;
-+
-+int db_type = DB_SQLITE3;
-+
- static char *file_config = 0;
- static char *dbdir = DBDIR;
- char *cfgname = "";
-
- char myhostname[256] = "";
-+char *csync_port = "30865";
- char *active_grouplist = 0;
- char *active_peerlist = 0;
-
-@@ -57,11 +64,11 @@ extern FILE *yyin;
- int csync_error_count = 0;
- int csync_debug_level = 0;
- FILE *csync_debug_out = 0;
-+int csync_syslog = 0;
-
- int csync_server_child_pid = 0;
- int csync_timestamps = 0;
- int csync_new_force = 0;
--int csync_port = 30865;
-
- int csync_dump_dir_fd = -1;
-
-@@ -93,6 +100,11 @@ void help(char *cmd)
- PACKAGE_STRING " - cluster synchronization tool, 2nd generation\n"
- "LINBIT Information Technologies GmbH <http://www.linbit.com>\n"
- "Copyright (C) 2004, 2005 Clifford Wolf <clifford@clifford.at>\n"
-+"Copyright (C) 2010 Dennis Schafroth <dennis@schafroth.com>\n"
-+"Copyright (C) 2010 Johannes Thoma <johannes.thoma@gmx.at>\n"
-+"\n"
-+"Version: " CSYNC2_VERSION "\n"
-+"\n"
- "This program is free software under the terms of the GNU GPL.\n"
- "\n"
- "Usage: %s [-v..] [-C config-name] \\\n"
-@@ -103,7 +115,7 @@ PACKAGE_STRING " - cluster synchronization tool, 2nd generation\n"
- " -c [-r] file.. Check files and maybe add to dirty db\n"
- " -u [-d] [-r] file.. Updates files if listed in dirty db\n"
- " -o [-r] file.. Create list of files in compare-mode\n"
--" -f [-r] file.. Force this file in sync (resolve conflict)\n"
-+" -f [-r] file.. Force files to win next conflict resolution\n"
- " -m file.. Mark files in database as dirty\n"
- "\n"
- "Simple mode:\n"
-@@ -161,11 +173,11 @@ PACKAGE_STRING " - cluster synchronization tool, 2nd generation\n"
- " -U Don't mark all other peers as dirty when doing a -TI run.\n"
- "\n"
- " -G Group1,Group2,Group3,...\n"
--" Only use this groups from config-file.\n"
-+" Only use these groups from config-file.\n"
- "\n"
- " -P peer1,peer1,...\n"
--" Only update this peers (still mark all as dirty).\n"
--" Only show files for this peers in -o (compare) mode.\n"
-+" Only update these peers (still mark all as dirty).\n"
-+" Only show files for these peers in -o (compare) mode.\n"
- "\n"
- " -F Add new entries to dirty database with force flag set.\n"
- "\n"
-@@ -178,6 +190,15 @@ PACKAGE_STRING " - cluster synchronization tool, 2nd generation\n"
- " found to the specified file descriptor (when doing a -c run).\n"
- " The directory names in this output are zero-terminated.\n"
- "\n"
-+"Database switches:\n"
-+"\n"
-+" -D database-dir\n"
-+" Use sqlite database in database dir (default: /var/lib/csync2)\n"
-+"\n"
-+" -a mysql-url\n"
-+" Use mysql database in URL:\n"
-+" mysql://[<user>:<password>@]<hostname>/<database>\n"
-+"\n"
- "Creating key file:\n"
- " %s -k filename\n"
- "\n"
-@@ -190,69 +211,124 @@ PACKAGE_STRING " - cluster synchronization tool, 2nd generation\n"
- int create_keyfile(const char *filename)
- {
- int fd = open(filename, O_WRONLY|O_CREAT|O_EXCL, 0600);
-- int rand = open("/dev/random", O_RDONLY);
-+ int rand = open("/dev/urandom", O_RDONLY);
- char matrix[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._";
- unsigned char n;
- int i;
--
-+ int rc;
- assert(sizeof(matrix) == 65);
- if ( fd == -1 ) {
- fprintf(stderr, "Can't create key file: %s\n", strerror(errno));
- return 1;
- }
- if ( rand == -1 ) {
-- fprintf(stderr, "Can't open /dev/random: %s\n", strerror(errno));
-+ fprintf(stderr, "Can't open /dev/urandom: %s\n", strerror(errno));
- return 1;
- }
- for (i=0; i<64; i++) {
-- read(rand, &n, 1);
-- write(fd, matrix+(n&63), 1);
-+ rc = read(rand, &n, 1);
-+ rc = write(fd, matrix+(n&63), 1);
- }
-- write(fd, "\n", 1);
-+ rc = write(fd, "\n", 1);
- close(rand);
- close(fd);
- return 0;
- }
-
--static int csync_server_loop(int single_connect)
-+static int csync_server_bind(void)
- {
- struct linger sl = { 1, 5 };
-- struct sockaddr_in addr;
-- int on = 1;
-+ struct addrinfo hints;
-+ struct addrinfo *result, *rp;
-+ int save_errno;
-+ int sfd, s, on = 1;
-+ memset(&hints, 0, sizeof(struct addrinfo));
-+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
-+ hints.ai_socktype = SOCK_STREAM;
-+ hints.ai_flags = AI_PASSIVE;
-+
-+ s = getaddrinfo(NULL, csync_port, &hints, &result);
-+ if (s != 0) {
-+ csync_debug(1, "Cannot prepare local socket, getaddrinfo: %s\n", gai_strerror(s));
-+ return -1;
-+ }
-
-- int listenfd = socket(AF_INET, SOCK_STREAM, 0);
-- if (listenfd < 0) goto error;
-+ /* getaddrinfo() returns a list of address structures.
-+ Try each address until we successfully bind(2).
-+ If socket(2) (or bind(2)) fails, we (close the socket
-+ and) try the next address. */
-+
-+ for (rp = result; rp != NULL; rp = rp->ai_next) {
-+ sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
-+ if (sfd == -1)
-+ continue;
-+
-+ if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t) sizeof(on)) < 0)
-+ goto error;
-+ if (setsockopt(sfd, SOL_SOCKET, SO_LINGER, &sl, (socklen_t) sizeof(sl)) < 0)
-+ goto error;
-+ if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on, (socklen_t) sizeof(on)) < 0)
-+ goto error;
-
-- bzero(&addr, sizeof(addr));
-- addr.sin_family = AF_INET;
-- addr.sin_addr.s_addr = htonl(INADDR_ANY);
-- addr.sin_port = htons(csync_port);
-+ if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
-+ break; /* Success */
-+
-+ close(sfd);
-+ }
-
-- if ( setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, (socklen_t) sizeof(on)) < 0 ) goto error;
-- if ( setsockopt(listenfd, SOL_SOCKET, SO_LINGER, &sl, (socklen_t) sizeof(sl)) < 0 ) goto error;
-- if ( setsockopt(listenfd, IPPROTO_TCP, TCP_NODELAY, &on, (socklen_t) sizeof(on)) < 0 ) goto error;
-+ freeaddrinfo(result); /* No longer needed */
-
-- if ( bind(listenfd, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) goto error;
-- if ( listen(listenfd, 5) < 0 ) goto error;
-+ if (rp == NULL) /* No address succeeded */
-+ return -1;
-+
-+ return sfd;
-+
-+error:
-+ save_errno = errno;
-+ close(sfd);
-+ errno = save_errno;
-+ return -1;
-+}
-
-+static int csync_server_loop(int single_connect)
-+{
-+ union {
-+ struct sockaddr sa;
-+ struct sockaddr_in sa_in;
-+ struct sockaddr_in6 sa_in6;
-+ struct sockaddr_storage ss;
-+ } addr;
-+ int listenfd = csync_server_bind();
-+ if (listenfd < 0) goto error;
-+
-+ if (listen(listenfd, 5) < 0) goto error;
-+
-+ /* we want to "cleanly" shutdown if the connection is lost unexpectedly */
- signal(SIGPIPE, SIG_IGN);
-+ /* server is not interested in its childs, prevent zombies */
- signal(SIGCHLD, SIG_IGN);
-
- printf("Csync2 daemon running. Waiting for connections.\n");
-
- while (1) {
-- int addrlen = sizeof(addr);
-- int conn = accept(listenfd, (struct sockaddr *) &addr, &addrlen);
-+ unsigned addrlen = sizeof(addr);
-+ int conn = accept(listenfd, &addr.sa, &addrlen);
- if (conn < 0) goto error;
-
- fflush(stdout); fflush(stderr);
-
- if (single_connect || !fork()) {
-+ char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
-+ /* need to restore default SIGCHLD handler in the session,
-+ * as we may need to wait on them in action.c */
-+ signal(SIGCHLD, SIG_DFL);
- csync_server_child_pid = getpid();
-- fprintf(stderr, "<%d> New connection from %s:%u.\n",
-- csync_server_child_pid,
-- inet_ntoa(addr.sin_addr),
-- ntohs(addr.sin_port));
-+ if (getnameinfo(&addr.sa, addrlen,
-+ hbuf, sizeof(hbuf), sbuf, sizeof(sbuf),
-+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
-+ goto error;
-+ fprintf(stderr, "<%d> New connection from %s:%s.\n",
-+ csync_server_child_pid, hbuf, sbuf);
- fflush(stderr);
-
- dup2(conn, 0);
-@@ -293,8 +369,13 @@ int main(int argc, char ** argv)
- return 1;
- }
-
-- while ( (opt = getopt(argc, argv, "W:s:Ftp:G:P:C:D:N:HBAIXULSTMRvhcuoimfxrd")) != -1 ) {
-+ while ( (opt = getopt(argc, argv, "a:W:s:Ftp:G:P:C:D:N:HBAIXULlSTMRvhcuoimfxrd")) != -1 ) {
-+
- switch (opt) {
-+ case 'a':
-+ csync_database = optarg;
-+ db_type = DB_MYSQL;
-+ break;
- case 'W':
- csync_dump_dir_fd = atoi(optarg);
- if (write(csync_dump_dir_fd, 0, 0) < 0)
-@@ -314,7 +395,7 @@ int main(int argc, char ** argv)
- csync_timestamps = 1;
- break;
- case 'p':
-- csync_port = atoi(optarg);
-+ csync_port = strdup(optarg);
- break;
- case 'G':
- active_grouplist = optarg;
-@@ -349,6 +430,10 @@ int main(int argc, char ** argv)
- case 'v':
- csync_debug_level++;
- break;
-+ case 'l':
-+ csync_syslog = 1;
-+ openlog("csync2", LOG_ODELAY, LOG_LOCAL0);
-+ break;
- case 'h':
- if ( mode != MODE_NONE ) help(argv[0]);
- mode = MODE_HINT;
-@@ -450,6 +535,13 @@ int main(int argc, char ** argv)
- if ( mode == MODE_NONE )
- help(argv[0]);
-
-+ /* Some inetd connect stderr to stdout. The debug level messages on
-+ * stderr would confuse the csync2 protocol. Log to syslog instead. */
-+ if ( mode == MODE_INETD && csync_debug_level && !csync_syslog ) {
-+ csync_syslog = 1;
-+ openlog("csync2", LOG_ODELAY, LOG_LOCAL0);
-+ }
-+
- if ( *myhostname == 0 ) {
- gethostname(myhostname, 256);
- myhostname[255] = 0;
-@@ -482,7 +574,7 @@ int main(int argc, char ** argv)
- para = cmd ? strtok(0, "\t \r\n") : 0;
-
- if (cmd && !strcasecmp(cmd, "ssl")) {
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- conn_printf("OK (activating_ssl).\n");
- conn_activate_ssl(1);
-
-@@ -503,10 +595,8 @@ int main(int argc, char ** argv)
- if (para)
- cfgname = strdup(url_decode(para));
- }
--
- if ( !*cfgname ) {
-- asprintf(&file_database, "%s/%s.db", dbdir, myhostname);
-- asprintf(&file_config, ETCDIR "/csync2.cfg");
-+ ASPRINTF(&file_config, ETCDIR "/csync2.cfg");
- } else {
- int i;
-
-@@ -518,14 +608,10 @@ int main(int argc, char ** argv)
- return mode != MODE_INETD;
- }
-
-- asprintf(&file_database, "%s/%s_%s.db", dbdir, myhostname, cfgname);
-- asprintf(&file_config, ETCDIR "/csync2_%s.cfg", cfgname);
-+ ASPRINTF(&file_config, ETCDIR "/csync2_%s.cfg", cfgname);
- }
-
-- csync_debug(2, "My hostname is %s.\n", myhostname);
-- csync_debug(2, "Database-File: %s\n", file_database);
- csync_debug(2, "Config-File: %s\n", file_config);
--
- yyin = fopen(file_config, "r");
- if ( !yyin )
- csync_fatal("Can not open config file `%s': %s\n",
-@@ -533,6 +619,12 @@ int main(int argc, char ** argv)
- yyparse();
- fclose(yyin);
-
-+ if (!csync_database)
-+ csync_database = db_default_database(dbdir, myhostname, cfgname);
-+
-+ csync_debug(2, "My hostname is %s.\n", myhostname);
-+ csync_debug(2, "Database-File: %s\n", csync_database);
-+
- {
- const struct csync_group *g;
- for (g=csync_group; g; g=g->next)
-@@ -541,7 +633,7 @@ int main(int argc, char ** argv)
- found_a_group:;
- }
-
-- csync_db_open(file_database);
-+ csync_db_open(csync_database);
-
- for (i=optind; i < argc; i++)
- on_cygwin_lowercase(argv[i]);
-@@ -582,8 +674,8 @@ found_a_group:;
- SQL_BEGIN("Check all hints",
- "SELECT filename, recursive FROM hint")
- {
-- textlist_add(&tl, url_decode(SQL_V[0]),
-- atoi(SQL_V[1]));
-+ textlist_add(&tl, url_decode(SQL_V(0)),
-+ atoi(SQL_V(1)));
- } SQL_END;
-
- for (t = tl; t != 0; t = t->next) {
-@@ -642,51 +734,56 @@ found_a_group:;
- case MODE_MARK:
- for (i=optind; i < argc; i++) {
- char *realname = getrealfn(argv[i]);
-+ char *pfname;
- csync_check_usefullness(realname, recursive);
-- csync_mark(realname, 0, 0);
-+ pfname=strdup(prefixencode(realname));
-+ csync_mark(pfname, 0, 0);
-
- if ( recursive ) {
- char *where_rec = "";
-
- if ( !strcmp(realname, "/") )
-- asprintf(&where_rec, "or 1");
-+ ASPRINTF(&where_rec, "or 1=1");
- else
-- asprintf(&where_rec, "or (filename > '%s/' "
-- "and filename < '%s0')",
-- url_encode(realname), url_encode(realname));
-+ ASPRINTF(&where_rec, "UNION ALL SELECT filename from file where filename > '%s/' "
-+ "and filename < '%s0'",
-+ url_encode(pfname), url_encode(pfname));
-
- SQL_BEGIN("Adding dirty entries recursively",
- "SELECT filename FROM file WHERE filename = '%s' %s",
-- url_encode(realname), where_rec)
-+ url_encode(pfname), where_rec)
- {
-- char *filename = strdup(url_encode(SQL_V[0]));
-+ char *filename = strdup(url_decode(SQL_V(0)));
- csync_mark(filename, 0, 0);
- free(filename);
- } SQL_END;
- }
-+ free(pfname);
- }
- break;
-
- case MODE_FORCE:
- for (i=optind; i < argc; i++) {
- char *realname = getrealfn(argv[i]);
-+ char *pfname = strdup(prefixencode(realname));
- char *where_rec = "";
-
- if ( recursive ) {
- if ( !strcmp(realname, "/") )
-- asprintf(&where_rec, "or 1");
-+ ASPRINTF(&where_rec, "or 1=1");
- else
-- asprintf(&where_rec, "or (filename > '%s/' "
-+ ASPRINTF(&where_rec, "or (filename > '%s/' "
- "and filename < '%s0')",
- url_encode(realname), url_encode(realname));
- }
-
- SQL("Mark file as to be forced",
-- "UPDATE dirty SET force = 1 WHERE filename = '%s' %s",
-+ "UPDATE dirty SET forced = 1 WHERE filename = '%s' %s",
- url_encode(realname), where_rec);
-
- if ( recursive )
- free(where_rec);
-+ free(pfname);
- }
- break;
-
-@@ -695,7 +792,7 @@ found_a_group:;
- SQL_BEGIN("DB Dump - Hint",
- "SELECT recursive, filename FROM hint ORDER BY filename")
- {
-- printf("%s\t%s\n", SQL_V[0], url_decode(SQL_V[1]));
-+ printf("%s\t%s\n", (char*)SQL_V(0), url_decode(SQL_V(1)));
- retval = -1;
- } SQL_END;
- break;
-@@ -705,8 +802,8 @@ found_a_group:;
- SQL_BEGIN("DB Dump - File",
- "SELECT checktxt, filename FROM file ORDER BY filename")
- {
-- if (csync_find_next(0, url_decode(SQL_V[1]))) {
-- printf("%s\t%s\n", url_decode(SQL_V[0]), url_decode(SQL_V[1]));
-+ if (csync_find_next(0, url_decode(SQL_V(1)))) {
-+ printf("%s\t%s\n", url_decode(SQL_V(0)), url_decode(SQL_V(1)));
- retval = -1;
- }
- } SQL_END;
-@@ -717,8 +814,8 @@ found_a_group:;
- SQL_BEGIN("DB Dump - File",
- "SELECT checktxt, filename FROM file ORDER BY filename")
- {
-- if ( csync_match_file_host(url_decode(SQL_V[1]), argv[optind], argv[optind+1], 0) ) {
-- printf("%s\t%s\n", url_decode(SQL_V[0]), url_decode(SQL_V[1]));
-+ if ( csync_match_file_host(url_decode(SQL_V(1)), argv[optind], argv[optind+1], 0) ) {
-+ printf("%s\t%s\n", url_decode(SQL_V(0)), url_decode(SQL_V(1)));
- retval = -1;
- }
- } SQL_END;
-@@ -767,11 +864,11 @@ found_a_group:;
- case MODE_LIST_DIRTY:
- retval = 2;
- SQL_BEGIN("DB Dump - Dirty",
-- "SELECT force, myname, peername, filename FROM dirty ORDER BY filename")
-+ "SELECT forced, myname, peername, filename FROM dirty ORDER BY filename")
- {
-- if (csync_find_next(0, url_decode(SQL_V[3]))) {
-- printf("%s\t%s\t%s\t%s\n", atoi(SQL_V[0]) ? "force" : "chary",
-- url_decode(SQL_V[1]), url_decode(SQL_V[2]), url_decode(SQL_V[3]));
-+ if (csync_find_next(0, url_decode(SQL_V(3)))) {
-+ printf("%s\t%s\t%s\t%s\n", atoi(SQL_V(0)) ? "force" : "chary",
-+ url_decode(SQL_V(1)), url_decode(SQL_V(2)), url_decode(SQL_V(3)));
- retval = -1;
- }
- } SQL_END;
-diff --git a/csync2.cfg b/csync2.cfg
-index 338bb7b..ff9e639 100644
---- a/csync2.cfg
-+++ b/csync2.cfg
-@@ -1,4 +1,3 @@
--
- # Csync2 Example Configuration File
- # ---------------------------------
- #
-@@ -12,6 +11,22 @@
- #
- # key /etc/csync2.key_mygroup;
- #
-+# #
-+# # WARNING:
-+# # You CANNOT use paths containing a symlink
-+# # component in include/exclude options!
-+# #
-+# # Here is a real-life example:
-+# # Suppose you have some 64bit Linux systems
-+# # and /usr/lib/ocf is what you want to keep
-+# # in sync. On 64bit Linux systems, /usr/lib
-+# # is usually a symlink to /usr/lib64.
-+# # This does not work:
-+# # include /usr/lib/ocf;
-+# # But this does work:
-+# # include /usr/lib64/ocf;
-+# #
-+#
- # include /etc/apache;
- # include %homedir%/bob;
- # exclude %homedir%/bob/temp;
-@@ -24,8 +39,12 @@
- # exec "/usr/sbin/apache2ctl graceful";
- # logfile "/var/log/csync2_action.log";
- # do-local;
-+# # you can use do-local-only if the execution
-+# # should be done locally only
-+# # do-local-only;
- # }
- #
-+# # The backup-directory needs to be created first!
- # backup-directory /var/backups/csync2;
- # backup-generations 3;
- #
-@@ -37,4 +56,3 @@
- # on host[12]: /export/users;
- # on *: /home;
- # }
--
-diff --git a/csync2.h b/csync2.h
-index 1306023..d76f880 100644
---- a/csync2.h
-+++ b/csync2.h
-@@ -21,7 +21,11 @@
- #ifndef CSYNC2_H
- #define CSYNC2_H 1
-
-+#define CSYNC2_VERSION "2.0-rc1"
-+
-+#ifndef _GNU_SOURCE
- #define _GNU_SOURCE
-+#endif
-
- #include "config.h"
- #include <stdio.h>
-@@ -31,6 +35,24 @@
- #include <errno.h>
-
-
-+#define DB_SCHEMA_VERSION 0
-+
-+/* asprintf with test for no memory */
-+
-+#define ASPRINTF(s, fmt, ...) do {\
-+ int __ret = asprintf(s, fmt, ##__VA_ARGS__);\
-+ if (__ret < 0) \
-+ csync_fatal("Out of memory in asprintf at %s:%d\n", __FILE__, __LINE__);\
-+} while (0)
-+
-+
-+#define VASPRINTF(s, fmt, args) do {\
-+ int __ret = vasprintf(s, fmt, args);\
-+ if (__ret < 0) \
-+ csync_fatal("Out of memory in vasprintf at %s:%d\n", __FILE__, __LINE__);\
-+} while (0)
-+
-+
- /* action.c */
-
- extern void csync_schedule_commands(const char *filename, int islocal);
-@@ -78,7 +100,7 @@ extern int conn_write(const void *buf, size_t count);
-
- extern void conn_printf(const char *fmt, ...);
- extern int conn_fgets(char *s, int size);
--extern int conn_gets(char *s, int size);
-+extern size_t conn_gets(char *s, size_t size);
-
-
- /* db.c */
-@@ -91,26 +113,56 @@ extern void* csync_db_begin(const char *err, const char *fmt, ...);
- extern int csync_db_next(void *vmx, const char *err,
- int *pN, const char ***pazValue, const char ***pazColName);
- extern void csync_db_fin(void *vmx, const char *err);
-+extern const void * csync_db_colblob(void *stmtx,int col);
-+extern char *db_default_database(char *dbdir, char *myhostname, char *cfg_name);
-+
-
- #define SQL(e, s, ...) csync_db_sql(e, s, ##__VA_ARGS__)
-
-+#if 0
-+#if defined(HAVE_LIBSQLITE)
- #define SQL_BEGIN(e, s, ...) \
- { \
- char *SQL_ERR = e; \
- void *SQL_VM = csync_db_begin(SQL_ERR, s, ##__VA_ARGS__); \
- int SQL_COUNT = 0; \
- while (1) { \
-- const char **SQL_V, **SQL_N; \
-+ const char **dataSQL_V, **dataSQL_N; \
- int SQL_C; \
- if ( !csync_db_next(SQL_VM, SQL_ERR, \
-- &SQL_C, &SQL_V, &SQL_N) ) break; \
-+ &SQL_C, &dataSQL_V, &dataSQL_N) ) break; \
- SQL_COUNT++;
-
-+#define SQL_V(col) \
-+ (dataSQL_V[(col)])
-+#endif
-+#endif
-+
-+// #if defined(HAVE_LIBSQLITE3)
-+
-+#define SQL_BEGIN(e, s, ...) \
-+{ \
-+ char *SQL_ERR = e; \
-+ void *SQL_VM = csync_db_begin(SQL_ERR, s, ##__VA_ARGS__); \
-+ int SQL_COUNT = 0; \
-+\
-+ if (SQL_VM) { \
-+ while (1) { \
-+ const char **dataSQL_V, **dataSQL_N; \
-+ int SQL_C; \
-+ if ( !csync_db_next(SQL_VM, SQL_ERR, \
-+ &SQL_C, &dataSQL_V, &dataSQL_N) ) break; \
-+ SQL_COUNT++;
-+
-+#define SQL_V(col) \
-+ (csync_db_colblob(SQL_VM,(col)))
-+// #endif
- #define SQL_FIN }{
-
- #define SQL_END \
-+ } \
-+ csync_db_fin(SQL_VM, SQL_ERR); \
- } \
-- csync_db_fin(SQL_VM, SQL_ERR); \
- }
-
- extern int db_blocking_mode;
-@@ -150,6 +202,7 @@ extern void csync_remove_old();
- /* daemon.c */
-
- extern void csync_daemon_session();
-+extern int csync_copy_file(int fd_in, int fd_out);
-
-
- /* getrealfn.c */
-@@ -170,6 +223,7 @@ const char *url_decode(const char *in);
-
- /* another ringbuffer here. so use it with care!! */
- const char *prefixsubst(const char *in);
-+const char *prefixencode(const char *filename);
-
-
- /* textlist implementation */
-@@ -233,12 +287,13 @@ struct csync_group_host {
-
- struct csync_group_pattern {
- struct csync_group_pattern *next;
-- int isinclude, iscompare;
-+ int isinclude, iscompare, star_matches_slashes;
- const char *pattern;
- };
-
- struct csync_group_action_pattern {
- struct csync_group_action_pattern *next;
-+ int star_matches_slashes;
- const char *pattern;
- };
-
-@@ -253,6 +308,7 @@ struct csync_group_action {
- struct csync_group_action_command *command;
- const char *logfile;
- int do_local;
-+ int do_local_only;
- };
-
- struct csync_group {
-@@ -301,8 +357,14 @@ extern struct csync_group *csync_group;
- extern struct csync_prefix *csync_prefix;
- extern struct csync_nossl *csync_nossl;
-
-+extern unsigned csync_lock_timeout;
-+extern char *csync_tempdir;
-+
-+extern char *csync_database;
-+
- extern int csync_error_count;
- extern int csync_debug_level;
-+extern int csync_syslog;
- extern FILE *csync_debug_out;
-
- extern long csync_last_printtime;
-@@ -312,9 +374,9 @@ extern int csync_messages_printed;
- extern int csync_server_child_pid;
- extern int csync_timestamps;
- extern int csync_new_force;
--extern int csync_port;
-
- extern char myhostname[];
-+extern char *csync_port;
- extern char *active_grouplist;
- extern char *active_peerlist;
-
-@@ -328,7 +390,7 @@ extern int csync_dump_dir_fd;
-
- extern int csync_compare_mode;
-
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- extern int csync_conn_usessl;
- #endif
-
-diff --git a/csync2.spec b/csync2.spec
-index 17daad6..5d342dc 100644
---- a/csync2.spec
-+++ b/csync2.spec
-@@ -23,15 +23,15 @@
- # norootforbuild
- # neededforbuild openssl openssl-devel
-
--BuildRequires: sqlite-devel sqlite librsync openssl-devel librsync-devel
-+BuildRequires: sqlite-devel sqlite librsync gnutls-devel librsync-devel
-
- Name: csync2
- License: GPL
- Group: System/Monitoring
- Requires: sqlite openssl librsync
- Autoreqprov: on
--Version: 1.34
--Release: 1
-+Version: 2.0
-+Release: 0.1.rc1
- Source0: csync2-%{version}.tar.gz
- URL: http://oss.linbit.com/csync2
- BuildRoot: %{_tmppath}/%{name}-%{version}-build
-@@ -83,6 +83,7 @@ fi
- %defattr(-,root,root)
- %doc ChangeLog README NEWS INSTALL TODO AUTHORS
- %{_sbindir}/csync2
-+%{_sbindir}/csync2-compare
- %{_var}/lib/csync2
- %{_mandir}/man1/csync2.1.gz
- %config(noreplace) %{_sysconfdir}/xinetd.d/csync2
-diff --git a/daemon.c b/daemon.c
-index a6357fa..2c054ed 100644
---- a/daemon.c
-+++ b/daemon.c
-@@ -23,6 +23,7 @@
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
-+#include <arpa/inet.h>
- #include <string.h>
- #include <fnmatch.h>
- #include <stdlib.h>
-@@ -38,6 +39,9 @@
-
- static char *cmd_error;
-
-+int csync_setBackupFileStatus(char *filename, int backupDirLength);
-+
-+
- int csync_unlink(const char *filename, int ign)
- {
- struct stat st;
-@@ -80,8 +84,13 @@ void csync_file_update(const char *filename, const char *peername)
- url_encode(filename));
- } else {
- const char *checktxt = csync_genchecktxt(&st, filename, 0);
-+
-+ SQL("Deleting old record from file db",
-+ "DELETE FROM file WHERE filename = '%s'",
-+ url_encode(filename));
-+
- SQL("Insert record to file db",
-- "insert into file (filename, checktxt) values "
-+ "INSERT INTO file (filename, checktxt) values "
- "('%s', '%s')", url_encode(filename),
- url_encode(checktxt));
- }
-@@ -98,73 +107,164 @@ int csync_file_backup(const char *filename)
- {
- static char error_buffer[1024];
- const struct csync_group *g = NULL;
-+ struct stat buf;
-+ int rc;
- while ( (g=csync_find_next(g, filename)) ) {
-- if (g->backup_directory && g->backup_generations > 0) {
-- int bak_dir_len = strlen(g->backup_directory);
-- int filename_len = strlen(filename);
-- char backup_filename[bak_dir_len + filename_len + 10];
-- char backup_otherfilename[bak_dir_len + filename_len + 10];
-- int fd_in, fd_out, i;
--
-- fd_in = open(filename, O_RDONLY);
-- if (fd_in < 0) return 0;
--
-- memcpy(backup_filename, g->backup_directory, bak_dir_len);
-- for (i=0; i<filename_len; i++)
-- backup_filename[bak_dir_len+i] =
-- filename[i] == '/' ? '_' : filename[i];
-- backup_filename[bak_dir_len] = '/';
-- memcpy(backup_otherfilename, backup_filename,
-- bak_dir_len + filename_len);
--
-- for (i=g->backup_generations-1; i; i--) {
-- snprintf(backup_filename+bak_dir_len+filename_len, 10, ".%d", i-1);
-- snprintf(backup_otherfilename+bak_dir_len+filename_len, 10, ".%d", i);
-- rename(backup_filename, backup_otherfilename);
-- }
--
-- strcpy(backup_filename+bak_dir_len+filename_len, ".0");
-- fd_out = open(backup_filename, O_WRONLY|O_CREAT, 0600);
--
-- if (fd_out < 0) {
-- snprintf(error_buffer, 1024,
-- "Open error while backing up '%s': %s\n",
-- filename, strerror(errno));
-- cmd_error = error_buffer;
-- close(fd_in);
-- return 1;
-- }
--
-- while (1) {
-- char buffer[512];
-- int read_len = read(fd_in, buffer, 512);
-- int write_len = 0;
--
-- if (read_len <= 0)
-- break;
--
-- while (write_len < read_len) {
-- int rc = write(fd_out, buffer+write_len, read_len-write_len);
-- if (rc <= 0) {
-- snprintf(error_buffer, 1024,
-- "Write error while backing up '%s': %s\n",
-- filename, strerror(errno));
-- cmd_error = error_buffer;
-- close(fd_in);
-- close(fd_out);
-- return 1;
-- }
-- write_len += rc;
-- }
-- }
-- close(fd_in);
-- close(fd_out);
-- }
-+ if (g->backup_directory && g->backup_generations > 1) {
-+
-+ int bak_dir_len = strlen(g->backup_directory);
-+ int filename_len = strlen(filename);
-+ char backup_filename[bak_dir_len + filename_len + 10];
-+ char backup_otherfilename[bak_dir_len + filename_len + 10];
-+ int fd_in, fd_out, i;
-+ int lastSlash = 0;
-+ mode_t mode;
-+ csync_debug(1, "backup\n");
-+ // Skip generation of directories
-+ rc = stat(filename, &buf);
-+ if (S_ISDIR(buf.st_mode)) {
-+ csync_debug(1, "directory. Skip generation \n");
-+ return 0;
-+ }
-+
-+ fd_in = open(filename, O_RDONLY);
-+ if (fd_in < 0)
-+ return 0;
-+
-+ memcpy(backup_filename, g->backup_directory, bak_dir_len);
-+ backup_filename[bak_dir_len] = 0;
-+ mode = 0777;
-+
-+
-+ for (i=filename_len; i> 0; i--)
-+ if (filename[i] == '/') {
-+ lastSlash = i;
-+ break;
-+ }
-+
-+ for (i=0; i < filename_len; i++) {
-+ // Create directories in filename
-+ // TODO: Get the mode from the orig. dir
-+ if (filename[i] == '/' && i <= lastSlash) {
-+
-+ backup_filename[bak_dir_len+i] = 0;
-+
-+ csync_debug(1, "mkdir %s \n", backup_filename);
-+
-+ mkdir(backup_filename, mode);
-+ // Dont check the empty string.
-+ if (i!= 0)
-+ csync_setBackupFileStatus(backup_filename, bak_dir_len);
-+
-+ }
-+ backup_filename[bak_dir_len+i] = filename[i];
-+ }
-+
-+ backup_filename[bak_dir_len + filename_len] = 0;
-+ backup_filename[bak_dir_len] = '/';
-+ memcpy(backup_otherfilename, backup_filename,
-+ bak_dir_len + filename_len);
-+
-+ //rc = unlink(
-+ for (i=g->backup_generations-1; i; i--) {
-+
-+ if (i != 1)
-+ snprintf(backup_filename+bak_dir_len+filename_len, 10, ".%d", i-1);
-+ backup_filename[bak_dir_len+filename_len] = '\0';
-+ snprintf(backup_otherfilename+bak_dir_len+filename_len, 10, ".%d", i);
-+
-+ rc = rename(backup_filename, backup_otherfilename);
-+ csync_debug(1, "renaming backup files '%s' to '%s'. rc = %d\n", backup_filename, backup_otherfilename, rc);
-+
-+ }
-+
-+ /* strcpy(backup_filename+bak_dir_len+filename_len, ""); */
-+
-+ fd_out = open(backup_filename, O_WRONLY|O_CREAT, 0600);
-+
-+ if (fd_out < 0) {
-+ snprintf(error_buffer, 1024,
-+ "Open error while backing up '%s': %s\n",
-+ filename, strerror(errno));
-+ cmd_error = error_buffer;
-+ close(fd_in);
-+ return 1;
-+ }
-+
-+ csync_debug(1,"Copying data from %s to backup file %s \n", filename, backup_filename);
-+
-+ rc = csync_copy_file(fd_in, fd_out);
-+ if (rc != 0) {
-+ csync_debug(1, "csync_backup error 2\n");
-+
-+ snprintf(error_buffer, 1024,
-+ "Write error while backing up '%s': %s\n",
-+ filename, strerror(errno));
-+
-+ cmd_error = error_buffer;
-+ // TODO verify file disapeared ?
-+ //
-+ // return 1;
-+ }
-+ csync_setBackupFileStatus(backup_filename, bak_dir_len);
-+ csync_debug(1, "csync_backup loop end\n");
-+ }
- }
--
-+ csync_debug(1, "csync_backup end\n");
- return 0;
- }
-
-+int csync_copy_file(int fd_in, int fd_out)
-+{
-+ char buffer[512];
-+ int read_len = read(fd_in, buffer, 512);
-+
-+ while (read_len > 0) {
-+ int write_len = 0;
-+
-+ while (write_len < read_len) {
-+ int rc = write(fd_out, buffer+write_len, read_len-write_len);
-+ if (rc == -1) {
-+ close(fd_in);
-+ close(fd_out);
-+ //TODO verify return code.
-+ return errno;
-+ }
-+ write_len += rc;
-+ }
-+ read_len = read(fd_in, buffer, 512);
-+ }
-+ close(fd_in);
-+ close(fd_out);
-+ return 0;
-+}
-+
-+/* get the mode from the orig directory.
-+ Looking from the back_dir_len should produce the original dir.
-+*/
-+int csync_setBackupFileStatus(char *filename, int backupDirLength) {
-+
-+ struct stat buf;
-+ int rc = stat((filename + backupDirLength), &buf);
-+ if (rc == 0 ) {
-+ csync_debug(0, "Stating original file %s rc: %d mode: %o", (filename + backupDirLength), rc, buf.st_mode);
-+
-+ rc = chown(filename, buf.st_uid, buf.st_gid);
-+ csync_debug(0, "Changing owner of %s to user %d and group %d, rc= %d \n",
-+ filename, buf.st_uid, buf.st_gid, rc);
-+
-+ rc = chmod(filename, buf.st_mode);
-+ csync_debug(0, "Changing mode of %s to mode %d, rc= %d \n",
-+ filename, buf.st_mode, rc);
-+
-+ }
-+ else {
-+ csync_debug(0, "Error getting mode and owner ship from %s \n", (filename + backupDirLength));
-+ return -1;
-+ }
-+ return 0;
-+};
-+
- struct csync_command {
- char *text;
- int check_perm;
-@@ -210,16 +310,149 @@ struct csync_command cmdtab[] = {
- { 0, 0, 0, 0, 0, 0, 0 }
- };
-
-+typedef union address {
-+ struct sockaddr sa;
-+ struct sockaddr_in sa_in;
-+ struct sockaddr_in6 sa_in6;
-+ struct sockaddr_storage ss;
-+} address_t;
-+
-+const char *csync_inet_ntop(address_t *addr)
-+{
-+ char buf[INET6_ADDRSTRLEN];
-+ sa_family_t af = addr->sa.sa_family;
-+ return inet_ntop(af,
-+ af == AF_INET ? (void*)&addr->sa_in.sin_addr :
-+ af == AF_INET6 ? (void*)&addr->sa_in6.sin6_addr : NULL,
-+ buf, sizeof(buf));
-+}
-+
-+/*
-+ * Loops (to cater for multihomed peers) through the address list returned by
-+ * gethostbyname(), returns 1 if any match with the address obtained from
-+ * getpeername() during session startup.
-+ * Otherwise returns 0 (-> identification failed).
-+ *
-+ * TODO switch to a getnameinfo in conn_open.
-+ * TODO add a "pre-authenticated" pipe mode for use over ssh */
-+int verify_peername(const char *name, address_t *peeraddr)
-+{
-+ sa_family_t af = peeraddr->sa.sa_family;
-+ struct addrinfo hints;
-+ struct addrinfo *result, *rp;
-+ int try_mapped_ipv4;
-+ int s;
-+
-+ /* Obtain address(es) matching host */
-+ memset(&hints, 0, sizeof(struct addrinfo));
-+ hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
-+ hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
-+
-+ s = getaddrinfo(name, NULL, &hints, &result);
-+ if (s != 0) {
-+ csync_debug(1, "getaddrinfo: %s\n", gai_strerror(s));
-+ return 0;
-+ }
-+
-+ try_mapped_ipv4 =
-+ af == AF_INET6 &&
-+ !memcmp(&peeraddr->sa_in6.sin6_addr,
-+ "\0\0\0\0" "\0\0\0\0" "\0\0\xff\xff", 12);
-+
-+ /* getaddrinfo() returns a list of address structures.
-+ * Try each address. */
-+
-+ for (rp = result; rp != NULL; rp = rp->ai_next) {
-+ /* both IPv4 */
-+ if (af == AF_INET && rp->ai_family == AF_INET &&
-+ !memcmp(&((struct sockaddr_in*)rp->ai_addr)->sin_addr,
-+ &peeraddr->sa_in.sin_addr, sizeof(struct in_addr)))
-+ break;
-+ /* both IPv6 */
-+ if (af == AF_INET6 && rp->ai_family == AF_INET6 &&
-+ !memcmp(&((struct sockaddr_in6*)rp->ai_addr)->sin6_addr,
-+ &peeraddr->sa_in6.sin6_addr, sizeof(struct in6_addr)))
-+ break;
-+ /* peeraddr IPv6, but actually ::ffff:I.P.v.4,
-+ * and forward lookup returned IPv4 only */
-+ if (af == AF_INET6 && rp->ai_family == AF_INET &&
-+ try_mapped_ipv4 &&
-+ !memcmp(&((struct sockaddr_in*)rp->ai_addr)->sin_addr,
-+ (unsigned char*)&peeraddr->sa_in6.sin6_addr + 12,
-+ sizeof(struct in_addr)))
-+ break;
-+ }
-+ freeaddrinfo(result);
-+ if (rp != NULL) /* memcmp found a match */
-+ return conn_check_peer_cert(name, 0);
-+ return 0;
-+}
-+
-+/* Why do all this fuzz, and not simply --assume-authenticated?
-+ * To limit the impact of an accidental misconfiguration.
-+ */
-+void set_peername_from_env(address_t *p, const char *env)
-+{
-+ struct addrinfo hints = {
-+ .ai_family = AF_UNSPEC,
-+ .ai_socktype = SOCK_STREAM,
-+ .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV,
-+ };
-+ struct addrinfo *result;
-+ char *c;
-+ int s;
-+
-+ char *val = getenv(env);
-+ csync_debug(3, "getenv(%s): >>%s<<\n", env, val ?: "");
-+ if (!val)
-+ return;
-+ val = strdup(val);
-+ if (!val)
-+ return;
-+
-+ c = strchr(val, ' ');
-+ if (!c)
-+ return;
-+ *c = '\0';
-+
-+ s = getaddrinfo(val, NULL, &hints, &result);
-+ if (s != 0) {
-+ csync_debug(1, "getaddrinfo: %s\n", gai_strerror(s));
-+ return;
-+ }
-+
-+ /* getaddrinfo() may return a list of address structures.
-+ * Use the first one. */
-+ if (result)
-+ memcpy(p, result->ai_addr, result->ai_addrlen);
-+ freeaddrinfo(result);
-+}
-+
- void csync_daemon_session()
- {
-- struct sockaddr_in peername;
-- struct hostent *hp;
-- int peerlen = sizeof(struct sockaddr_in);
-+ struct stat sb;
-+ address_t peername = { .sa.sa_family = AF_UNSPEC, };
-+ socklen_t peerlen = sizeof(peername);
- char line[4096], *peer=0, *tag[32];
- int i;
-
-- if ( getpeername(0, (struct sockaddr*)&peername, &peerlen) == -1 )
-- csync_fatal("Can't run getpeername on fd 0: %s", strerror(errno));
-+
-+ if (fstat(0, &sb))
-+ csync_fatal("Can't run fstat on fd 0: %s", strerror(errno));
-+
-+ switch (sb.st_mode & S_IFMT) {
-+ case S_IFSOCK:
-+ if ( getpeername(0, &peername.sa, &peerlen) == -1 )
-+ csync_fatal("Can't run getpeername on fd 0: %s", strerror(errno));
-+ break;
-+ case S_IFIFO:
-+ set_peername_from_env(&peername, "SSH_CLIENT");
-+ break;
-+ /* fall through */
-+ default:
-+ csync_fatal("I'm only talking to sockets or pipes! %x\n", sb.st_mode & S_IFMT);
-+ break;
-+ }
-
- while ( conn_gets(line, 4096) ) {
- int cmdnr;
-@@ -246,13 +479,8 @@ void csync_daemon_session()
- cmd_error = 0;
-
- if ( cmdtab[cmdnr].need_ident && !peer ) {
-- union {
-- in_addr_t addr;
-- unsigned char oct[4];
-- } tmp;
-- tmp.addr = peername.sin_addr.s_addr;
-- conn_printf("Dear %d.%d.%d.%d, please identify first.\n",
-- tmp.oct[0], tmp.oct[1], tmp.oct[2], tmp.oct[3]);
-+ conn_printf("Dear %s, please identify first.\n",
-+ csync_inet_ntop(&peername) ?: "stranger");
- goto next_cmd;
- }
-
-@@ -443,8 +671,8 @@ void csync_daemon_session()
- strcmp(tag[2], "-") ? url_encode(tag[2]) : "",
- strcmp(tag[2], "-") ? "'" : "")
- {
-- if ( csync_match_file_host(url_decode(SQL_V[1]), tag[1], peer, (const char **)&tag[3]) )
-- conn_printf("%s\t%s\n", SQL_V[0], SQL_V[1]);
-+ if ( csync_match_file_host(url_decode(SQL_V(1)), tag[1], peer, (const char **)&tag[3]) )
-+ conn_printf("%s\t%s\n", SQL_V(0), SQL_V(1));
- } SQL_END;
- break;
-
-@@ -454,18 +682,18 @@ void csync_daemon_session()
- csync_debug_level = atoi(tag[1]);
- break;
- case A_HELLO:
-- if (peer) free(peer);
-- hp = gethostbyname(tag[1]);
-- if ( hp != 0 && peername.sin_family == hp->h_addrtype &&
-- !memcmp(hp->h_addr, &peername.sin_addr, hp->h_length) &&
-- conn_check_peer_cert(tag[1], 0)) {
-+ if (peer) {
-+ free(peer);
-+ peer = NULL;
-+ }
-+ if (verify_peername(tag[1], &peername)) {
- peer = strdup(tag[1]);
- } else {
-- peer = 0;
-+ peer = NULL;
- cmd_error = "Identification failed!";
- break;
- }
--#ifdef HAVE_LIBGNUTLS_OPENSSL
-+#ifdef HAVE_LIBGNUTLS
- if (!csync_conn_usessl) {
- struct csync_nossl *t;
- for (t = csync_nossl; t; t=t->next) {
-@@ -507,7 +735,7 @@ found_asactive: ;
- break;
- case A_BYE:
- for (i=0; i<32; i++)
-- tag[i] = strdup(url_decode(tag[i]));
-+ free(tag[i]);
- conn_printf("OK (cu_later).\n");
- return;
- }
-@@ -529,7 +757,6 @@ abort_cmd:
-
- next_cmd:
- for (i=0; i<32; i++)
-- tag[i] = strdup(url_decode(tag[i]));
-+ free(tag[i]);
- }
- }
--
-diff --git a/db.c b/db.c
-index 1cd6953..68848b3 100644
---- a/db.c
-+++ b/db.c
-@@ -19,13 +19,13 @@
- */
-
- #include "csync2.h"
--#include <sqlite.h>
- #include <stdio.h>
- #include <stdarg.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <signal.h>
- #include <time.h>
-+#include "db_api.h"
-
- #define DEADLOCK_MESSAGE \
- "Database backend is exceedingly busy => Terminating (requesting retry).\n"
-@@ -33,14 +33,16 @@
- int db_blocking_mode = 1;
- int db_sync_mode = 1;
-
--static sqlite *db = 0;
-+extern int db_type;
-+static db_conn_p db = 0;
-+// TODO make configurable
-+int wait = 1;
-
- static int get_dblock_timeout()
- {
-- return getpid() % 7 + 12;
-+ return getpid() % 7 + csync_lock_timeout;
- }
-
--
- static int tqueries_counter = -50;
- static time_t transaction_begin = 0;
- static time_t last_wait_cycle = 0;
-@@ -58,7 +60,7 @@ void csync_db_alarmhandler(int signum)
- begin_commit_recursion++;
-
- csync_debug(2, "Database idle in transaction. Forcing COMMIT.\n");
-- SQL("COMMIT TRANSACTION", "COMMIT TRANSACTION");
-+ SQL("COMMIT ", "COMMIT ");
- tqueries_counter = -10;
-
- begin_commit_recursion--;
-@@ -82,7 +84,7 @@ void csync_db_maybegin()
- transaction_begin = time(0);
- if (!last_wait_cycle)
- last_wait_cycle = transaction_begin;
-- SQL("BEGIN TRANSACTION", "BEGIN TRANSACTION");
-+ SQL("BEGIN ", "BEGIN ");
- }
-
- begin_commit_recursion--;
-@@ -103,9 +105,11 @@ void csync_db_maycommit()
- now = time(0);
-
- if ((now - last_wait_cycle) > 10) {
-- SQL("COMMIT TRANSACTION", "COMMIT TRANSACTION");
-- csync_debug(2, "Waiting 2 secs so others can lock the database (%d - %d)...\n", (int)now, (int)last_wait_cycle);
-- sleep(2);
-+ SQL("COMMIT", "COMMIT ");
-+ if (wait) {
-+ csync_debug(2, "Waiting %d secs so others can lock the database (%d - %d)...\n", wait, (int)now, (int)last_wait_cycle);
-+ sleep(wait);
-+ }
- last_wait_cycle = 0;
- tqueries_counter = -10;
- begin_commit_recursion--;
-@@ -113,7 +117,7 @@ void csync_db_maycommit()
- }
-
- if ((tqueries_counter > 1000) || ((now - transaction_begin) > 3)) {
-- SQL("COMMIT TRANSACTION", "COMMIT TRANSACTION");
-+ SQL("COMMIT ", "COMMIT ");
- tqueries_counter = 0;
- begin_commit_recursion--;
- return;
-@@ -128,45 +132,23 @@ void csync_db_maycommit()
-
- void csync_db_open(const char *file)
- {
-- db = sqlite_open(file, 0, 0);
-- if ( db == 0 )
-+ int rc = db_open(file, db_type, &db);
-+ if ( rc != DB_OK )
- csync_fatal("Can't open database: %s\n", file);
-
-+ db_set_logger(db, csync_debug);
-+
- /* ignore errors on table creation */
- in_sql_query++;
-- sqlite_exec(db,
-- "CREATE TABLE file ("
-- " filename, checktxt,"
-- " UNIQUE ( filename ) ON CONFLICT REPLACE"
-- ")",
-- 0, 0, 0);
-- sqlite_exec(db,
-- "CREATE TABLE dirty ("
-- " filename, force, myname, peername,"
-- " UNIQUE ( filename, peername ) ON CONFLICT IGNORE"
-- ")",
-- 0, 0, 0);
-- sqlite_exec(db,
-- "CREATE TABLE hint ("
-- " filename, recursive,"
-- " UNIQUE ( filename, recursive ) ON CONFLICT IGNORE"
-- ")",
-- 0, 0, 0);
-- sqlite_exec(db,
-- "CREATE TABLE action ("
-- " filename, command, logfile,"
-- " UNIQUE ( filename, command ) ON CONFLICT IGNORE"
-- ")",
-- 0, 0, 0);
-- sqlite_exec(db,
-- "CREATE TABLE x509_cert ("
-- " peername, certdata,"
-- " UNIQUE ( peername ) ON CONFLICT IGNORE"
-- ")",
-- 0, 0, 0);
-+
-+ if (db_schema_version(db) < DB_SCHEMA_VERSION)
-+ if (db_upgrade_to_schema(db, DB_SCHEMA_VERSION) != DB_OK)
-+ csync_fatal("Cannot create database tables (version requested = %d): %s\n", DB_SCHEMA_VERSION, db_errmsg(db));
-+
- if (!db_sync_mode)
-- sqlite_exec(db, "PRAGMA synchronous = OFF", 0, 0, 0);
-+ db_exec(db, "PRAGMA synchronous = OFF");
- in_sql_query--;
-+ // return db;
- }
-
- void csync_db_close()
-@@ -175,10 +157,10 @@ void csync_db_close()
-
- begin_commit_recursion++;
- if (tqueries_counter > 0) {
-- SQL("COMMIT TRANSACTION", "COMMIT TRANSACTION");
-+ SQL("COMMIT ", "COMMIT ");
- tqueries_counter = -10;
- }
-- sqlite_close(db);
-+ db_close(db);
- begin_commit_recursion--;
- db = 0;
- }
-@@ -190,7 +172,7 @@ void csync_db_sql(const char *err, const char *fmt, ...)
- int rc, busyc = 0;
-
- va_start(ap, fmt);
-- vasprintf(&sql, fmt, ap);
-+ VASPRINTF(&sql, fmt, ap);
- va_end(ap);
-
- in_sql_query++;
-@@ -199,15 +181,15 @@ void csync_db_sql(const char *err, const char *fmt, ...)
- csync_debug(2, "SQL: %s\n", sql);
-
- while (1) {
-- rc = sqlite_exec(db, sql, 0, 0, 0);
-- if ( rc != SQLITE_BUSY ) break;
-- if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); }
-- csync_debug(2, "Database is busy, sleeping a sec.\n");
-- sleep(1);
-+ rc = db_exec(db, sql);
-+ if ( rc != DB_BUSY ) break;
-+ if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); }
-+ csync_debug(2, "Database is busy, sleeping a sec.\n");
-+ sleep(1);
- }
-
-- if ( rc != SQLITE_OK && err )
-- csync_fatal("Database Error: %s [%d]: %s\n", err, rc, sql);
-+ if ( rc != DB_OK && err )
-+ csync_fatal("Database Error: %s [%d]: %s on executing %s\n", err, rc, db_errmsg(db), sql);
- free(sql);
-
- csync_db_maycommit();
-@@ -216,77 +198,140 @@ void csync_db_sql(const char *err, const char *fmt, ...)
-
- void* csync_db_begin(const char *err, const char *fmt, ...)
- {
-- sqlite_vm *vm;
-+ db_stmt_p stmt = NULL;
- char *sql;
- va_list ap;
- int rc, busyc = 0;
--
-+ char *ppTail;
- va_start(ap, fmt);
-- vasprintf(&sql, fmt, ap);
-+ VASPRINTF(&sql, fmt, ap);
- va_end(ap);
-
- in_sql_query++;
- csync_db_maybegin();
-
- csync_debug(2, "SQL: %s\n", sql);
--
- while (1) {
-- rc = sqlite_compile(db, sql, 0, &vm, 0);
-- if ( rc != SQLITE_BUSY ) break;
-+ rc = db_prepare_stmt(db, sql, &stmt, &ppTail);
-+ if ( rc != DB_BUSY ) break;
- if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); }
- csync_debug(2, "Database is busy, sleeping a sec.\n");
- sleep(1);
- }
-
-- if ( rc != SQLITE_OK && err )
-- csync_fatal("Database Error: %s [%d]: %s\n", err, rc, sql);
-+ if ( rc != DB_OK && err )
-+ csync_fatal("Database Error: %s [%d]: %s on executing %s\n", err, rc, db_errmsg(db), sql);
- free(sql);
-
-- return vm;
-+ return stmt;
-+}
-+
-+const char *csync_db_get_column_text(void *stmt, int column) {
-+ return db_stmt_get_column_text(stmt, column);
-+}
-+
-+int csync_db_get_column_int(void *stmt, int column) {
-+ return db_stmt_get_column_int((db_stmt_p) stmt, column);
- }
-
- int csync_db_next(void *vmx, const char *err,
- int *pN, const char ***pazValue, const char ***pazColName)
- {
-- sqlite_vm *vm = vmx;
-+ db_stmt_p stmt = vmx;
- int rc, busyc = 0;
-
- csync_debug(4, "Trying to fetch a row from the database.\n");
-
- while (1) {
-- rc = sqlite_step(vm, pN, pazValue, pazColName);
-- if ( rc != SQLITE_BUSY ) break;
-- if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); }
-+ rc = db_stmt_next(stmt);
-+ if ( rc != DB_BUSY )
-+ break;
-+ if (busyc++ > get_dblock_timeout()) {
-+ db = 0;
-+ csync_fatal(DEADLOCK_MESSAGE);
-+ }
- csync_debug(2, "Database is busy, sleeping a sec.\n");
- sleep(1);
- }
-
-- if ( rc != SQLITE_OK && rc != SQLITE_ROW &&
-- rc != SQLITE_DONE && err )
-- csync_fatal("Database Error: %s [%d].\n", err, rc);
-+ if ( rc != DB_OK && rc != DB_ROW &&
-+ rc != DB_DONE && err )
-+ csync_fatal("Database Error: %s [%d]: %s\n", err, rc, db_errmsg(db));
-+
-+ return rc == DB_ROW;
-+}
-
-- return rc == SQLITE_ROW;
-+const void * csync_db_colblob(void *stmtx, int col) {
-+ db_stmt_p stmt = stmtx;
-+ const void *ptr = stmt->get_column_blob(stmt, col);
-+ if (stmt->db && stmt->db->logger) {
-+ stmt->db->logger(4, "DB get blob: %s ", (char *) ptr);
-+ }
-+ return ptr;
- }
-
- void csync_db_fin(void *vmx, const char *err)
- {
-- sqlite_vm *vm = vmx;
-+ db_stmt_p stmt = (db_stmt_p) vmx;
- int rc, busyc = 0;
-
-+ if (vmx == NULL)
-+ return;
-+
- csync_debug(2, "SQL Query finished.\n");
-
- while (1) {
-- rc = sqlite_finalize(vm, 0);
-- if ( rc != SQLITE_BUSY ) break;
-- if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); }
-- csync_debug(2, "Database is busy, sleeping a sec.\n");
-- sleep(1);
-+ rc = db_stmt_close(stmt);
-+ if ( rc != DB_BUSY )
-+ break;
-+ if (busyc++ > get_dblock_timeout()) { db = 0; csync_fatal(DEADLOCK_MESSAGE); }
-+ csync_debug(2, "Database is busy, sleeping a sec.\n");
-+ sleep(1);
- }
-
-- if ( rc != SQLITE_OK && err )
-- csync_fatal("Database Error: %s [%d].\n", err, rc);
-+ if ( rc != DB_OK && err )
-+ csync_fatal("Database Error: %s [%d]: %s\n", err, rc, db_errmsg(db));
-
- csync_db_maycommit();
- in_sql_query--;
- }
-
-+#if defined(HAVE_SQLITE)
-+#define DBEXTENSION ".db"
-+#endif
-+#if defined(HAVE_SQLITE3)
-+#define DBEXTENSION ".db3"
-+#endif
-+
-+char *db_default_database(char *dbdir, char *myhostname, char *cfg_name)
-+{
-+ char *db;
-+
-+#if defined(HAVE_SQLITE3)
-+ if (cfg_name[0] != '\0')
-+ ASPRINTF(&db, "sqlite3://%s/%s_%s" DBEXTENSION, dbdir, myhostname, cfgname);
-+ else
-+ ASPRINTF(&db, "sqlite3://%s/%s" DBEXTENSION, dbdir, myhostname);
-+#elif defined(HAVE_SQLITE)
-+ if (cfg_name[0] != '\0')
-+ ASPRINTF(&db, "sqlite2://%s/%s_%s" DBEXTENSION, dbdir, myhostname, cfgname);
-+ else
-+ ASPRINTF(&db, "sqlite2://%s/%s" DBEXTENSION, dbdir, myhostname);
-+#elif defined(HAVE_MYSQL)
-+ if (cfg_name[0] != '\0')
-+ ASPRINTF(&db, "mysql://root@localhost/csync2_%s_%s" DBEXTENSION, myhostname, cfgname);
-+ else
-+ ASPRINTF(&db, "mysql://root@localhost/csync2_%s" DBEXTENSION, myhostname);
-+
-+#elif defined(HAVE_POSTGRES)
-+ if (cfg_name[0] != '\0')
-+ ASPRINTF(&db, "pgsql://root@localhost/csync2_%s_%s" DBEXTENSION, myhostname, cfgname);
-+ else
-+ ASPRINTF(&db, "pgsql://root@localhost/csync2_%s" DBEXTENSION, myhostname);
-+
-+#else
-+#error "No database backend available. Please install either libpg, libmysqlclient or libsqlite, reconfigure and recompile"
-+#endif
-+
-+ return db;
-+}
-diff --git a/db_api.c b/db_api.c
-new file mode 100644
-index 0000000..af5591c
---- /dev/null
-+++ b/db_api.c
-@@ -0,0 +1,186 @@
-+/*
-+ DB API
-+
-+ */
-+
-+#include "csync2.h"
-+#include <stdio.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <signal.h>
-+#include <time.h>
-+#include "db_api.h"
-+
-+#include "db_mysql.h"
-+#include "db_postgres.h"
-+#include "db_sqlite.h"
-+#include "db_sqlite2.h"
-+
-+#define DEADLOCK_MESSAGE \
-+ "Database backend is exceedingly busy => Terminating (requesting retry).\n"
-+
-+int db_sqlite_open(const char *file, db_conn_p *db);
-+int db_mysql_open(const char *file, db_conn_p *db);
-+
-+int db_detect_type(const char **db_str, int type) {
-+ const char *db_types[] = { "mysql://", "sqlite3://", "sqlite2://", "pgsql://", 0 };
-+ int types[] = { DB_MYSQL, DB_SQLITE3, DB_SQLITE2, DB_PGSQL };
-+ int index;
-+ for (index = 0; 1 ; index++) {
-+ if (db_types[index] == 0)
-+ break;
-+ if (!strncmp(*db_str, db_types[index], strlen(db_types[index]))) {
-+ *db_str += strlen(db_types[index]);
-+ return types[index];
-+ }
-+ }
-+ return type;
-+}
-+
-+int db_open(const char *file, int type, db_conn_p *db)
-+{
-+ int rc = DB_ERROR;
-+ const char *db_str;
-+ db_str = file;
-+
-+ type = db_detect_type(&db_str, type);
-+ /* Switch between implementation */
-+ switch (type) {
-+ case DB_SQLITE2:
-+ rc = db_sqlite2_open(db_str, db);
-+
-+ if (rc != DB_OK && db_str[0] != '/')
-+ fprintf(csync_debug_out, "Cannot open database file: %s, maybe you need three slashes (like sqlite:///var/lib/csync2/csync2.db)\n", db_str);
-+ break;
-+ case DB_SQLITE3:
-+ rc = db_sqlite_open(db_str, db);
-+
-+ if (rc != DB_OK && db_str[0] != '/')
-+ fprintf(csync_debug_out, "Cannot open database file: %s, maybe you need three slashes (like sqlite:///var/lib/csync2/csync2.db)\n", db_str);
-+ break;
-+#ifdef HAVE_MYSQL
-+ case DB_MYSQL:
-+ rc = db_mysql_open(db_str, db);
-+ break;
-+#else
-+ case DB_MYSQL:
-+ csync_fatal("No Mysql support configured. Please reconfigure with --enable-mysql (database is %s).\n", file);
-+ rc = DB_ERROR;
-+ break;
-+#endif
-+#ifdef HAVE_POSTGRES
-+ case DB_PGSQL:
-+ rc = db_postgres_open(db_str, db);
-+ break;
-+#else
-+ case DB_PGSQL:
-+ csync_fatal("No Postgres SQL support configured. Please reconfigure with --enable-postgres (database is %s).\n", file);
-+ rc = DB_ERROR;
-+ break;
-+#endif
-+
-+ default:
-+ csync_fatal("Database type not found. Can't open database %s\n", file);
-+ rc = DB_ERROR;
-+ }
-+ if (*db)
-+ (*db)->logger = 0;
-+ return rc;
-+}
-+
-+void db_set_logger(db_conn_p conn, void (*logger)(int lv, const char *fmt, ...)) {
-+ if (conn == NULL)
-+ csync_fatal("No connection in set_logger.\n");
-+
-+ conn->logger = logger;
-+}
-+
-+void db_close(db_conn_p conn)
-+{
-+ if (!conn || !conn->close)
-+ return;
-+ conn->close(conn);
-+}
-+
-+const char *db_errmsg(db_conn_p conn)
-+{
-+ if (conn && conn->errmsg)
-+ return conn->errmsg(conn);
-+
-+ return "(no error message function available)";
-+}
-+
-+int db_exec(db_conn_p conn, const char *sql) {
-+ if (conn && conn->exec)
-+ return conn->exec(conn, sql);
-+
-+ csync_debug(0, "No exec function in db_exec.\n");
-+ return DB_ERROR;
-+}
-+
-+int db_prepare_stmt(db_conn_p conn, const char *sql, db_stmt_p *stmt, char **pptail) {
-+ if (conn && conn->prepare)
-+ return conn->prepare(conn, sql, stmt, pptail);
-+
-+ csync_debug(0, "No prepare function in db_prepare_stmt.\n");
-+ return DB_ERROR;
-+}
-+
-+const char *db_stmt_get_column_text(db_stmt_p stmt, int column) {
-+ if (stmt && stmt->get_column_text)
-+ return stmt->get_column_text(stmt, column);
-+
-+ csync_debug(0, "No stmt in db_stmt_get_column_text / no function.\n");
-+ return NULL;
-+}
-+
-+int db_stmt_get_column_int(db_stmt_p stmt, int column) {
-+ if (stmt && stmt->get_column_int)
-+ return stmt->get_column_int(stmt, column);
-+
-+ csync_debug(0, "No stmt in db_stmt_get_column_int / no function.\n");
-+ return 0;
-+}
-+
-+int db_stmt_next(db_stmt_p stmt)
-+{
-+ if (stmt && stmt->next)
-+ return stmt->next(stmt);
-+
-+ csync_debug(0, "No stmt in db_stmt_next / no function.\n");
-+ return DB_ERROR;
-+}
-+
-+int db_stmt_close(db_stmt_p stmt)
-+{
-+ if (stmt && stmt->close)
-+ return stmt->close(stmt);
-+
-+ csync_debug(0, "No stmt in db_stmt_close / no function.\n");
-+ return DB_ERROR;
-+}
-+
-+int db_schema_version(db_conn_p db)
-+{
-+ int version = -1;
-+
-+ SQL_BEGIN(NULL, /* ignore errors */
-+ "SELECT count(*) from file")
-+ {
-+ version = 0;
-+ } SQL_END;
-+
-+ return version;
-+}
-+
-+
-+int db_upgrade_to_schema(db_conn_p db, int version)
-+{
-+ if (db && db->upgrade_to_schema)
-+ return db->upgrade_to_schema(version);
-+
-+ return DB_ERROR;
-+}
-+
-+
-diff --git a/db_api.h b/db_api.h
-new file mode 100644
-index 0000000..eab627b
---- /dev/null
-+++ b/db_api.h
-@@ -0,0 +1,62 @@
-+
-+#ifndef DB_API_H
-+#define DB_API_H
-+
-+#define DB_SQLITE2 1
-+#define DB_SQLITE3 2
-+#define DB_MYSQL 3
-+#define DB_PGSQL 4
-+
-+#define DB_OK 0
-+#define DB_ERROR 1
-+#define DB_BUSY 2
-+#define DB_NO_CONNECTION 3
-+#define DB_NO_CONNECTION_REAL 4
-+#define DB_ROW 100
-+#define DB_DONE 101
-+
-+typedef struct db_conn_t *db_conn_p;
-+typedef struct db_stmt_t *db_stmt_p;
-+
-+struct db_conn_t {
-+ void *private;
-+ int (*exec) (db_conn_p conn, const char* exec);
-+ int (*prepare)(db_conn_p conn, const char *statement, db_stmt_p *stmt, char **value);
-+ void (*close) (db_conn_p conn);
-+ void (*logger) (int lv, const char *fmt, ...);
-+ const char* (*errmsg) (db_conn_p conn);
-+ int (*upgrade_to_schema) (int version);
-+};
-+
-+struct db_stmt_t {
-+ void *private;
-+ void *private2;
-+ db_conn_p db;
-+ const char * (*get_column_text) (db_stmt_p vmx, int column);
-+ const void* (*get_column_blob) (db_stmt_p vmx, int column);
-+ int (*get_column_int) (db_stmt_p vmx, int column);
-+ int (*next) (db_stmt_p stmt);
-+ int (*close)(db_stmt_p stmt);
-+};
-+
-+//struct db_conn *db_conn;
-+
-+int db_open(const char *file, int type, db_conn_p *db);
-+void db_close(db_conn_p conn);
-+
-+int db_exec(db_conn_p conn, const char* exec);
-+int db_exec2(db_conn_p conn, const char* exec, void (*callback)(void *, int, int), void *data, const char **err);
-+
-+int db_prepare_stmt(db_conn_p conn, const char *statement, db_stmt_p *stmt, char **value);
-+
-+const char * db_stmt_get_column_text(db_stmt_p stmt, int column);
-+int db_stmt_get_column_int(db_stmt_p stmt, int column);
-+int db_stmt_next (db_stmt_p stmt);
-+int db_stmt_close(db_stmt_p stmt);
-+
-+void db_set_logger(db_conn_p conn, void (*logger)(int lv, const char *fmt, ...));
-+int db_schema_version(db_conn_p db);
-+int db_upgrade_to_schema(db_conn_p db, int version);
-+const char *db_errmsg(db_conn_p conn);
-+
-+#endif
-diff --git a/db_mysql.c b/db_mysql.c
-new file mode 100644
-index 0000000..1b6d09e
---- /dev/null
-+++ b/db_mysql.c
-@@ -0,0 +1,408 @@
-+/*
-+ * Copyright (C) 2010 Dennis Schafroth <dennis@schafroth.com>>
-+ * Copyright (C) 2010 Johannes Thoma <johannes.thoma@gmx.at>
-+ *
-+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ */
-+
-+#include "csync2.h"
-+#include <stdio.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <signal.h>
-+#include <time.h>
-+#include <string.h>
-+#include "db_api.h"
-+#include "db_mysql.h"
-+#include "dl.h"
-+
-+#ifdef HAVE_MYSQL
-+#include <mysql/mysql.h>
-+#include <mysql/mysqld_error.h>
-+
-+static struct db_mysql_fns {
-+ MYSQL *(*mysql_init_fn)(MYSQL*);
-+ MYSQL *(*mysql_real_connect_fn)(MYSQL *, const char *, const char *, const char *, const char *, unsigned int, const char *, unsigned long);
-+ int (*mysql_errno_fn)(MYSQL*);
-+ int (*mysql_query_fn)(MYSQL*, const char*);
-+ void (*mysql_close_fn)(MYSQL*);
-+ const char *(*mysql_error_fn)(MYSQL *);
-+ MYSQL_RES *(*mysql_store_result_fn)(MYSQL *);
-+ unsigned int (*mysql_num_fields_fn)(MYSQL_RES *);
-+ MYSQL_ROW (*mysql_fetch_row_fn)(MYSQL_RES *);
-+ void (*mysql_free_result_fn)(MYSQL_RES *);
-+ unsigned int (*mysql_warning_count_fn)(MYSQL *);
-+} f;
-+
-+static void *dl_handle;
-+
-+
-+static void db_mysql_dlopen(void)
-+{
-+ csync_debug(1, "Opening shared library libmysqlclient.so\n");
-+ dl_handle = dlopen("libmysqlclient.so", RTLD_LAZY);
-+ if (dl_handle == NULL) {
-+ csync_fatal("Could not open libmysqlclient.so: %s\nPlease install Mysql client library (libmysqlclient) or use other database (sqlite, postgres)\n", dlerror());
-+ }
-+
-+ csync_debug(1, "Reading symbols from shared library libmysqlclient.so\n");
-+
-+ LOOKUP_SYMBOL(dl_handle, mysql_init);
-+ LOOKUP_SYMBOL(dl_handle, mysql_real_connect);
-+ LOOKUP_SYMBOL(dl_handle, mysql_errno);
-+ LOOKUP_SYMBOL(dl_handle, mysql_query);
-+ LOOKUP_SYMBOL(dl_handle, mysql_close);
-+ LOOKUP_SYMBOL(dl_handle, mysql_error);
-+ LOOKUP_SYMBOL(dl_handle, mysql_store_result);
-+ LOOKUP_SYMBOL(dl_handle, mysql_num_fields);
-+ LOOKUP_SYMBOL(dl_handle, mysql_fetch_row);
-+ LOOKUP_SYMBOL(dl_handle, mysql_free_result);
-+ LOOKUP_SYMBOL(dl_handle, mysql_warning_count);
-+}
-+
-+
-+int db_mysql_parse_url(char *url, char **host, char **user, char **pass, char **database, unsigned int *port, char **unix_socket)
-+{
-+ char *pos = strchr(url, '@');
-+ if (pos) {
-+ // Optional user/passwd
-+ *(pos) = 0;
-+ *(user) = url;
-+ url = pos + 1;
-+ // TODO password
-+ pos = strchr(*user, ':');
-+ if (pos) {
-+ *(pos) = 0;
-+ *(pass) = (pos +1);
-+ }
-+ else
-+ *pass = 0;
-+ }
-+ else {
-+ // No user/pass password
-+ *user = 0;
-+ *pass = 0;
-+ }
-+ *host = url;
-+ pos = strchr(*host, '/');
-+ if (pos) {
-+ // Database
-+ (*pos) = 0;
-+ *database = pos+1;
-+ }
-+ else {
-+ *database = 0;
-+ }
-+ pos = strchr(*host, ':');
-+ if (pos) {
-+ (*pos) = 0;
-+ *port = atoi(pos+1);
-+ }
-+ *unix_socket = 0;
-+ return DB_OK;
-+}
-+
-+#endif
-+
-+int db_mysql_open(const char *file, db_conn_p *conn_p)
-+{
-+#ifdef HAVE_MYSQL
-+ db_mysql_dlopen();
-+
-+ MYSQL *db = f.mysql_init_fn(0);
-+ char *host, *user, *pass, *database, *unix_socket;
-+ unsigned int port;
-+ char *db_url = malloc(strlen(file)+1);
-+ char *create_database_statement;
-+
-+ if (db_url == NULL)
-+ csync_fatal("No memory for db_url\n");
-+
-+ strcpy(db_url, file);
-+ int rc = db_mysql_parse_url(db_url, &host, &user, &pass, &database, &port, &unix_socket);
-+ if (rc != DB_OK) {
-+ return rc;
-+ }
-+
-+ if (f.mysql_real_connect_fn(db, host, user, pass, database, port, unix_socket, 0) == NULL) {
-+ if (f.mysql_errno_fn(db) == ER_BAD_DB_ERROR) {
-+ if (f.mysql_real_connect_fn(db, host, user, pass, NULL, port, unix_socket, 0) != NULL) {
-+ ASPRINTF(&create_database_statement, "create database %s", database);
-+
-+ csync_debug(2, "creating database %s\n", database);
-+ if (f.mysql_query_fn(db, create_database_statement) != 0)
-+ csync_fatal("Cannot create database %s: Error: %s\n", database, f.mysql_error_fn(db));
-+ free(create_database_statement);
-+
-+ f.mysql_close_fn(db);
-+ db = f.mysql_init_fn(0);
-+
-+ if (f.mysql_real_connect_fn(db, host, user, pass, database, port, unix_socket, 0) == NULL)
-+ goto fatal;
-+ }
-+ } else
-+fatal:
-+ csync_fatal("Failed to connect to database: Error: %s\n", f.mysql_error_fn(db));
-+ }
-+
-+ db_conn_p conn = calloc(1, sizeof(*conn));
-+ if (conn == NULL) {
-+ return DB_ERROR;
-+ }
-+ *conn_p = conn;
-+ conn->private = db;
-+ conn->close = db_mysql_close;
-+ conn->exec = db_mysql_exec;
-+ conn->prepare = db_mysql_prepare;
-+ conn->errmsg = db_mysql_errmsg;
-+ conn->upgrade_to_schema = db_mysql_upgrade_to_schema;
-+
-+ return rc;
-+#else
-+ return DB_ERROR;
-+#endif
-+}
-+
-+#ifdef HAVE_MYSQL
-+
-+void db_mysql_close(db_conn_p conn)
-+{
-+ if (!conn)
-+ return;
-+ if (!conn->private)
-+ return;
-+ f.mysql_close_fn(conn->private);
-+ conn->private = 0;
-+}
-+
-+const char *db_mysql_errmsg(db_conn_p conn)
-+{
-+ if (!conn)
-+ return "(no connection)";
-+ if (!conn->private)
-+ return "(no private data in conn)";
-+ return f.mysql_error_fn(conn->private);
-+}
-+
-+static void print_warnings(int level, MYSQL *m)
-+{
-+ int rc;
-+ MYSQL_RES *res;
-+ int fields;
-+ MYSQL_ROW row;
-+
-+ if (m == NULL)
-+ csync_fatal("print_warnings: m is NULL");
-+
-+ rc = f.mysql_query_fn(m, "SHOW WARNINGS");
-+ if (rc != 0)
-+ csync_fatal("print_warnings: Failed to get warning messages");
-+
-+ res = f.mysql_store_result_fn(m);
-+ if (res == NULL)
-+ csync_fatal("print_warnings: Failed to get result set for warning messages");
-+
-+ fields = f.mysql_num_fields_fn(res);
-+ if (fields < 2)
-+ csync_fatal("print_warnings: Strange: show warnings result set has less than 2 rows");
-+
-+ row = f.mysql_fetch_row_fn(res);
-+
-+ while (row) {
-+ csync_debug(level, "MySql Warning: %s\n", row[2]);
-+ row = f.mysql_fetch_row_fn(res);
-+ }
-+
-+ f.mysql_free_result_fn(res);
-+}
-+
-+int db_mysql_exec(db_conn_p conn, const char *sql)
-+{
-+ int rc = DB_ERROR;
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ rc = f.mysql_query_fn(conn->private, sql);
-+
-+/* Treat warnings as errors. For example when a column is too short this should
-+ be an error. */
-+
-+ if (f.mysql_warning_count_fn(conn->private) > 0) {
-+ print_warnings(1, conn->private);
-+ return DB_ERROR;
-+ }
-+
-+ /* On error parse, create DB ERROR element */
-+ return rc;
-+}
-+
-+int db_mysql_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p,
-+ char **pptail) {
-+ int rc = DB_ERROR;
-+
-+ *stmt_p = NULL;
-+
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ db_stmt_p stmt = malloc(sizeof(*stmt));
-+ /* TODO avoid strlen, use configurable limit? */
-+ rc = f.mysql_query_fn(conn->private, sql);
-+
-+/* Treat warnings as errors. For example when a column is too short this should
-+ be an error. */
-+
-+ if (f.mysql_warning_count_fn(conn->private) > 0) {
-+ print_warnings(1, conn->private);
-+ return DB_ERROR;
-+ }
-+
-+ MYSQL_RES *mysql_stmt = f.mysql_store_result_fn(conn->private);
-+ if (mysql_stmt == NULL) {
-+ csync_debug(2, "Error in mysql_store_result: %s", f.mysql_error_fn(conn->private));
-+ return DB_ERROR;
-+ }
-+
-+/* Treat warnings as errors. For example when a column is too short this should
-+ be an error. */
-+
-+ if (f.mysql_warning_count_fn(conn->private) > 0) {
-+ print_warnings(1, conn->private);
-+ return DB_ERROR;
-+ }
-+
-+ stmt->private = mysql_stmt;
-+ /* TODO error mapping / handling */
-+ *stmt_p = stmt;
-+ stmt->get_column_text = db_mysql_stmt_get_column_text;
-+ stmt->get_column_blob = db_mysql_stmt_get_column_blob;
-+ stmt->get_column_int = db_mysql_stmt_get_column_int;
-+ stmt->next = db_mysql_stmt_next;
-+ stmt->close = db_mysql_stmt_close;
-+ stmt->db = conn;
-+ return DB_OK;
-+}
-+
-+const void* db_mysql_stmt_get_column_blob(db_stmt_p stmt, int column) {
-+ if (!stmt || !stmt->private2) {
-+ return 0;
-+ }
-+ MYSQL_ROW row = stmt->private2;
-+ return row[column];
-+}
-+
-+const char *db_mysql_stmt_get_column_text(db_stmt_p stmt, int column) {
-+ if (!stmt || !stmt->private2) {
-+ return 0;
-+ }
-+ MYSQL_ROW row = stmt->private2;
-+ return row[column];
-+}
-+
-+int db_mysql_stmt_get_column_int(db_stmt_p stmt, int column) {
-+ const char *value = db_mysql_stmt_get_column_text(stmt, column);
-+ if (value)
-+ return atoi(value);
-+ /* error mapping */
-+ return 0;
-+}
-+
-+
-+int db_mysql_stmt_next(db_stmt_p stmt)
-+{
-+ MYSQL_RES *mysql_stmt = stmt->private;
-+ stmt->private2 = f.mysql_fetch_row_fn(mysql_stmt);
-+ /* error mapping */
-+ if (stmt->private2)
-+ return DB_ROW;
-+ return DB_DONE;
-+}
-+
-+int db_mysql_stmt_close(db_stmt_p stmt)
-+{
-+ MYSQL_RES *mysql_stmt = stmt->private;
-+ f.mysql_free_result_fn(mysql_stmt);
-+ free(stmt);
-+ return DB_OK;
-+}
-+
-+
-+int db_mysql_upgrade_to_schema(int version)
-+{
-+ if (version < 0)
-+ return DB_OK;
-+
-+ if (version > 0)
-+ return DB_ERROR;
-+
-+ csync_debug(2, "Upgrading database schema to version %d.\n", version);
-+
-+/* We want proper logging, so use the csync sql function instead
-+ * of that from the database layer.
-+ */
-+ csync_db_sql("Creating action table",
-+ "CREATE TABLE `action` ("
-+ " `filename` varchar(4096) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,"
-+ " `command` text,"
-+ " `logfile` text,"
-+ " UNIQUE KEY `filename` (`filename`(326),`command`(20))"
-+ ")");
-+
-+ csync_db_sql("Creating dirty table",
-+ "CREATE TABLE `dirty` ("
-+ " `filename` varchar(4096) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,"
-+ " `forced` int(11) DEFAULT NULL,"
-+ " `myname` varchar(50) DEFAULT NULL,"
-+ " `peername` varchar(50) DEFAULT NULL,"
-+ " UNIQUE KEY `filename` (`filename`(316),`peername`),"
-+ " KEY `dirty_host` (`peername`(10))"
-+ ")");
-+
-+ csync_db_sql("Creating file table",
-+ "CREATE TABLE `file` ("
-+ " `filename` varchar(4096) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,"
-+ " `checktxt` varchar(200) DEFAULT NULL,"
-+ " UNIQUE KEY `filename` (`filename`(333))"
-+ ")");
-+
-+ csync_db_sql("Creating hint table",
-+ "CREATE TABLE `hint` ("
-+ " `filename` varchar(4096) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL,"
-+ " `recursive` int(11) DEFAULT NULL"
-+ ")");
-+
-+ csync_db_sql("Creating x509_cert table",
-+ "CREATE TABLE `x509_cert` ("
-+ " `peername` varchar(50) DEFAULT NULL,"
-+ " `certdata` varchar(255) DEFAULT NULL,"
-+ " UNIQUE KEY `peername` (`peername`)"
-+ ")");
-+
-+/* csync_db_sql does a csync_fatal on error, so we always return DB_OK here. */
-+
-+ return DB_OK;
-+}
-+
-+
-+#endif
-diff --git a/db_mysql.h b/db_mysql.h
-new file mode 100644
-index 0000000..c5aeab3
---- /dev/null
-+++ b/db_mysql.h
-@@ -0,0 +1,19 @@
-+
-+#ifndef DB_MYSQL_H
-+#define DB_MYSQL_H
-+
-+/* public */
-+int db_mysql_open(const char *file, db_conn_p *conn_p);
-+/* Private */
-+void db_mysql_close(db_conn_p db_conn);
-+int db_mysql_exec(db_conn_p conn, const char *sql);
-+int db_mysql_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p, char **pptail);
-+int db_mysql_stmt_next(db_stmt_p stmt);
-+const void* db_mysql_stmt_get_column_blob(db_stmt_p stmt, int column);
-+const char *db_mysql_stmt_get_column_text(db_stmt_p stmt, int column);
-+int db_mysql_stmt_get_column_int(db_stmt_p stmt, int column);
-+int db_mysql_stmt_close(db_stmt_p stmt);
-+const char *db_mysql_errmsg(db_conn_p db_conn);
-+int db_mysql_upgrade_to_schema(int version);
-+
-+#endif
-diff --git a/db_postgres.c b/db_postgres.c
-new file mode 100644
-index 0000000..b40bdfb
---- /dev/null
-+++ b/db_postgres.c
-@@ -0,0 +1,458 @@
-+/*
-+ * Copyright (C) 2010 Dennis Schafroth <dennis@schafroth.com>
-+ * Copyright (C) 2010 Johannes Thoma <johannes.thoma@gmx.at>
-+ *
-+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ */
-+
-+#include "csync2.h"
-+#include <stdio.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <signal.h>
-+#include <time.h>
-+#include <string.h>
-+#include "db_api.h"
-+#include "db_postgres.h"
-+#include "dl.h"
-+
-+#ifdef HAVE_POSTGRES
-+#include <postgresql/libpq-fe.h>
-+#endif
-+
-+#if (!defined HAVE_POSTGRES)
-+int db_postgres_open(const char *file, db_conn_p *conn_p)
-+{
-+ return DB_ERROR;
-+}
-+#else
-+
-+static struct db_postgres_fns {
-+ PGconn *(*PQconnectdb_fn)(char *);
-+ ConnStatusType (*PQstatus_fn)(const PGconn *);
-+ char *(*PQerrorMessage_fn)(const PGconn *);
-+ void (*PQfinish_fn)(PGconn *);
-+ PGresult *(*PQexec_fn)(PGconn *, const char *);
-+ ExecStatusType (*PQresultStatus_fn)(const PGresult *);
-+ char *(*PQresultErrorMessage_fn)(const PGresult *);
-+ void (*PQclear_fn)(PGresult *);
-+ int (*PQntuples_fn)(const PGresult *);
-+ char *(*PQgetvalue_fn)(const PGresult *, int, int);
-+} f;
-+
-+static void *dl_handle;
-+
-+
-+static void db_postgres_dlopen(void)
-+{
-+ csync_debug(1, "Opening shared library libpq.so\n");
-+
-+ dl_handle = dlopen("libpq.so", RTLD_LAZY);
-+ if (dl_handle == NULL) {
-+ csync_fatal("Could not open libpq.so: %s\nPlease install postgres client library (libpg) or use other database (sqlite, mysql)\n", dlerror());
-+ }
-+ csync_debug(1, "Reading symbols from shared library libpq.so\n");
-+
-+ LOOKUP_SYMBOL(dl_handle, PQconnectdb);
-+ LOOKUP_SYMBOL(dl_handle, PQstatus);
-+ LOOKUP_SYMBOL(dl_handle, PQerrorMessage);
-+ LOOKUP_SYMBOL(dl_handle, PQfinish);
-+ LOOKUP_SYMBOL(dl_handle, PQexec);
-+ LOOKUP_SYMBOL(dl_handle, PQresultStatus);
-+ LOOKUP_SYMBOL(dl_handle, PQresultErrorMessage);
-+ LOOKUP_SYMBOL(dl_handle, PQclear);
-+ LOOKUP_SYMBOL(dl_handle, PQntuples);
-+ LOOKUP_SYMBOL(dl_handle, PQgetvalue);
-+}
-+
-+
-+
-+/* Thi function parses a URL string like pgsql://[user[:passwd]@]hostname[:port]/database.
-+ and returns the result in the given parameters.
-+
-+ If an optional keyword is not given, the value of the parameter is not changed.
-+*/
-+
-+static int db_pgsql_parse_url(char *url, char **host, char **user, char **pass, char **database, unsigned int *port)
-+{
-+ char *pos = strchr(url, '@');
-+ if (pos) {
-+ *(pos) = 0;
-+ *(user) = url;
-+ url = pos + 1;
-+
-+ pos = strchr(*user, ':');
-+ if (pos) {
-+ *(pos) = 0;
-+ *(pass) = (pos +1);
-+ }
-+ }
-+ *host = url;
-+ pos = strchr(*host, '/');
-+ if (pos) {
-+ // Database
-+ (*pos) = 0;
-+ *database = pos+1;
-+ }
-+ pos = strchr(*host, ':');
-+ if (pos) {
-+ (*pos) = 0;
-+ *port = atoi(pos+1);
-+ }
-+ return DB_OK;
-+}
-+
-+int db_postgres_open(const char *file, db_conn_p *conn_p)
-+{
-+ PGconn *pg_conn;
-+ char *host, *user, *pass, *database;
-+ unsigned int port = 5432; /* default postgres port */
-+ char *db_url = malloc(strlen(file)+1);
-+ char *create_database_statement;
-+ char *pg_conn_info;
-+
-+ db_postgres_dlopen();
-+
-+ if (db_url == NULL)
-+ csync_fatal("No memory for db_url\n");
-+
-+ user = "postgres";
-+ pass = "";
-+ host = "localhost";
-+ database = "csync2";
-+
-+ strcpy(db_url, file);
-+ int rc = db_pgsql_parse_url(db_url, &host, &user, &pass, &database, &port);
-+ if (rc != DB_OK)
-+ return rc;
-+
-+ ASPRINTF(&pg_conn_info, "host='%s' user='%s' password='%s' dbname='%s' port=%d",
-+ host, user, pass, database, port);
-+
-+ pg_conn = f.PQconnectdb_fn(pg_conn_info);
-+ if (pg_conn == NULL)
-+ csync_fatal("No memory for postgress connection handle\n");
-+
-+ if (f.PQstatus_fn(pg_conn) != CONNECTION_OK) {
-+ f.PQfinish_fn(pg_conn);
-+ free(pg_conn_info);
-+
-+ ASPRINTF(&pg_conn_info, "host='%s' user='%s' password='%s' dbname='postgres' port=%d",
-+ host, user, pass, port);
-+
-+ pg_conn = f.PQconnectdb_fn(pg_conn_info);
-+ if (pg_conn == NULL)
-+ csync_fatal("No memory for postgress connection handle\n");
-+
-+ if (f.PQstatus_fn(pg_conn) != CONNECTION_OK) {
-+ csync_debug(0, "Connection failed: %s", f.PQerrorMessage_fn(pg_conn));
-+ f.PQfinish_fn(pg_conn);
-+ free(pg_conn_info);
-+ return DB_ERROR;
-+ } else {
-+ char *create_database_statement;
-+ PGresult *res;
-+
-+ csync_debug(1, "Database %s not found, trying to create it ...", database);
-+ ASPRINTF(&create_database_statement, "create database %s", database);
-+ res = f.PQexec_fn(pg_conn, create_database_statement);
-+
-+ free(create_database_statement);
-+
-+ switch (f.PQresultStatus_fn(res)) {
-+ case PGRES_COMMAND_OK:
-+ case PGRES_TUPLES_OK:
-+ break;
-+
-+ default:
-+ csync_debug(0, "Could not create database %s: %s", database, f.PQerrorMessage_fn(pg_conn));
-+ return DB_ERROR;
-+ }
-+
-+ f.PQfinish_fn(pg_conn);
-+ free(pg_conn_info);
-+
-+ ASPRINTF(&pg_conn_info, "host='%s' user='%s' password='%s' dbname='%s' port=%d",
-+ host, user, pass, database, port);
-+
-+ pg_conn = f.PQconnectdb_fn(pg_conn_info);
-+ if (pg_conn == NULL)
-+ csync_fatal("No memory for postgress connection handle\n");
-+
-+ if (f.PQstatus_fn(pg_conn) != CONNECTION_OK) {
-+ csync_debug(0, "Connection failed: %s", f.PQerrorMessage_fn(pg_conn));
-+ f.PQfinish_fn(pg_conn);
-+ free(pg_conn_info);
-+ return DB_ERROR;
-+ }
-+ }
-+ }
-+
-+ db_conn_p conn = calloc(1, sizeof(*conn));
-+
-+ if (conn == NULL)
-+ csync_fatal("No memory for conn\n");
-+
-+ *conn_p = conn;
-+ conn->private = pg_conn;
-+ conn->close = db_postgres_close;
-+ conn->exec = db_postgres_exec;
-+ conn->errmsg = db_postgres_errmsg;
-+ conn->prepare = db_postgres_prepare;
-+ conn->upgrade_to_schema = db_postgres_upgrade_to_schema;
-+
-+ free(pg_conn_info);
-+
-+ return DB_OK;
-+}
-+
-+
-+void db_postgres_close(db_conn_p conn)
-+{
-+ if (!conn)
-+ return;
-+ if (!conn->private)
-+ return;
-+ f.PQfinish_fn(conn->private);
-+ conn->private = 0;
-+}
-+
-+const char *db_postgres_errmsg(db_conn_p conn)
-+{
-+ if (!conn)
-+ return "(no connection)";
-+ if (!conn->private)
-+ return "(no private data in conn)";
-+ return f.PQerrorMessage_fn(conn->private);
-+}
-+
-+
-+int db_postgres_exec(db_conn_p conn, const char *sql)
-+{
-+ PGresult *res;
-+
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ res = f.PQexec_fn(conn->private, sql);
-+ switch (f.PQresultStatus_fn(res)) {
-+ case PGRES_COMMAND_OK:
-+ case PGRES_TUPLES_OK:
-+ return DB_OK;
-+
-+ default:
-+ return DB_ERROR;
-+ }
-+}
-+
-+
-+int db_postgres_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p,
-+ char **pptail)
-+{
-+ PGresult *result;
-+ int *row_p;
-+
-+ *stmt_p = NULL;
-+
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ result = f.PQexec_fn(conn->private, sql);
-+
-+ if (result == NULL)
-+ csync_fatal("No memory for result\n");
-+
-+ switch (f.PQresultStatus_fn(result)) {
-+ case PGRES_COMMAND_OK:
-+ case PGRES_TUPLES_OK:
-+ break;
-+
-+ default:
-+ csync_debug(1, "Error in PQexec: %s", f.PQresultErrorMessage_fn(result));
-+ f.PQclear_fn(result);
-+ return DB_ERROR;
-+ }
-+
-+ row_p = malloc(sizeof(*row_p));
-+ if (row_p == NULL)
-+ csync_fatal("No memory for row\n");
-+ *row_p = -1;
-+
-+ db_stmt_p stmt = malloc(sizeof(*stmt));
-+ if (stmt == NULL)
-+ csync_fatal("No memory for stmt\n");
-+
-+ stmt->private = result;
-+ stmt->private2 = row_p;
-+
-+ *stmt_p = stmt;
-+ stmt->get_column_text = db_postgres_stmt_get_column_text;
-+ stmt->get_column_blob = db_postgres_stmt_get_column_blob;
-+ stmt->get_column_int = db_postgres_stmt_get_column_int;
-+ stmt->next = db_postgres_stmt_next;
-+ stmt->close = db_postgres_stmt_close;
-+ stmt->db = conn;
-+ return DB_OK;
-+}
-+
-+
-+const void* db_postgres_stmt_get_column_blob(db_stmt_p stmt, int column)
-+{
-+ PGresult *result;
-+ int *row_p;
-+
-+ if (!stmt || !stmt->private || !stmt->private2) {
-+ return 0;
-+ }
-+ result = (PGresult*)stmt->private;
-+ row_p = (int*)stmt->private2;
-+
-+ if (*row_p >= f.PQntuples_fn(result) || *row_p < 0) {
-+ csync_debug(1, "row index out of range (should be between 0 and %d, is %d)\n",
-+ *row_p, f.PQntuples_fn(result));
-+ return NULL;
-+ }
-+ return f.PQgetvalue_fn(result, *row_p, column);
-+}
-+
-+const char *db_postgres_stmt_get_column_text(db_stmt_p stmt, int column)
-+{
-+ PGresult *result;
-+ int *row_p;
-+
-+ if (!stmt || !stmt->private || !stmt->private2) {
-+ return 0;
-+ }
-+ result = (PGresult*)stmt->private;
-+ row_p = (int*)stmt->private2;
-+
-+ if (*row_p >= f.PQntuples_fn(result) || *row_p < 0) {
-+ csync_debug(1, "row index out of range (should be between 0 and %d, is %d)\n",
-+ *row_p, f.PQntuples_fn(result));
-+ return NULL;
-+ }
-+ return f.PQgetvalue_fn(result, *row_p, column);
-+}
-+
-+int db_postgres_stmt_get_column_int(db_stmt_p stmt, int column)
-+{
-+ PGresult *result;
-+ int *row_p;
-+
-+ if (!stmt || !stmt->private || !stmt->private2) {
-+ return 0;
-+ }
-+ result = (PGresult*)stmt->private;
-+ row_p = (int*)stmt->private2;
-+
-+ if (*row_p >= f.PQntuples_fn(result) || *row_p < 0) {
-+ csync_debug(1, "row index out of range (should be between 0 and %d, is %d)\n",
-+ *row_p, f.PQntuples_fn(result));
-+ return 0;
-+ }
-+ return atoi(f.PQgetvalue_fn(result, *row_p, column));
-+}
-+
-+
-+int db_postgres_stmt_next(db_stmt_p stmt)
-+{
-+ PGresult *result;
-+ int *row_p;
-+
-+ if (!stmt || !stmt->private || !stmt->private2) {
-+ return 0;
-+ }
-+ result = (PGresult*)stmt->private;
-+ row_p = (int*)stmt->private2;
-+
-+ (*row_p)++;
-+ if (*row_p >= f.PQntuples_fn(result))
-+ return DB_DONE;
-+
-+ return DB_ROW;
-+}
-+
-+int db_postgres_stmt_close(db_stmt_p stmt)
-+{
-+ PGresult *res = stmt->private;
-+
-+ f.PQclear_fn(res);
-+ free(stmt->private2);
-+ free(stmt);
-+ return DB_OK;
-+}
-+
-+
-+int db_postgres_upgrade_to_schema(int version)
-+{
-+ if (version < 0)
-+ return DB_OK;
-+
-+ if (version > 0)
-+ return DB_ERROR;
-+
-+ csync_debug(2, "Upgrading database schema to version %d.\n", version);
-+
-+ csync_db_sql("Creating action table",
-+"CREATE TABLE action ("
-+" filename varchar(255) DEFAULT NULL,"
-+" command text,"
-+" logfile text,"
-+" UNIQUE (filename,command)"
-+");");
-+
-+ csync_db_sql("Creating dirty table",
-+"CREATE TABLE dirty ("
-+" filename varchar(200) DEFAULT NULL,"
-+" forced int DEFAULT NULL,"
-+" myname varchar(100) DEFAULT NULL,"
-+" peername varchar(100) DEFAULT NULL,"
-+" UNIQUE (filename,peername)"
-+");");
-+
-+ csync_db_sql("Creating file table",
-+"CREATE TABLE file ("
-+" filename varchar(200) DEFAULT NULL,"
-+" checktxt varchar(200) DEFAULT NULL,"
-+" UNIQUE (filename)"
-+");");
-+
-+ csync_db_sql("Creating hint table",
-+"CREATE TABLE hint ("
-+" filename varchar(255) DEFAULT NULL,"
-+" recursive int DEFAULT NULL"
-+");");
-+
-+ csync_db_sql("Creating x509_cert table",
-+"CREATE TABLE x509_cert ("
-+" peername varchar(255) DEFAULT NULL,"
-+" certdata varchar(255) DEFAULT NULL,"
-+" UNIQUE (peername)"
-+");");
-+
-+ return DB_OK;
-+}
-+
-+
-+#endif /* HAVE_POSTGRES */
-diff --git a/db_postgres.h b/db_postgres.h
-new file mode 100644
-index 0000000..949439e
---- /dev/null
-+++ b/db_postgres.h
-@@ -0,0 +1,20 @@
-+
-+#ifndef DB_POSTGRES_H
-+#define DB_POSTGRES_H
-+
-+/* public */
-+int db_postgres_open(const char *file, db_conn_p *conn_p);
-+/* Private */
-+void db_postgres_close(db_conn_p db_conn);
-+int db_postgres_exec(db_conn_p conn, const char *sql);
-+int db_postgres_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p, char **pptail);
-+const char *db_postgres_errmsg(db_conn_p db_conn);
-+
-+int db_postgres_stmt_next(db_stmt_p stmt);
-+const void* db_postgres_stmt_get_column_blob(db_stmt_p stmt, int column);
-+const char *db_postgres_stmt_get_column_text(db_stmt_p stmt, int column);
-+int db_postgres_stmt_get_column_int(db_stmt_p stmt, int column);
-+int db_postgres_stmt_close(db_stmt_p stmt);
-+int db_postgres_upgrade_to_schema(int version);
-+
-+#endif
-diff --git a/db_sqlite.c b/db_sqlite.c
-new file mode 100644
-index 0000000..81c5c75
---- /dev/null
-+++ b/db_sqlite.c
-@@ -0,0 +1,263 @@
-+/*
-+ * Copyright (C) 2010 Dennis Schafroth <dennis@schafroth.com>>
-+ * Copyright (C) 2010 Johannes Thoma <johannes.thoma@gmx.at>
-+ *
-+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ */
-+
-+#include "csync2.h"
-+#if defined(HAVE_SQLITE3)
-+#include <sqlite3.h>
-+#endif
-+#include <stdio.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <signal.h>
-+#include <time.h>
-+#include "db_api.h"
-+#include "db_sqlite.h"
-+#include "dl.h"
-+
-+#ifndef HAVE_SQLITE3
-+int db_sqlite_open(const char *file, db_conn_p *conn_p) {
-+ return DB_ERROR;
-+}
-+#else
-+
-+static struct db_sqlite3_fns {
-+ int (*sqlite3_open_fn) (const char*, sqlite3 **);
-+ int (*sqlite3_close_fn) (sqlite3 *);
-+ const char *(*sqlite3_errmsg_fn) (sqlite3 *);
-+ int (*sqlite3_exec_fn) (sqlite3*, const char *,
-+ int (*) (void*,int,char**,char**), void*, char **);
-+ int (*sqlite3_prepare_v2_fn)(sqlite3 *, const char *, int,
-+ sqlite3_stmt **, const char **pzTail);
-+ const unsigned char *(*sqlite3_column_text_fn)(sqlite3_stmt*, int);
-+ const void *(*sqlite3_column_blob_fn)(sqlite3_stmt*, int);
-+ int (*sqlite3_column_int_fn)(sqlite3_stmt*, int);
-+ int (*sqlite3_step_fn)(sqlite3_stmt*);
-+ int (*sqlite3_finalize_fn)(sqlite3_stmt *);
-+} f;
-+
-+static void *dl_handle;
-+
-+
-+static void db_sqlite3_dlopen(void)
-+{
-+ csync_debug(1, "Opening shared library libsqlite3.so\n");
-+
-+ dl_handle = dlopen("libsqlite3.so", RTLD_LAZY);
-+ if (dl_handle == NULL) {
-+ csync_fatal("Could not open libsqlite3.so: %s\nPlease install sqlite3 client library (libsqlite3) or use other database (postgres, mysql)\n", dlerror());
-+ }
-+ csync_debug(1, "Reading symbols from shared library libsqlite3.so\n");
-+
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_open);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_close);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_errmsg);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_exec);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_prepare_v2);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_column_text);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_column_blob);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_column_int);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_step);
-+ LOOKUP_SYMBOL(dl_handle, sqlite3_finalize);
-+}
-+
-+static int sqlite_errors[] = { SQLITE_OK, SQLITE_ERROR, SQLITE_BUSY, SQLITE_ROW, SQLITE_DONE, -1 };
-+static int db_errors[] = { DB_OK, DB_ERROR, DB_BUSY, DB_ROW, DB_DONE, -1 };
-+
-+int db_sqlite_error_map(int sqlite_err) {
-+ int index;
-+ for (index = 0; ; index++) {
-+ if (sqlite_errors[index] == -1)
-+ return DB_ERROR;
-+ if (sqlite_err == sqlite_errors[index])
-+ return db_errors[index];
-+ }
-+}
-+
-+int db_sqlite_open(const char *file, db_conn_p *conn_p)
-+{
-+ sqlite3 *db;
-+
-+ db_sqlite3_dlopen();
-+
-+ int rc = f.sqlite3_open_fn(file, &db);
-+ if ( rc != SQLITE_OK ) {
-+ return db_sqlite_error_map(rc);
-+ };
-+ db_conn_p conn = calloc(1, sizeof(*conn));
-+ if (conn == NULL) {
-+ return DB_ERROR;
-+ }
-+ *conn_p = conn;
-+ conn->private = db;
-+ conn->close = db_sqlite_close;
-+ conn->exec = db_sqlite_exec;
-+ conn->prepare = db_sqlite_prepare;
-+ conn->errmsg = db_sqlite_errmsg;
-+ conn->upgrade_to_schema = db_sqlite_upgrade_to_schema;
-+ return db_sqlite_error_map(rc);
-+}
-+
-+void db_sqlite_close(db_conn_p conn)
-+{
-+ if (!conn)
-+ return;
-+ if (!conn->private)
-+ return;
-+ f.sqlite3_close_fn(conn->private);
-+ conn->private = 0;
-+}
-+
-+const char *db_sqlite_errmsg(db_conn_p conn)
-+{
-+ if (!conn)
-+ return "(no connection)";
-+ if (!conn->private)
-+ return "(no private data in conn)";
-+ return f.sqlite3_errmsg_fn(conn->private);
-+}
-+
-+int db_sqlite_exec(db_conn_p conn, const char *sql) {
-+ int rc;
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ rc = f.sqlite3_exec_fn(conn->private, sql, 0, 0, 0);
-+ return db_sqlite_error_map(rc);
-+}
-+
-+int db_sqlite_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p, char **pptail) {
-+ int rc;
-+
-+ *stmt_p = NULL;
-+
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ db_stmt_p stmt = malloc(sizeof(*stmt));
-+ sqlite3_stmt *sqlite_stmt = 0;
-+ /* TODO avoid strlen, use configurable limit? */
-+ rc = f.sqlite3_prepare_v2_fn(conn->private, sql, strlen(sql), &sqlite_stmt, (const char **) pptail);
-+ if (rc != SQLITE_OK)
-+ return db_sqlite_error_map(rc);
-+ stmt->private = sqlite_stmt;
-+ *stmt_p = stmt;
-+ stmt->get_column_text = db_sqlite_stmt_get_column_text;
-+ stmt->get_column_blob = db_sqlite_stmt_get_column_blob;
-+ stmt->get_column_int = db_sqlite_stmt_get_column_int;
-+ stmt->next = db_sqlite_stmt_next;
-+ stmt->close = db_sqlite_stmt_close;
-+ stmt->db = conn;
-+ return db_sqlite_error_map(rc);
-+}
-+
-+const char *db_sqlite_stmt_get_column_text(db_stmt_p stmt, int column) {
-+ if (!stmt || !stmt->private) {
-+ return 0;
-+ }
-+ sqlite3_stmt *sqlite_stmt = stmt->private;
-+ const unsigned char *result = f.sqlite3_column_text_fn(sqlite_stmt, column);
-+ /* error handling */
-+ return (const char*)result;
-+}
-+
-+#if defined(HAVE_SQLITE3)
-+const void* db_sqlite_stmt_get_column_blob(db_stmt_p stmtx, int col) {
-+ sqlite3_stmt *stmt = stmtx->private;
-+ return f.sqlite3_column_blob_fn(stmt,col);
-+}
-+#endif
-+
-+
-+
-+int db_sqlite_stmt_get_column_int(db_stmt_p stmt, int column) {
-+ sqlite3_stmt *sqlite_stmt = stmt->private;
-+ int rc = f.sqlite3_column_int_fn(sqlite_stmt, column);
-+ return db_sqlite_error_map(rc);
-+}
-+
-+
-+int db_sqlite_stmt_next(db_stmt_p stmt)
-+{
-+ sqlite3_stmt *sqlite_stmt = stmt->private;
-+ int rc = f.sqlite3_step_fn(sqlite_stmt);
-+ return db_sqlite_error_map(rc);
-+}
-+
-+int db_sqlite_stmt_close(db_stmt_p stmt)
-+{
-+ sqlite3_stmt *sqlite_stmt = stmt->private;
-+ int rc = f.sqlite3_finalize_fn(sqlite_stmt);
-+ free(stmt);
-+ return db_sqlite_error_map(rc);
-+}
-+
-+
-+int db_sqlite_upgrade_to_schema(int version)
-+{
-+ if (version < 0)
-+ return DB_OK;
-+
-+ if (version > 0)
-+ return DB_ERROR;
-+
-+ csync_debug(2, "Upgrading database schema to version %d.\n", version);
-+
-+ csync_db_sql("Creating file table",
-+ "CREATE TABLE file ("
-+ " filename, checktxt,"
-+ " UNIQUE ( filename ) ON CONFLICT REPLACE"
-+ ")");
-+
-+ csync_db_sql("Creating dirty table",
-+ "CREATE TABLE dirty ("
-+ " filename, forced, myname, peername,"
-+ " UNIQUE ( filename, peername ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ csync_db_sql("Creating hint table",
-+ "CREATE TABLE hint ("
-+ " filename, recursive,"
-+ " UNIQUE ( filename, recursive ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ csync_db_sql("Creating action table",
-+ "CREATE TABLE action ("
-+ " filename, command, logfile,"
-+ " UNIQUE ( filename, command ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ csync_db_sql("Creating x509_cert table",
-+ "CREATE TABLE x509_cert ("
-+ " peername, certdata,"
-+ " UNIQUE ( peername ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ return DB_OK;
-+}
-+
-+#endif
-diff --git a/db_sqlite.h b/db_sqlite.h
-new file mode 100644
-index 0000000..f5e2340
---- /dev/null
-+++ b/db_sqlite.h
-@@ -0,0 +1,19 @@
-+
-+#ifndef DB_SQLITE_H
-+#define DB_SQLITE_H
-+
-+/* public */
-+int db_sqlite_open(const char *file, db_conn_p *conn_p);
-+/* Private */
-+void db_sqlite_close(db_conn_p db_conn);
-+int db_sqlite_exec(db_conn_p conn, const char *sql);
-+int db_sqlite_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p, char **pptail);
-+int db_sqlite_stmt_next(db_stmt_p stmt);
-+const char* db_sqlite_stmt_get_column_text(db_stmt_p stmt, int column);
-+const void* db_sqlite_stmt_get_column_blob(db_stmt_p stmt, int column);
-+int db_sqlite_stmt_get_column_int(db_stmt_p stmt, int column);
-+int db_sqlite_stmt_close(db_stmt_p stmt);
-+const char *db_sqlite_errmsg(db_conn_p conn);
-+int db_sqlite_upgrade_to_schema(int version);
-+
-+#endif
-diff --git a/db_sqlite2.c b/db_sqlite2.c
-new file mode 100644
-index 0000000..8b2c85e
---- /dev/null
-+++ b/db_sqlite2.c
-@@ -0,0 +1,257 @@
-+/*
-+ * Copyright (C) 2010 Dennis Schafroth <dennis@schafroth.com>>
-+ * Copyright (C) 2010 Johannes Thoma <johannes.thoma@gmx.at>
-+ *
-+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+ */
-+
-+#include "db_api.h"
-+#include "config.h"
-+
-+#ifndef HAVE_SQLITE
-+/* dummy function to implement a open that fails */
-+int db_sqlite2_open(const char *file, db_conn_p *conn_p) {
-+ return DB_ERROR;
-+}
-+#else
-+
-+#include <sqlite.h>
-+#include <stdio.h>
-+#include <stdarg.h>
-+#include <stdlib.h>
-+#include <unistd.h>
-+#include <signal.h>
-+#include <time.h>
-+#include "db_sqlite2.h"
-+#include <dl.h>
-+
-+
-+static struct db_sqlite_fns {
-+ sqlite *(*sqlite_open_fn)(const char *, int, char**);
-+ void (*sqlite_close_fn)(sqlite *);
-+ int (*sqlite_exec_fn)(sqlite *, char *, int (*)(void*,int,char**,char**), void *, char **);
-+ int (*sqlite_compile_fn)(sqlite *, const char *, const char **, sqlite_vm **, char **);
-+ int (*sqlite_step_fn)(sqlite_vm *, int *, const char ***, const char ***);
-+ int (*sqlite_finalize_fn)(sqlite_vm *, char **);
-+} f;
-+
-+static char *errmsg;
-+
-+static void *dl_handle;
-+
-+
-+static void db_sqlite_dlopen(void)
-+{
-+ csync_debug(1, "Opening shared library libsqlite.so\n");
-+
-+ dl_handle = dlopen("libsqlite.so", RTLD_LAZY);
-+ if (dl_handle == NULL) {
-+ csync_debug(1, "Libsqlite.so not found, trying libsqlite.so.0\n");
-+ dl_handle = dlopen("libsqlite.so.0", RTLD_LAZY);
-+ if (dl_handle == NULL) {
-+ csync_fatal("Could not open libsqlite.so: %s\nPlease install sqlite client library (libsqlite) or use other database (postgres, mysql)\n", dlerror());
-+ }
-+ }
-+ csync_debug(1, "Opening shared library libsqlite.so\n");
-+
-+ LOOKUP_SYMBOL(dl_handle, sqlite_open);
-+ LOOKUP_SYMBOL(dl_handle, sqlite_close);
-+ LOOKUP_SYMBOL(dl_handle, sqlite_exec);
-+ LOOKUP_SYMBOL(dl_handle, sqlite_compile);
-+ LOOKUP_SYMBOL(dl_handle, sqlite_step);
-+ LOOKUP_SYMBOL(dl_handle, sqlite_finalize);
-+
-+}
-+
-+
-+int db_sqlite2_open(const char *file, db_conn_p *conn_p)
-+{
-+ db_sqlite_dlopen();
-+
-+ sqlite *db = f.sqlite_open_fn(file, 0, &errmsg);
-+ if ( db == 0 ) {
-+ return DB_ERROR;
-+ };
-+ db_conn_p conn = calloc(1, sizeof(*conn));
-+ if (conn == NULL) {
-+ return DB_ERROR;
-+ }
-+ *conn_p = conn;
-+ conn->private = db;
-+ conn->close = db_sqlite2_close;
-+ conn->exec = db_sqlite2_exec;
-+ conn->prepare = db_sqlite2_prepare;
-+ conn->errmsg = NULL;
-+ conn->upgrade_to_schema = db_sqlite2_upgrade_to_schema;
-+ return DB_OK;
-+}
-+
-+void db_sqlite2_close(db_conn_p conn)
-+{
-+ if (!conn)
-+ return;
-+ if (!conn->private)
-+ return;
-+ f.sqlite_close_fn(conn->private);
-+ conn->private = 0;
-+}
-+
-+const char *db_sqlite2_errmsg(db_conn_p conn)
-+{
-+ if (!conn)
-+ return "(no connection)";
-+ if (!conn->private)
-+ return "(no private data in conn)";
-+ return errmsg;
-+}
-+
-+int db_sqlite2_exec(db_conn_p conn, const char *sql) {
-+ int rc;
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ rc = f.sqlite_exec_fn(conn->private, (char*) sql, 0, 0, &errmsg);
-+ /* On error parse, create DB ERROR element */
-+ return rc;
-+}
-+
-+int db_sqlite2_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p, char **pptail) {
-+ int rc;
-+ sqlite *db;
-+
-+ *stmt_p = NULL;
-+
-+ if (!conn)
-+ return DB_NO_CONNECTION;
-+
-+ if (!conn->private) {
-+ /* added error element */
-+ return DB_NO_CONNECTION_REAL;
-+ }
-+ db = conn->private;
-+
-+ db_stmt_p stmt = malloc(sizeof(*stmt));
-+ sqlite_vm *sqlite_stmt = 0;
-+ rc = f.sqlite_compile_fn(db, sql, 0, &sqlite_stmt, &errmsg);
-+ if (rc != SQLITE_OK)
-+ return 0;
-+ stmt->private = sqlite_stmt;
-+ *stmt_p = stmt;
-+ stmt->get_column_text = db_sqlite2_stmt_get_column_text;
-+ stmt->get_column_blob = db_sqlite2_stmt_get_column_blob;
-+ stmt->get_column_int = db_sqlite2_stmt_get_column_int;
-+ stmt->next = db_sqlite2_stmt_next;
-+ stmt->close = db_sqlite2_stmt_close;
-+ stmt->db = conn;
-+ return DB_OK;
-+}
-+
-+const char *db_sqlite2_stmt_get_column_text(db_stmt_p stmt, int column) {
-+ if (!stmt || !stmt->private) {
-+ return 0;
-+ }
-+ sqlite_vm *sqlite_stmt = stmt->private;
-+ const char **values = stmt->private2;
-+ return values[column];
-+}
-+
-+const void* db_sqlite2_stmt_get_column_blob(db_stmt_p stmt, int col) {
-+ return db_sqlite2_stmt_get_column_text(stmt, col);
-+}
-+
-+int db_sqlite2_stmt_get_column_int(db_stmt_p stmt, int column) {
-+ sqlite_vm *sqlite_stmt = stmt->private;
-+ const char **values = stmt->private2;
-+ const char *str_value = values[column];
-+ int value = 0;
-+ if (value)
-+ value = atoi(str_value);
-+ /* TODO missing way to return error */
-+ return value;
-+}
-+
-+
-+int db_sqlite2_stmt_next(db_stmt_p stmt)
-+{
-+ sqlite_vm *sqlite_stmt = stmt->private;
-+ const char **dataSQL_V, **dataSQL_N;
-+ const char **values;
-+ const char **names;
-+ int columns;
-+
-+ int rc = f.sqlite_step_fn(sqlite_stmt, &columns, &values, &names);
-+ stmt->private2 = values;
-+ /* TODO error mapping */
-+ return rc; // == SQLITE_ROW;
-+}
-+
-+int db_sqlite2_stmt_close(db_stmt_p stmt)
-+{
-+ sqlite_vm *sqlite_stmt = stmt->private;
-+ int rc = f.sqlite_finalize_fn(sqlite_stmt, &errmsg);
-+ free(stmt);
-+ return rc;
-+}
-+
-+
-+int db_sqlite2_upgrade_to_schema(int version)
-+{
-+ if (version < 0)
-+ return DB_OK;
-+
-+ if (version > 0)
-+ return DB_ERROR;
-+
-+ csync_debug(2, "Upgrading database schema to version %d.\n", version);
-+
-+ csync_db_sql("Creating file table",
-+ "CREATE TABLE file ("
-+ " filename, checktxt,"
-+ " UNIQUE ( filename ) ON CONFLICT REPLACE"
-+ ")");
-+
-+ csync_db_sql("Creating dirty table",
-+ "CREATE TABLE dirty ("
-+ " filename, forced, myname, peername,"
-+ " UNIQUE ( filename, peername ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ csync_db_sql("Creating hint table",
-+ "CREATE TABLE hint ("
-+ " filename, recursive,"
-+ " UNIQUE ( filename, recursive ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ csync_db_sql("Creating action table",
-+ "CREATE TABLE action ("
-+ " filename, command, logfile,"
-+ " UNIQUE ( filename, command ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ csync_db_sql("Creating x509_cert table",
-+ "CREATE TABLE x509_cert ("
-+ " peername, certdata,"
-+ " UNIQUE ( peername ) ON CONFLICT IGNORE"
-+ ")");
-+
-+ return DB_OK;
-+}
-+
-+
-+#endif
-diff --git a/db_sqlite2.h b/db_sqlite2.h
-new file mode 100644
-index 0000000..79336a4
---- /dev/null
-+++ b/db_sqlite2.h
-@@ -0,0 +1,18 @@
-+
-+#ifndef DB_SQLITE2_H
-+#define DB_SQLITE2_H
-+
-+/* public */
-+int db_sqlite2_open(const char *file, db_conn_p *conn_p);
-+/* Private, should not be here */
-+void db_sqlite2_close(db_conn_p db_conn);
-+int db_sqlite2_exec(db_conn_p conn, const char *sql);
-+int db_sqlite2_prepare(db_conn_p conn, const char *sql, db_stmt_p *stmt_p, char **pptail);
-+int db_sqlite2_stmt_next(db_stmt_p stmt);
-+const char* db_sqlite2_stmt_get_column_text(db_stmt_p stmt, int column);
-+const void* db_sqlite2_stmt_get_column_blob(db_stmt_p stmt, int column);
-+int db_sqlite2_stmt_get_column_int(db_stmt_p stmt, int column);
-+int db_sqlite2_stmt_close(db_stmt_p stmt);
-+int db_sqlite2_upgrade_to_schema(int version);
-+
-+#endif
-diff --git a/dl.h b/dl.h
-new file mode 100644
-index 0000000..0769b2f
---- /dev/null
-+++ b/dl.h
-@@ -0,0 +1,12 @@
-+#ifndef DL_H
-+#define DL_H
-+
-+#include <dlfcn.h>
-+
-+#define LOOKUP_SYMBOL(dl_handle, sym) \
-+ f.sym ## _fn = dlsym(dl_handle, #sym); \
-+ if ((f.sym ## _fn) == NULL) { \
-+ csync_fatal ("Could not lookup %s in shared library: %s\n", #sym, dlerror()); \
-+ }
-+
-+#endif
-diff --git a/doc/csync2_paper.tex b/doc/csync2_paper.tex
-new file mode 100644
-index 0000000..00f0de0
---- /dev/null
-+++ b/doc/csync2_paper.tex
-@@ -0,0 +1,910 @@
-+\documentclass[a4paper,twocolumn]{article}
-+\usepackage{nopageno}
-+
-+\usepackage{svn}
-+\SVNdate $Date$
-+
-+\def\csync2{{\sc Csync$^{2}$}}
-+
-+\begin{document}
-+
-+\title{Cluster synchronization with \csync2}
-+\author{Clifford Wolf, http://www.clifford.at/}
-+\maketitle
-+
-+\section{Introduction}
-+
-+\csync2 [1] is a tool for asynchronous file synchronization in clusters.
-+Asynchronous file synchronization is good for files which are seldom modified -
-+such as configuration files or application images - but it is not adequate for
-+some other types of data.
-+
-+For instance a database with continuous write accesses should be synced
-+synchronously in order to ensure the data integrity. But that does not
-+automatically mean that synchronous synchronization is better; it simply is
-+different and there are many cases where asynchronous synchronization is
-+favored over synchronous synchronization. Some pros of asynchronous
-+synchronization are:
-+
-+{\bf 1.}
-+Most asynchronous synchronization tools (including \csync2) are implemented as
-+single-shot commands which need to be executed each time in order to run one
-+synchronization cycle. Therefore it is possible to test changes on one host
-+before deploying them on the others (and also return to the old state if the
-+changes turn out to be bogus).
-+
-+{\bf 2.}
-+The synchronization algorithms are much simpler and thus less error-prone.
-+
-+{\bf 3.}
-+Asynchronous synchronization tools can be (and usually are) implemented as
-+normal user mode programs. Synchronous synchronization tools need to be
-+implemented as operating system extensions. Therefore asynchronous tools are
-+easier to deploy and more portable.
-+
-+{\bf 4.}
-+It is much easier to build systems which allow setups with many hosts and
-+complex replication rules.
-+
-+But most asynchronous synchronization tools are pretty primitive and do not
-+even cover a small portion of the issues found in real world environments.
-+
-+I have developed \csync2 because I found none of the existing tools for
-+asynchronous synchronization satisfying. The development of \csync2 has
-+been sponsored by LINBIT Information Technologies [2], the company which also
-+sponsors the synchronous block device synchronization toolchain DRBD [3].
-+
-+\hspace{0.2cm}
-+
-+Note: I will simply use the term {\it synchronization} instead of the
-+semi-oxymoron {\it asynchronous synchronization} in the rest of this paper.
-+
-+\subsection{\csync2 features}
-+
-+Most synchronization tools are very simple wrappers for remote-copy tools such
-+as {\tt rsync} or {\tt scp}. These solutions work well in most cases but
-+still leave a big gap for more sophisticated tools such as \csync2. The most
-+important features of \csync2 are described in the following sections.
-+
-+\subsubsection{Conflict detection}
-+
-+\label{confl_detect}
-+
-+Most of the trivial synchronization tools just copy the newer file over the
-+older one. This can be a very dangerous behavior if the same file has been
-+changed on more than one host. \csync2 detects such a situation as a conflict
-+and will not synchronize the file. Those conflicts then need to be resolved
-+manually by the cluster administrator.
-+
-+It is not considered as a conflict by \csync2 when the same change has been
-+performed on two hosts (e.g. because it has already been synchronized with
-+another tool).
-+
-+It is also possible to let \csync2 resolve conflicts automatically for some or
-+all files using one of the pre-defined auto-resolve methods. The available
-+methods are: {\tt none} (the default behavior), {\tt first} (the host on which
-+\csync2 is executed first wins), {\tt younger} and {\tt older} (the younger or
-+older file wins), {\tt bigger} and {\tt smaller} (the bigger or smaller file
-+wins), {\tt left} and {\tt right} (the host on the left side or the right side
-+in the host list wins).
-+
-+The {\tt younger}, {\tt older}, {\tt bigger} and {\tt smaller} methods let the
-+remote side win the conflict if the file has been removed on the local side.
-+
-+\subsubsection{Replicating file removals}
-+
-+Many synchronization tools can not synchronize file removals because they can
-+not distinguish between the file being removed on one host and being created on
-+the other one. So instead of removing the file on the second host they recreate
-+it on the first one.
-+
-+\csync2 detects file removals as such and can synchronize them correctly.
-+
-+\subsubsection{Complex setups}
-+
-+Many synchronization tools are strictly designed for two-host-setups. This is
-+an inadequate restriction and so \csync2 can handle any number of hosts.
-+
-+\csync2 can even handle complex setups where e.g. all hosts in a cluster share
-+the {\tt /etc/hosts} file, but one {\tt /etc/passwd} file is only shared among
-+the members of a small sub-group of hosts and another {\tt /etc/passwd} file is
-+shared among the other hosts in the cluster.
-+
-+\subsubsection{Reacting to updates}
-+
-+In many cases it is not enough to simply synchronize a file between cluster
-+nodes. It also is important to tell the applications using the synchronized
-+file that the underlying file has been changed, e.g. by restarting the
-+application.
-+
-+\csync2 can be configured to execute arbitrary commands when files matching an
-+arbitrary set of shell patterns are synchronized.
-+
-+\section{The \csync2 algorithm}
-+
-+Many other synchronization tools compare the hosts, try to figure out which
-+host is the most up-to-date one and then synchronize the state from this host
-+to all other hosts. This algorithm can not detect conflicts, can not
-+distinguish between file removals and file creations and therfore it is not
-+used in \csync2.
-+
-+\csync2 creates a little database with filesystem metadata on each host. This
-+database ({\tt /var/lib/csync2/{\it hostname}.db}) contains a list of the local
-+files under the control of \csync2. The database also contains information such
-+as the file modification timestamps and file sizes.
-+
-+This database is used by \csync2 to detect changes by comparison with the local
-+filesystem. The synchronization itself is then performed using the \csync2
-+protocol (TCP port 30865).
-+
-+Note that this approach implies that \csync2 can only push changes from the
-+machine on which the changes has been performed to the other machines in the
-+cluster. Running \csync2 on any other machine in the cluster can not detect and
-+so can not synchronize the changes.
-+
-+Librsync [4] is used for bandwidth-saving file synchronization and SSL is used for
-+encrypting the network traffic. The sqlite library [5] (version 2) is used for
-+managing the \csync2 database files. Authentication is performed using
-+auto-generated pre-shared-keys in combination with the peer IP address and
-+the peer SSL certificate.
-+
-+\section{Setting up \csync2}
-+
-+\subsection{Building \csync2 from source}
-+
-+Simply download the latest \csync2 source tar.gz from {\bf \tt http://oss.linbit.com/csync2/},
-+extract it and run the usual {\tt ./configure} - {\tt make} - {\tt make install} trio.
-+
-+\csync2 has a few prerequisites in addition to a C compiler, the standard
-+system libraries and headers and the usual gnu toolchain ({\tt make}, etc):
-+
-+{\bf 1.} You need librsync, libsqlite (version 2) and libssl installed
-+(including development headers).
-+
-+{\bf 2.} Bison and flex are needed to build the configuration file parser.
-+
-+\subsection{\csync2 in Linux distributions}
-+
-+As of this writing there are no official Debian, RedHat or SuSE packages for
-+\csync2. Gentoo has a \csync2 package, but is has not been updated for a year
-+now. As far as I know, ROCK Linux [6] is the only system with an up-to-date
-+\csync2 package. So I recommend that all users of non-ROCK distributions built
-+the package from source.
-+
-+The \csync2 source package contains an RPM {\tt .specs} file as well as a {\tt
-+debian/} directory. So it is possible to use {\tt rpmbuild} or {\tt debuild} to
-+build \csync2.
-+
-+\subsection{Post installation}
-+
-+Next you need to create an SSL certificate for the local \csync2 server.
-+Simply running {\tt make cert} in the \csync2 source directory will create and
-+install a self-signed SSL certificate for you. Alternatively, if you have no
-+source around, run the following commands:
-+
-+\begin{verbatim}
-+openssl genrsa \
-+ -out /etc/csync2_ssl_key.pem 1024
-+openssl req -new \
-+ -key /etc/csync2_ssl_key.pem \
-+ -out /etc/csync2_ssl_cert.csr
-+openssl x509 -req -days 600 \
-+ -in /etc/csync2_ssl_cert.csr \
-+ -signkey /etc/csync2_ssl_key.pem \
-+ -out /etc/csync2_ssl_cert.pem
-+\end{verbatim}
-+
-+You have to do that on each host you're running csync2 on. When servers are
-+talking with each other for the first time, they add each other to the database.
-+
-+The \csync2 TCP port 30865 needs to be added to the {\tt /etc/services} file and
-+inetd needs to be told about \csync2 by adding
-+
-+\begin{verbatim}
-+csync2 stream tcp nowait root \
-+ /usr/local/sbin/csync2 csync2 -i
-+\end{verbatim}
-+
-+to {\tt /etc/inetd.conf}.
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+\begin{figure*}[t]
-+ \begin{center}
-+\begin{verbatim}
-+group mygroup # A synchronization group (see 3.4.1)
-+{
-+ host host1 host2 (host3); # host list (see 3.4.2)
-+ host host4@host4-eth2;
-+
-+ key /etc/csync2.key_mygroup; # pre-shared-key (see 3.4.3)
-+
-+ include /etc/apache; # include/exclude patterns (see 3.4.4)
-+ include %homedir%/bob;
-+ exclude %homedir%/bob/temp;
-+ exclude *~ .*;
-+
-+ action # an action section (see 3.4.5)
-+ {
-+ pattern /etc/apache/httpd.conf;
-+ pattern /etc/apache/sites-available/*;
-+ exec "/usr/sbin/apache2ctl graceful";
-+ logfile "/var/log/csync2_action.log";
-+ do-local;
-+ # do-local-only;
-+ }
-+
-+ backup-directory /var/backups/csync2;
-+ backup-generations 3; # backup old files (see 3.4.11)
-+
-+ auto none; # auto resolving mode (see 3.4.6)
-+}
-+
-+prefix homedir # a prefix declaration (see 3.4.7)
-+{
-+ on host[12]: /export/users;
-+ on *: /home;
-+}
-+\end{verbatim}
-+ \end{center}
-+ \caption{Example \csync2 configuration file}
-+\end{figure*}
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+\begin{figure*}[t]
-+ \begin{center}
-+\begin{verbatim}
-+csync2 -cr /
-+if csync2 -M; then
-+ echo "!!"
-+ echo "!! There are unsynced changes! Type 'yes' if you still want to"
-+ echo "!! exit (or press crtl-c) and anything else if you want to start"
-+ echo "!! a new login shell instead."
-+ echo "!!"
-+ if read -p "Do you really want to logout? " in &&
-+ [ ".$in" != ".yes" ]; then
-+ exec bash --login
-+ fi
-+fi
-+\end{verbatim}
-+ \end{center}
-+ \caption{The {\tt csync2\_locheck.sh} script}
-+\end{figure*}
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+\subsection{Configuration File}
-+
-+Figure 1 shows a simple \csync2 configuration file. The configuration filename
-+is {\tt /etc/csync2.cfg} when no {\tt -C {\it configname}} option has been
-+passed and {\tt /etc/csync2\_{\it configname}.cfg} with a {\tt -C {\it
-+configname}} option.
-+
-+\subsubsection{Synchronization Groups}
-+
-+In the example configuration file you will find the declaration of a
-+synchronization group called {\tt mygroup}. A \csync2 setup can have any number
-+of synchronization groups. Each group has its own list of member hosts and
-+include/exclude rules.
-+
-+\csync2 automatically ignores all groups which do not contain the local
-+hostname in the host list. This way you can use one big \csync2 configuration
-+file for the entire cluster.
-+
-+\subsubsection{Host Lists}
-+
-+Host lists are specified using the {\tt host} keyword. You can eighter specify
-+the hosts in a whitespace seperated list or use an extra {\tt host} statement
-+for each host.
-+
-+The hostnames used here must be the local hostnames of the cluster nodes. That
-+means you must use exactly the same string as printed out by the {\tt hostname}
-+command. Otherwise csync2 would be unable to associate the hostnames in the
-+configuration file with the cluster nodes.
-+
-+The {\tt -N \it hostname} command line option can be used to set the local
-+hostname used by \csync2 to a different value than the one provided by the {\tt
-+hostname} command. This may be e.g. useful for environments where the local
-+hostnames are automatically set by a DHCP server and because of that change
-+often.
-+
-+Sometimes it is desired that a host is receiving \csync2 connections on an IP
-+address which is not the IP address its DNS entry resolves to, e.g.~when a
-+crossover cable is used to directly connect the hosts or an extra
-+synchronization network should be used. In this cases the syntax {\tt{\it
-+hostname}@{\it interfacename}} has to be used for the {\tt host} records (see
-+{\tt host4} in the example config file).
-+
-+Sometimes a host shall only receive updates from other hosts in the
-+synchronization group but shall not be allowed to send updates to the other
-+hosts. Such hosts (so-called {\it slave hosts}) must be specified in
-+brackets, such as {\tt host3} in the example config file.
-+
-+\subsubsection{Pre-Shared-Keys}
-+
-+Authentication is performed using the IP addresses and pre-shared-keys in
-+\csync2. Each synchronization group in the config file must have exactly one
-+{\tt key} record specifying the file containing the pre-shared-key for this
-+group. It is recommended to use a separate key for each synchronization group
-+and only place a key file on those hosts which actually are members in the
-+corresponding synchronization group.
-+
-+The key files can be generated with {\tt csync2 -k {\it filename}}.
-+
-+\subsubsection{Include/Exclude Patterns}
-+
-+The {\tt include} and {\tt exclude} patterns are used to specify which files
-+should be synced in the synchronization group.
-+
-+There are two kinds of patterns: pathname patterns which start with a slash
-+character (or a prefix such as the {\tt \%homedir\%} in the example; prefixes
-+are explained in a later section) and basename patterns which do not.
-+
-+The last matching pattern for each of both categories is chosen. If
-+both categories match, the file will be synchronized.
-+
-+The pathname patterns are matched against the beginning of the filename. So they
-+must either match the full absolute filename or must match a directory in the
-+path to the file. The file will not be synchronized if no matching {\tt include} or
-+{\tt exclude} pathname pattern is found (i.e. the default pathname pattern is
-+an exclude pattern).
-+
-+The basename patterns are matched against the base filename without the path. So
-+they can e.g. be used to include or exclude files by their filename extensions.
-+The default basename pattern is an include pattern.
-+
-+In our example config file that means that all files from {\tt /etc/apache} and
-+{\tt \%homedir\%/bob} are synced, except the dot files, files with a tilde
-+character at the end of the filename, and files from {\tt
-+\%homedir\%/bob/temp}.
-+
-+\subsubsection{Actions}
-+
-+Each synchronization group may have any number of {\tt action} sections. These
-+{\tt action} sections are used to specify shell commands which should be
-+executed after a file is synchronized that matches any of the specified
-+patterns.
-+
-+The {\tt exec} statement is used to specify the command which should be
-+executed. Note that if multiple files matching the pattern are synced in one
-+run, this command will only be executed once. The special token {\tt \%\%} in
-+the command string is substituted with the list of files which triggered the
-+command execution.
-+
-+The output of the command is appended to the specified logfile, or to
-+{\tt /dev/null} if the {\tt logfile} statement is omitted.
-+
-+Usually the action is only triggered on the targed hosts, not on the host on
-+which the file modification has been detected in the first place. The {\tt
-+do-local} statement can be used to change this behavior and let \csync2 also
-+execute the command on the host from which the modification originated. You can
-+use {\ttdo-local-only} to execute the action only on this machine.
-+
-+\subsubsection{Conflict Auto-resolving}
-+
-+The {\tt auto} statement is used to specify the conflict auto-resolving
-+mechanism for this synchronization group. The default value is {\tt auto none}.
-+
-+See section \ref{confl_detect} for a list of possible values for this setting.
-+
-+\subsubsection{Prefix Declarations}
-+
-+Prefixes (such as the {\tt \%homedir\%} prefix in the example configuration
-+file) can be used to synchronize directories which are named differently on
-+the cluster nodes. In the example configuration file the directory for the
-+user home directories is {\tt /export/users} on the hosts {\tt host1} and
-+{\tt host2} and {\tt /home} on the other hosts.
-+
-+The prefix value must be an absolute path name and must not contain any
-+wildcard characters.
-+
-+\subsubsection{The {\tt nossl} statement}
-+
-+Usually all \csync2 network communication is encrypted using SSL. This can be
-+changed with the {\tt nossl} statement. This statement may only occur in the
-+root context (not in a {\tt group} or {\tt prefix} section) and has two
-+parameters. The first one is a shell pattern matching the source DNS name for
-+the TCP connection and the second one is a shell pattern matching the
-+destination DNS name.
-+
-+So if e.g.~a secure synchronization network is used between some hosts and
-+all the interface DNS names end with {\tt -sync}, a simple
-+
-+\begin{verbatim}
-+nossl *-sync *-sync;
-+\end{verbatim}
-+
-+will disable the encryption overhead on the synchronization network. All other
-+traffic will stay SSL encrypted.
-+
-+\subsubsection{The {\tt config} statement}
-+
-+The {\tt config} statement is nothing more then an include statement and can be
-+used to include other config files. This can be used to modularize the
-+configuration file.
-+
-+\subsubsection{The {\tt ignore} statement}
-+
-+The {\tt ignore} statement can be used to tell \csync2 to not check and not sync
-+the file user-id, the file group-id and/or the file permissions. The statement
-+is only valid in the root context and accepts the parameters {\tt uid}, {\tt
-+gid} and {\tt mod} to turn off handling of user-ids, group-ids and file
-+permissions.
-+
-+\subsubsection{The {\tt tempdir} statement}
-+
-+The {\tt tempdir} statement specifies the directory to be used for temporary
-+files while receiving data through librsync. Note that internally, csync2 uses
-+a wrapper around tempnam(3), so the {\tt TMPDIR} environment variable will be
-+considered first, then the directory defined here, and if that does not work out,
-+the system default or {\tt /tmp} will be used.
-+
-+\subsubsection{The {\tt lock-timeout} statement}
-+
-+The {\tt lock-timeout} statement specifies the seconds to wait wor a database lock
-+before giving up. Default is 12 seconds. The amount will be slightly randomized
-+with a jitter of up to 6 seconds based on the respective process id.
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+\begin{figure*}[t]
-+ \begin{center}
-+\begin{verbatim}
-+CREATE TABLE file (
-+ filename, checktxt,
-+ UNIQUE ( filename ) ON CONFLICT REPLACE
-+);
-+
-+CREATE TABLE dirty (
-+ filename, force, myname, peername,
-+ UNIQUE ( filename, peername ) ON CONFLICT IGNORE
-+);
-+
-+CREATE TABLE hint (
-+ filename, recursive,
-+ UNIQUE ( filename, recursive ) ON CONFLICT IGNORE
-+);
-+
-+CREATE TABLE action (
-+ filename, command, logfile,
-+ UNIQUE ( filename, command ) ON CONFLICT IGNORE
-+);
-+
-+CREATE TABLE x509_cert (
-+ peername, certdata,
-+ UNIQUE ( peername ) ON CONFLICT IGNORE
-+);
-+\end{verbatim}
-+ \end{center}
-+ \caption{The \csync2 database schema}
-+\end{figure*}
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+\subsubsection{Backing up}
-+
-+\csync2 can back up the files it modifies. This may be useful for scenarios
-+where one is afraid of accidentally syncing files in the wrong direction.
-+
-+The {\tt backup-directory} statement is used to tell \csync2 in which directory
-+it should create the backup files and the {\tt backup-generations} statement is
-+used to tell \csync2 how many old versions of the files should be kept in the
-+backup directory.
-+
-+The files in the backup directory are named like the file they back up, with
-+all slashes substituted by underscores and a generation counter appended. Note
-+that only the file content, not the metadata such as ownership and permissions
-+are backed up.
-+
-+Per default \csync2 does not back up the files it modifies. The default
-+value for {\tt backup-generations} is {\tt 3}.
-+
-+\subsection{Activating the Logout Check}
-+
-+The \csync2 sources contain a little script called {\tt csync2\_locheck.sh}
-+(Figure 2).
-+
-+If you copy that script into your {\tt \textasciitilde/.bash\_logout} script
-+(or include it using the {\tt source} shell command), the shell will not let
-+you log out if there are any unsynced changes.
-+
-+\section{Database Schema}
-+
-+Figure 3 shows the \csync2 database schema. The database can be accessed using
-+the {\tt sqlite} command line shell. All string values are URL encoded in the
-+database.
-+
-+The {\tt file} table contains a list of all local files under \csync2 control,
-+the {\tt checktxt} attribute contains a special string with information about
-+file type, size, modification time and more. It looks like this:
-+
-+\begin{verbatim}
-+v1:mtime=1103471832:mode=33152:
-+uid=1001:gid=111:type=reg:size=301
-+\end{verbatim}
-+
-+This {\tt checktxt} attribute is used to check if a file has been changed on
-+the local host.
-+
-+If a local change has been detected, the entry in the {\tt file} table is
-+updated and entries in the {\tt dirty} table are created for all peer hosts
-+which should be updated. This way the information that a host should be updated
-+does not get lost, even if the host in question is unreachable right now. The
-+{\tt force} attribute is set to {\tt 0} by default and to {\tt 1} when the
-+cluster administrator marks one side as the right one in a synchronization
-+conflict.
-+
-+The {\tt hint} table is usually not used. In large setups this table can be
-+filled by a daemon listening on the inotify API. It is possible to tell \csync2
-+to not check all files it is responsible for but only those which have entries
-+in the {\tt hint} table. However, the Linux syscall API is so fast that this
-+only makes sense for really huge setups.
-+
-+The {\tt action} table is used for scheduling actions. Usually this table is
-+empty after \csync2 has been terminated. However, it is possible that \csync2
-+gets interrupted in the middle of the synchronization process. In this case
-+the records in the {\tt action} table are processed when \csync2 is executed
-+the next time.
-+
-+The {\tt x509\_cert} table is used to cache the SSL cetrificates used by the
-+other hosts in the csync2 cluster (like the SSH {\tt known\_hosts} file).
-+
-+\section{Running \csync2}
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+\begin{figure*}[t]
-+ \begin{center}
-+ \begin{tabular}{|p{0.5\linewidth}|p{0.5\linewidth}|}
-+ \hline
-+\begin{tiny}
-+\begin{verbatim}
-+
-+
-+csync2 1.26 - cluster synchronization tool, 2nd generation
-+LINBIT Information Technologies GmbH <http://www.linbit.com>
-+Copyright (C) 2004, 2005 Clifford Wolf <clifford@clifford.at>
-+This program is free software under the terms of the GNU GPL.
-+
-+Usage: csync2 [-v..] [-C config-name] \
-+ [-D database-dir] [-N hostname] [-p port] ..
-+
-+With file parameters:
-+ -h [-r] file.. Add (recursive) hints for check to db
-+ -c [-r] file.. Check files and maybe add to dirty db
-+ -u [-d] [-r] file.. Updates files if listed in dirty db
-+ -f file.. Force this file in sync (resolve conflict)
-+ -m file.. Mark files in database as dirty
-+
-+Simple mode:
-+ -x [-d] [[-r] file..] Run checks for all given files and update
-+ remote hosts.
-+
-+Without file parameters:
-+ -c Check all hints in db and eventually mark files as dirty
-+ -u [-d] Update (transfer dirty files to peers and mark as clear)
-+
-+ -H List all pending hints from status db
-+ -L List all file-entries from status db
-+ -M List all dirty files from status db
-+
-+ -S myname peername List file-entries from status db for this
-+ synchronization pair.
-+
-+ -T Test if everything is in sync with all peers.
-+
-+ -T filename Test if this file is in sync with all peers.
-+
-+ -T myname peername Test if this synchronization pair is in sync.
-+
-+ -T myname peer file Test only this file in this sync pair.
-+
-+ -TT As -T, but print the unified diffs.
-+
-+ The modes -H, -L, -M and -S return 2 if the requested db is empty.
-+ The mode -T returns 2 if both hosts are in sync.
-+
-+ -i Run in inetd server mode.
-+ -ii Run in stand-alone server mode.
-+ -iii Run in stand-alone server mode (one connect only).
-+
-+ -R Remove files from database which do not match config entries.
-+\end{verbatim}
-+\end{tiny}
-+
-+&
-+
-+\begin{tiny}
-+\begin{verbatim}
-+Modifiers:
-+ -r Recursive operation over subdirectories
-+ -d Dry-run on all remote update operations
-+
-+ -B Do not block everything into big SQL transactions. This
-+ slows down csync2 but allows multiple csync2 processes to
-+ access the database at the same time. Use e.g. when slow
-+ lines are used or huge files are transferred.
-+
-+ -A Open database in asynchronous mode. This will cause data
-+ corruption if the operating system crashes or the computer
-+ loses power.
-+
-+ -I Init-run. Use with care and read the documentation first!
-+ You usually do not need this option unless you are
-+ initializing groups with really large file lists.
-+
-+ -X Also add removals to dirty db when doing a -TI run.
-+ -U Don't mark all other peers as dirty when doing a -TI run.
-+
-+ -G Group1,Group2,Group3,...
-+ Only use this groups from config-file.
-+
-+ -P peer1,peer1,...
-+ Only update this peers (still mark all as dirty).
-+
-+ -F Add new entries to dirty database with force flag set.
-+
-+ -t Print timestamps to debug output (e.g. for profiling).
-+
-+Creating key file:
-+ csync2 -k filename
-+
-+Csync2 will refuse to do anything when a /etc/csync2.lock file is found.
-+\end{verbatim}
-+\end{tiny}
-+ \tabularnewline
-+ \hline
-+ \end{tabular}
-+ \end{center}
-+ \caption{The \csync2 help message}
-+\end{figure*}
-+
-+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-+
-+Simply calling {\tt csync2} without any additional arguments prints out a
-+help message (Figure 4). A more detailed description of the most
-+important usage scenarios is given in the next sections.
-+
-+\subsection{Just synchronizing the files}
-+
-+The command {\tt csync2 -x} (or {\tt csync2 -xv}) checks for local changes and
-+tries to synchronize them to the other hosts. The option {\tt -d} (dry-run) can
-+be used to do everything but the actual synchronization.
-+
-+When you start \csync2 the first time it compares its empty database with the
-+filesystem and sees that all files just have been created. It then will try
-+to synchronize the files. If the file is not present on the remote hosts it
-+will simply be copied to the other host. There also is no problem if the file
-+is already present on the remote host and has the same content. But if the
-+file already exists on the remote host and has a different content, you
-+have your first conflict.
-+
-+\subsection{Resolving a conflict}
-+
-+When two or more hosts in a \csync2 synchronization group have detected changes
-+for the same file we run into a conflict: \csync2 can not know which version is
-+the right one (unless an auto-resolving method has been specified in the
-+configuration file). The cluster administrator needs to tell \csync2 which
-+version is the correct one. This can be done using {\tt \csync2 -f}, e.g.:
-+
-+\begin{verbatim}
-+# csync2 -x
-+While syncing file /etc/hosts:
-+ERROR from peer apollo:
-+ File is also marked dirty here!
-+Finished with 1 errors.
-+
-+# csync2 -f /etc/hosts
-+# csync2 -xv
-+Connecting to host apollo (PLAIN) ...
-+Updating /etc/hosts on apollo ...
-+Finished with 0 errors.
-+\end{verbatim}
-+
-+\subsection{Checking without syncing}
-+
-+It is also possible to just check the local filesystem without doing any
-+connections to remote hosts: {\tt csync2 -cr /} (the {\tt -r} modifier
-+tells \csync2 to do a recursive check).
-+
-+{\tt csync2 -c} without any additional parameters checks all files listed
-+in the {\tt hints} table.
-+
-+The command {\tt csync2 -M} can be used to print the list of files marked dirty
-+and therfore scheduled for synchronization.
-+
-+\subsection{Comparing the hosts}
-+
-+The {\tt csync2 -T} command can be used to compare the local database with the
-+database of the remote hosts. Note that this command compares the databases and
-+not the filesystems - so make sure that the databases are up-to-date on all
-+hosts before running {\tt csync2 -T} and run {\tt csync2 -cr /} if you are
-+unsure.
-+
-+The output of {\tt csync2 -T} is a table with 4 columns:
-+
-+{\bf 1.} The type of the found difference: {\tt X} means that the file exists
-+on both hosts but is different, {\tt L} that the file is only present on the
-+local host and {\tt R} that the file is only present on the remote host.
-+
-+{\bf 2.} The local interface DNS name (usually just the local hostname).
-+
-+{\bf 3.} The remote interface DNS name (usually just the remote hostname).
-+
-+{\bf 4.} The filename.
-+
-+The {\tt csync2 -TT {\it filename}} command can be used for displaying unified
-+diffs between a local file and the remote hosts.
-+
-+\subsection{Bootstrapping large setups}
-+
-+The {\tt -I} option is a nice tool for bootstrapping larger \csync2
-+installations on slower networks. In such scenarios one usually wants to
-+initially replicate the data using a more efficient way and then use \csync2 to
-+synchronize the changes on a regular basis.
-+
-+The problem here is that when you start \csync2 the first time it detects a lot
-+of newly created files and wants to synchronize them, just to find out that
-+they are already in sync with the peers.
-+
-+The {\tt -I} option modifies the behavior of {\tt -c} so it only updates the
-+{\tt file} table but does not create entries in the {\tt dirty} table. So you
-+can simply use {\tt csync2 -cIr /} to initially create the \csync2 database on
-+the cluster nodes when you know for sure that the hosts are already in sync.
-+
-+The {\tt -I} option may also be used with {\tt -T} to add the detected
-+differences to the dirty table and so induce \csync2 to synchronize the local
-+status of the files in question to the remote host.
-+
-+Usually {\tt -TI} does only schedule local files which do exist to the dirty
-+database. That means that it does not induce \csync2 to remove a file on a
-+remote host if it does not exist on the local host. That behavior can be
-+changed using the {\tt -X} option.
-+
-+The files scheduled to be synced by {\tt -TI} are usually scheduled to be
-+synced to all peers, not just the one peer which has been used in the {\tt -TI}
-+run. This behavior can be changed using the {\tt -U} option.
-+
-+\subsection{Cleaning up the database}
-+
-+It can happen that old data is left over in the \csync2 database after a
-+configuration change (e.g. files and hosts which are not referred anymore
-+by the configuration file). Running {\tt csync2 -R} cleans up such old
-+entries in the \csync2 database.
-+
-+\subsection{Multiple Configurations}
-+
-+Sometimes a higher abstracion level than simply having different
-+synchronization groups is needed. For such cases it is possible to use multiple
-+configuration files (and databases) side by side.
-+
-+The additional configurations must have a unique name. The configuration file
-+is then named {\tt /etc/csync2\_{\it myname}.cfg} and the database is named
-+{\tt /var/lib/csync2/{\it hostname}\_{\it myname}.db}. Accordingly \csync2 must
-+be called with the {\tt -C {\it myname}} option.
-+
-+But there is no need for multiple \csync2 daemons. The \csync2 protocol allows
-+the client to tell the server which configuration should be used for the
-+current TCP connection.
-+
-+\section{Performance}
-+
-+In most cases \csync2 is used for syncing just some (up to a few hundred) system
-+configuration files. In these cases all \csync2 calls are processed in less than
-+one second, even on slow hardware. So a performance analysis is not interesting
-+for these cases but only for setups where a huge amount of files is synced,
-+e.g. when syncing entire application images with \csync2.
-+
-+A well-founded performance analysis which would allow meaningful comparisons
-+with other synchronization tools would be beyond the scope of this paper.
-+So here are just some quick and dirty numbers from a production
-+2-node cluster (2.40GHz dual-Xeon, 7200 RPM ATA HD, 1 GB Ram). The machines
-+had an average load of 0.3 (web and mail) during my tests..
-+
-+I have about 128.000 files (1.7 GB) of Linux kernel sources and object
-+files on an ext3 filesystem under \csync2 control on the machines.
-+
-+Checking for changes ({\tt csync2 -cr /}) took 13.7 seconds wall clock time,
-+9.1 seconds in user mode and 4.1 seconds in kernel mode. The remaining 0.5
-+seconds were spent in other processes.
-+
-+Recreating the local database without adding the files to dirty table ({\tt
-+csync2 -cIr} after removing the database file) took 28.5 seconds (18.6 sec
-+user mode and 2.6 sec kernel mode).
-+
-+Comparing the \csync2 databases of both hosts ({\tt csync2 -T}) took 3 seconds
-+wall clock time.
-+
-+Running {\tt csync2 -u} after adding all 128.000 files took 10 minutes wall
-+clock time. That means that \csync2 tried to sync all 128.000 files and then
-+recognized that the remote side had already the most up-to-date version of
-+the file after comparing the checksums.
-+
-+All numbers are the average values of 10 iterations.
-+
-+\section{Security Notes}
-+
-+As statet earlier, authentication is performed using the peer IP address and a
-+pre-shared-key. The traffic is SSL encrypted and the SSL certificate of the
-+peer is checked when there has been already an SSL connection to that peer in
-+the past (i.e.~the peer certificate is already cached in the database).
-+
-+All DNS names used in the \csync2 configuration file (the {\tt host} records)
-+should be resolvable via the {\tt /etc/hosts} file to guard against DNS
-+spoofing attacks.
-+
-+Depending on the list of files being managed by \csync2, an intruder on one of
-+the cluster nodes can also modify the files under \csync2 control on the other
-+cluster nodes and so might also gain access on them. However, an intruder can
-+not modify any other files on the other hosts because \csync2 checks on the
-+receiving side if all updates are OK according to the configuration file.
-+
-+For sure, an intruder would be able to work around this security checks when
-+\csync2 is also used to sync the \csync2 configuration files.
-+
-+\csync2 only syncs the standard UNIX permissions (uid, gid and file mode).
-+ACLs, Linux ext2fs/ext3fs attributes and other extended filesystem permissions
-+are neither synced nor flushed (e.g. if they are set automatically when
-+the file is created).
-+
-+\section{Alternatives}
-+
-+\csync2 is not the only file synchronization tool. Some of the other
-+free software file synchronization tools are:
-+
-+\subsection{Rsync}
-+
-+Rsync [7] is a tool for fast incremental file transfers, but is not a
-+synchronization tool in the context of this paper. Actually \csync2 is
-+using the rsync algorithm for file transfers. A variety of synchronization
-+tools have been written on top of rsync. Most of them are tiny shell scripts.
-+
-+\subsection{Unison}
-+
-+Unison [8] is using an algorithm similar to the one used by \csync2, but is
-+limited to two-host setups. Its focus is on interactive syncs (there even are
-+graphical user interfaces) and it is targeting on syncing home directories
-+between a laptop and a workstation. Unison is pretty intuitive to use, among
-+other things because of its limitations.
-+
-+\subsection{Version Control Systems}
-+
-+Version control systems such as Subversion [9] can also be used to synchronize
-+configuration files or application images. The advantage of version control
-+systems is that they can do three way merges and preserve the entire history
-+of a repository. The disadvantage is that they are much slower and require more
-+disk space than plain synchronization tools.
-+
-+\section{References}
-+
-+{[1]} \csync2 \\
-+http://oss.linbit.com/csync2/
-+\medskip \\
-+{[2]} LINBIT Information Technologies \\
-+http://www.linbit.com/
-+\medskip \\
-+{[3]} DRBD \\
-+http://www.drbd.org/
-+\medskip \\
-+{[4]} Librsync \\
-+http://librsync.sourceforge.net/
-+\medskip \\
-+{[5]} SQLite \\
-+http://www.sqlite.org/
-+\medskip \\
-+{[6]} ROCK Linux \\
-+http://www.rocklinux.org/
-+\medskip \\
-+{[7]} Rsync \\
-+http://samba.anu.edu.au/rsync/
-+\medskip \\
-+{[8]} Unison \\
-+http://www.cis.upenn.edu/\textasciitilde{}bcpierce/unison/
-+\medskip \\
-+{[9]} Subversion \\
-+http://subversion.tigris.org/
-+
-+\end{document}
-diff --git a/error.c b/error.c
-index 26f04b2..82f2f3f 100644
---- a/error.c
-+++ b/error.c
-@@ -26,6 +26,7 @@
- #include <time.h>
- #include <sys/types.h>
- #include <unistd.h>
-+#include <syslog.h>
-
- long csync_last_printtime = 0;
- FILE *csync_timestamp_out = 0;
-@@ -117,20 +118,29 @@ void csync_debug(int lv, const char *fmt, ...)
- {
- va_list ap;
-
-- csync_printtime();
--
- if ( csync_debug_level < lv ) return;
-
-- if (csync_timestamps)
-- csync_printtime_prefix();
-+ if (!csync_syslog) {
-+ csync_printtime();
-+
-+ if (csync_timestamps)
-+ csync_printtime_prefix();
-
-- if ( csync_server_child_pid )
-- fprintf(csync_debug_out, "<%d> ", csync_server_child_pid);
--
-- va_start(ap, fmt);
-- vfprintf(csync_debug_out, fmt, ap);
-- va_end(ap);
-+ if ( csync_server_child_pid )
-+ fprintf(csync_debug_out, "<%d> ", csync_server_child_pid);
-
-+ va_start(ap, fmt);
-+ vfprintf(csync_debug_out, fmt, ap);
-+ va_end(ap);
-+ // Good / bad with extra line
-+ fprintf(csync_debug_out,"\n");
-+ }
-+ else {
-+ va_start(ap,fmt);
-+ vsyslog(LOG_DEBUG, fmt, ap);
-+ va_end(ap);
-+ }
- csync_messages_printed++;
- }
-
-+/* Test 3 */
-diff --git a/getrealfn.c b/getrealfn.c
-index 01d13ce..b2bc0b7 100644
---- a/getrealfn.c
-+++ b/getrealfn.c
-@@ -53,8 +53,11 @@ char *getrealfn(const char *filename)
- /* make the path absolute */
- if ( *tempfn != '/' ) {
- char *t2, *t1 = my_get_current_dir_name();
-- asprintf(&t2, "%s/%s", t1, tempfn);
-- free(t1); free(tempfn); tempfn = t2;
-+
-+ ASPRINTF(&t2, "%s/%s", t1, tempfn);
-+ free(t1);
-+ free(tempfn);
-+ tempfn = t2;
- }
-
- /* remove leading slashes from tempfn */
-@@ -108,7 +111,7 @@ char *getrealfn(const char *filename)
- if ( !chdir(tempfn) ) {
- char *t2, *t1 = my_get_current_dir_name();
- if ( st_mark ) {
-- asprintf(&t2, "%s/%s", t1, st_mark+1);
-+ ASPRINTF(&t2, "%s/%s", t1, st_mark+1);
- free(tempfn); free(t1); tempfn = t2;
- } else {
- free(tempfn); tempfn = t1;
-diff --git a/groups.c b/groups.c
-index 1ff9a1a..511586e 100644
---- a/groups.c
-+++ b/groups.c
-@@ -41,8 +41,9 @@ int match_pattern_list(
- matched = 1;
- }
- } else {
-+ int fnm_pathname = p->star_matches_slashes ? 0 : FNM_PATHNAME;
- if ( !fnmatch(p->pattern, filename,
-- FNM_LEADING_DIR|FNM_PATHNAME) ) {
-+ FNM_LEADING_DIR|fnm_pathname) ) {
- match_path = p->isinclude;
- matched = 1;
- }
-@@ -91,10 +92,11 @@ int csync_step_into(const char *file)
- continue;
- if ( (p->pattern[0] == '/' || p->pattern[0] == '%') && p->isinclude ) {
- char t[strlen(p->pattern)+1], *l;
-+ int fnm_pathname = p->star_matches_slashes ? 0 : FNM_PATHNAME;
- strcpy(t, p->pattern);
- while ( (l=strrchr(t, '/')) != 0 ) {
- *l = 0;
-- if ( !fnmatch(t, file, FNM_PATHNAME) )
-+ if ( !fnmatch(t, file, fnm_pathname) )
- return 1;
- }
- }
-diff --git a/prefixsubst.c b/prefixsubst.c
-index 6adedd4..d003bb5 100644
---- a/prefixsubst.c
-+++ b/prefixsubst.c
-@@ -46,7 +46,7 @@ const char *prefixsubst(const char *in)
- ringbuff_counter = (ringbuff_counter+1) % RINGBUFF_LEN;
- if (ringbuff[ringbuff_counter])
- free(ringbuff[ringbuff_counter]);
-- asprintf(&ringbuff[ringbuff_counter], "%s%s", p->path, path);
-+ ASPRINTF(&ringbuff[ringbuff_counter], "%s%s", p->path, path);
- return ringbuff[ringbuff_counter];
- }
- }
-@@ -56,3 +56,35 @@ const char *prefixsubst(const char *in)
- return 0;
- }
-
-+const char *prefixencode(const char *filename) {
-+#if __CYGWIN__
-+ if (!strcmp(filename, "/")) {
-+ filename = "/cygdrive";
-+ }
-+#endif
-+ struct csync_prefix *p = csync_prefix;
-+
-+ /*
-+ * Canonicalized paths will always contain /
-+ * Prefixsubsted paths will probably contain %
-+ */
-+ if (*filename == '/')
-+ while (p) {
-+ if (p->path) {
-+ int p_len = strlen(p->path);
-+ int f_len = strlen(filename);
-+
-+ if (p_len <= f_len && !strncmp(p->path, filename, p_len) &&
-+ (filename[p_len] == '/' || !filename[p_len])) {
-+ ringbuff_counter = (ringbuff_counter+1) % RINGBUFF_LEN;
-+ if (ringbuff[ringbuff_counter])
-+ free(ringbuff[ringbuff_counter]);
-+ ASPRINTF(&ringbuff[ringbuff_counter], "%%%s%%%s", p->name, filename+p_len);
-+ return ringbuff[ringbuff_counter];
-+ }
-+ }
-+ p = p->next;
-+ }
-+ return filename;
-+}
-+
-diff --git a/release.sh b/release.sh
-new file mode 100755
-index 0000000..8ee447c
---- /dev/null
-+++ b/release.sh
-@@ -0,0 +1,69 @@
-+#!/bin/bash
-+#
-+# csync2 - cluster synchronization tool, 2nd generation
-+# LINBIT Information Technologies GmbH <http://www.linbit.com>
-+# Copyright (C) 2004, 2005 Clifford Wolf <clifford@clifford.at>
-+#
-+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-+#
-+#
-+# Internal script for tagging a release in subversion and creating
-+# the source tar file.
-+
-+PACKAGE=csync2
-+URL=http://svn.linbit.com/csync2
-+
-+case "$1" in
-+ -*)
-+ echo "Usage: $0 newversion"
-+ ;;
-+ '')
-+ svn ls $URL/tags | tr -d / | perl -pe '$x=$_; $x=~s/\n/\t/; print $x;
-+ s/(\d+)/sprintf"%04d",$1/eg;' | sort -k2 | cut -f1
-+ ;;
-+ *)
-+ VERSION=$1
-+ set -ex
-+
-+ date "+csync2 ($VERSION-1) unstable; urgency=low%n%n`
-+ ` * New Upstream Version.%n%n -- Clifford Wolf `
-+ `<clifford.wolf@linbit.com> %a, %d %b %Y `
-+ `%H:%M:%S %z%n" > debian/changelog.new
-+ cat debian/changelog >> debian/changelog.new
-+ mv debian/changelog.new debian/changelog
-+ svn commit -m "Added version $VERSION to debian changelog." \
-+ debian/changelog
-+
-+ svn cp -m "Tagged version $VERSION" \
-+ $URL/trunk $URL/tags/$PACKAGE-$VERSION
-+ svn co $URL/tags/$PACKAGE-$VERSION ../$PACKAGE-$VERSION
-+
-+ cd ../$PACKAGE-$VERSION
-+ svn rm release.sh copycheck.sh
-+ perl -pi -e "s/SNAPSHOT/$VERSION/g" configure.ac
-+ perl -pi -e "s/SNAPSHOT/$VERSION/g" csync2.spec
-+ svn commit -m "Fixed version info in tag $VERSION"
-+
-+ sleep 2
-+ wget -O paper.pdf http://www.clifford.at/papers/2005/csync2/paper.pdf
-+ ./autogen.sh; rm -rf autom4te.cache debian/ $( find -name .svn )
-+
-+ cd ..
-+ tar cvzf $PACKAGE-$VERSION.tar.gz \
-+ --owner=0 --group=0 $PACKAGE-$VERSION
-+ rm -rf $PACKAGE-$VERSION
-+ ;;
-+esac
-+
-diff --git a/rsync.c b/rsync.c
-index e4a918c..86482ee 100644
---- a/rsync.c
-+++ b/rsync.c
-@@ -25,10 +25,188 @@
- #include <errno.h>
- #include <stdio.h>
-
-+/* for tmpfile replacement: */
-+#include <sys/types.h>
-+#include <sys/stat.h>
-+#include <fcntl.h>
-+
-+/* for MAXPATHLEN */
-+#include <sys/param.h>
-+
-+
- #ifdef __CYGWIN__
- #include <w32api/windows.h>
- #endif
-
-+
-+/* This has been taken from rsync:lib/compat.c */
-+
-+/**
-+ * Like strncpy but does not 0 fill the buffer and always null
-+ * terminates.
-+ *
-+ * @param bufsize is the size of the destination buffer.
-+ *
-+ * @return index of the terminating byte.
-+ **/
-+static size_t strlcpy(char *d, const char *s, size_t bufsize)
-+{
-+ size_t len = strlen(s);
-+ size_t ret = len;
-+ if (bufsize > 0) {
-+ if (len >= bufsize)
-+ len = bufsize-1;
-+ memcpy(d, s, len);
-+ d[len] = 0;
-+ }
-+ return ret;
-+}
-+
-+
-+/* This has been taken from rsync sources: receiver.c */
-+
-+#define TMPNAME_SUFFIX ".XXXXXX"
-+#define TMPNAME_SUFFIX_LEN ((int)sizeof TMPNAME_SUFFIX - 1)
-+#define MAX_UNIQUE_NUMBER 999999
-+#define MAX_UNIQUE_LOOP 100
-+
-+/* get_tmpname() - create a tmp filename for a given filename
-+ *
-+ * If a tmpdir is defined, use that as the directory to put it in. Otherwise,
-+ * the tmp filename is in the same directory as the given name. Note that
-+ * there may be no directory at all in the given name!
-+ *
-+ * The tmp filename is basically the given filename with a dot prepended, and
-+ * .XXXXXX appended (for mkstemp() to put its unique gunk in). We take care
-+ * to not exceed either the MAXPATHLEN or NAME_MAX, especially the last, as
-+ * the basename basically becomes 8 characters longer. In such a case, the
-+ * original name is shortened sufficiently to make it all fit.
-+ *
-+ * If the make_unique arg is True, the XXXXXX string is replaced with a unique
-+ * string that doesn't exist at the time of the check. This is intended to be
-+ * used for creating hard links, symlinks, devices, and special files, since
-+ * normal files should be handled by mkstemp() for safety.
-+ *
-+ * Of course, the only reason the file is based on the original name is to
-+ * make it easier to figure out what purpose a temp file is serving when a
-+ * transfer is in progress. */
-+
-+static int get_tmpname(char *fnametmp, const char *fname)
-+{
-+ int maxname, added, length = 0;
-+ const char *f;
-+ char *suf;
-+
-+ static unsigned counter_limit;
-+ unsigned counter;
-+
-+ if ((f = strrchr(fname, '/')) != NULL) {
-+ ++f;
-+ length = f - fname;
-+ /* copy up to and including the slash */
-+ strlcpy(fnametmp, fname, length + 1);
-+ } else
-+ f = fname;
-+ fnametmp[length++] = '.';
-+
-+ /* The maxname value is bufsize, and includes space for the '\0'.
-+ * NAME_MAX needs an extra -1 for the name's leading dot. */
-+ maxname = MIN(MAXPATHLEN - length - TMPNAME_SUFFIX_LEN,
-+ NAME_MAX - 1 - TMPNAME_SUFFIX_LEN);
-+
-+ if (maxname < 1) {
-+ csync_debug(1, "temporary filename too long: %s\n", fname);
-+ fnametmp[0] = '\0';
-+ return 0;
-+ }
-+
-+ added = strlcpy(fnametmp + length, f, maxname);
-+ if (added >= maxname)
-+ added = maxname - 1;
-+ suf = fnametmp + length + added;
-+
-+ if (!counter_limit) {
-+ counter_limit = (unsigned)getpid() + MAX_UNIQUE_LOOP;
-+ if (counter_limit > MAX_UNIQUE_NUMBER || counter_limit < MAX_UNIQUE_LOOP)
-+ counter_limit = MAX_UNIQUE_LOOP;
-+
-+ counter = counter_limit - MAX_UNIQUE_LOOP;
-+
-+ /* This doesn't have to be very good because we don't need
-+ * to worry about someone trying to guess the values: all
-+ * a conflict will do is cause a device, special file, hard
-+ * link, or symlink to fail to be created. Also: avoid
-+ * using mktemp() due to gcc's annoying warning. */
-+ while (1) {
-+ snprintf(suf, TMPNAME_SUFFIX_LEN+1, ".%d", counter);
-+ if (access(fnametmp, 0) < 0)
-+ break;
-+ if (++counter >= counter_limit)
-+ return 0;
-+ }
-+ } else
-+ memcpy(suf, TMPNAME_SUFFIX, TMPNAME_SUFFIX_LEN+1);
-+
-+ return 1;
-+}
-+
-+
-+/* Returns open file handle for a temp file that resides in the
-+ same directory as file fname. The file must be removed after
-+ usage.
-+*/
-+
-+static FILE *open_temp_file(char *fnametmp, const char *fname)
-+{
-+ FILE *f;
-+ int fd;
-+
-+ if (get_tmpname(fnametmp, fname) == 0) {
-+ csync_debug(1, "ERROR: Couldn't find tempname for file %s\n", fname);
-+ return NULL;
-+ }
-+
-+ f = NULL;
-+ fd = open(fnametmp, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR);
-+ if (fd >= 0) {
-+ f = fdopen(fd, "wb+");
-+ /* not unlinking since rename wouldn't work then */
-+ }
-+ if (fd < 0 || !f) {
-+ csync_debug(1, "ERROR: Could not open result from tempnam(%s)!\n", fnametmp);
-+ return NULL;
-+ }
-+
-+ return f;
-+}
-+
-+
-+
-+#ifdef _SVID_SOURCE
-+static FILE *paranoid_tmpfile()
-+{
-+ char *name;
-+ FILE *f;
-+ int fd;
-+
-+ name = tempnam(csync_tempdir, "csync2");
-+ if (!name)
-+ csync_fatal("ERROR: tempnam() didn't return a valid filename!\n");
-+
-+ f = NULL;
-+ fd = open(name, O_CREAT | O_EXCL | O_RDWR, S_IWUSR | S_IRUSR);
-+ if (fd >= 0) {
-+ f = fdopen(fd, "wb+");
-+ unlink(name);
-+ }
-+ if (fd < 0 || !f)
-+ csync_fatal("ERROR: Could not open result from tempnam(%s)!\n", name);
-+
-+ csync_debug(3, "Tempfilename is %s\n", name);
-+ free(name);
-+ return f;
-+}
-+#else
- static FILE *paranoid_tmpfile()
- {
- FILE *f;
-@@ -41,6 +219,7 @@ static FILE *paranoid_tmpfile()
-
- return f;
- }
-+#endif
-
- void csync_send_file(FILE *in)
- {
-@@ -119,18 +298,23 @@ int csync_rs_check(const char *filename, int isreg)
- rs_stats_t stats;
- rs_result result;
- long size;
-+ char tmpfname[MAXPATHLEN];
-
- csync_debug(3, "Csync2 / Librsync: csync_rs_check('%s', %d [%s])\n",
- filename, isreg, isreg ? "regular file" : "non-regular file");
-
- csync_debug(3, "Opening basis_file and sig_file..\n");
-
-- sig_file = paranoid_tmpfile();
-+ sig_file = open_temp_file(tmpfname, prefixsubst(filename));
- if ( !sig_file ) goto io_error;
-+ if (unlink(tmpfname) < 0) goto io_error;
-
- basis_file = fopen(prefixsubst(filename), "rb");
-- if ( !basis_file ) basis_file = paranoid_tmpfile();
-- if ( !basis_file ) goto io_error;
-+ if ( !basis_file ) { /* ?? why a tmp file? */
-+ basis_file = open_temp_file(tmpfname, prefixsubst(filename));
-+ if ( !basis_file ) goto io_error;
-+ if (unlink(tmpfname) < 0) goto io_error;
-+ }
-
- if ( isreg ) {
- csync_debug(3, "Running rs_sig_file() from librsync....\n");
-@@ -204,14 +388,19 @@ error:;
-
- void csync_rs_sig(const char *filename)
- {
-- FILE *basis_file, *sig_file;
-+ FILE *basis_file = 0, *sig_file = 0;
- rs_stats_t stats;
- rs_result result;
-+ char tmpfname[MAXPATHLEN];
-
- csync_debug(3, "Csync2 / Librsync: csync_rs_sig('%s')\n", filename);
-
- csync_debug(3, "Opening basis_file and sig_file..\n");
-- sig_file = paranoid_tmpfile();
-+
-+ sig_file = open_temp_file(tmpfname, prefixsubst(filename));
-+ if ( !sig_file ) goto io_error;
-+ if (unlink(tmpfname) < 0) goto io_error;
-+
- basis_file = fopen(prefixsubst(filename), "rb");
- if ( !basis_file ) basis_file = fopen("/dev/null", "rb");
-
-@@ -227,19 +416,34 @@ void csync_rs_sig(const char *filename)
- csync_debug(3, "Signature has been created successfully.\n");
- fclose(basis_file);
- fclose(sig_file);
-+
-+ return;
-+
-+io_error:
-+ csync_debug(0, "I/O Error '%s' in rsync-sig: %s\n",
-+ strerror(errno), prefixsubst(filename));
-+
-+ if (basis_file) fclose(basis_file);
-+ if (sig_file) fclose(sig_file);
- }
-
-+
-+
- int csync_rs_delta(const char *filename)
- {
-- FILE *sig_file, *new_file, *delta_file;
-+ FILE *sig_file = 0, *new_file = 0, *delta_file = 0;
- rs_result result;
- rs_signature_t *sumset;
- rs_stats_t stats;
-+ char tmpfname[MAXPATHLEN];
-
- csync_debug(3, "Csync2 / Librsync: csync_rs_delta('%s')\n", filename);
-
- csync_debug(3, "Receiving sig_file from peer..\n");
-- sig_file = paranoid_tmpfile();
-+ sig_file = open_temp_file(tmpfname, prefixsubst(filename));
-+ if ( !sig_file ) goto io_error;
-+ if (unlink(tmpfname) < 0) goto io_error;
-+
- if ( csync_recv_file(sig_file) ) {
- fclose(sig_file);
- return -1;
-@@ -260,7 +464,10 @@ int csync_rs_delta(const char *filename)
- errno = backup_errno;
- return -1;
- }
-- delta_file = paranoid_tmpfile();
-+
-+ delta_file = open_temp_file(tmpfname, prefixsubst(filename));
-+ if ( !delta_file ) goto io_error;
-+ if (unlink(tmpfname) < 0) goto io_error;
-
- csync_debug(3, "Running rs_build_hash_table() from librsync..\n");
- result = rs_build_hash_table(sumset);
-@@ -281,6 +488,16 @@ int csync_rs_delta(const char *filename)
- fclose(new_file);
-
- return 0;
-+
-+io_error:
-+ csync_debug(0, "I/O Error '%s' in rsync-delta: %s\n",
-+ strerror(errno), prefixsubst(filename));
-+
-+ if (new_file) fclose(new_file);
-+ if (delta_file) fclose(delta_file);
-+ if (sig_file) fclose(sig_file);
-+
-+ return -1;
- }
-
- int csync_rs_patch(const char *filename)
-@@ -289,24 +506,27 @@ int csync_rs_patch(const char *filename)
- int backup_errno;
- rs_stats_t stats;
- rs_result result;
-- char buffer[512];
- char *errstr = "?";
-- int rc;
-+ char tmpfname[MAXPATHLEN], newfname[MAXPATHLEN];
-
- csync_debug(3, "Csync2 / Librsync: csync_rs_patch('%s')\n", filename);
-
- csync_debug(3, "Receiving delta_file from peer..\n");
-- delta_file = paranoid_tmpfile();
-+ delta_file = open_temp_file(tmpfname, prefixsubst(filename));
- if ( !delta_file ) { errstr="creating delta temp file"; goto io_error; }
-+ if (unlink(tmpfname) < 0) { errstr="removing delta temp file"; goto io_error; }
- if ( csync_recv_file(delta_file) ) goto error;
-
- csync_debug(3, "Opening to be patched file on local host..\n");
- basis_file = fopen(prefixsubst(filename), "rb");
-- if ( !basis_file ) basis_file = paranoid_tmpfile();
-- if ( !basis_file ) { errstr="opening data file for reading"; goto io_error; }
-+ if ( !basis_file ) {
-+ basis_file = open_temp_file(tmpfname, prefixsubst(filename));
-+ if ( !basis_file ) { errstr="opening data file for reading"; goto io_error; }
-+ if (unlink(tmpfname) < 0) { errstr="removing data temp file"; goto io_error; }
-+ }
-
- csync_debug(3, "Opening temp file for new data on local host..\n");
-- new_file = paranoid_tmpfile();
-+ new_file = open_temp_file(newfname, prefixsubst(filename));
- if ( !new_file ) { errstr="creating new data temp file"; goto io_error; }
-
- csync_debug(3, "Running rs_patch_file() from librsync..\n");
-@@ -316,12 +536,12 @@ int csync_rs_patch(const char *filename)
- goto error;
- }
-
-- csync_debug(3, "Copying new data to local file..\n");
-+ csync_debug(3, "Renaming tmp file to data file..\n");
- fclose(basis_file);
-- rewind(new_file);
-- unlink(prefixsubst(filename));
-
- #ifdef __CYGWIN__
-+
-+/* TODO: needed? */
- // This creates the file using the native windows API, bypassing
- // the cygwin wrappers and so making sure that we do not mess up the
- // permissions..
-@@ -350,14 +570,9 @@ int csync_rs_patch(const char *filename)
- }
- #endif
-
-- basis_file = fopen(prefixsubst(filename), "wb");
-- if ( !basis_file ) { errstr="opening data file for writing"; goto io_error; }
--
-- while ( (rc = fread(buffer, 1, 512, new_file)) > 0 )
-- fwrite(buffer, rc, 1, basis_file);
-+ if (rename(newfname, prefixsubst(filename)) < 0) { errstr="renaming tmp file to to be patched file"; goto io_error; }
-
- csync_debug(3, "File has been patched successfully.\n");
-- fclose(basis_file);
- fclose(delta_file);
- fclose(new_file);
-
-diff --git a/update.c b/update.c
-index 7c55113..b2c2b85 100644
---- a/update.c
-+++ b/update.c
-@@ -44,7 +44,9 @@ int read_conn_status(const char *file, const char *host)
- }
- if ( file )
- csync_debug(0, "While syncing file %s:\n", file);
-- csync_debug(0, "ERROR from peer %s: %s", host, line);
-+ else
-+ file = "<no file>";
-+ csync_debug(0, "ERROR from peer(%s): %s %s", file, host, line);
- csync_error_count++;
- return !strcmp(line, "File is also marked dirty here!") ? 1 : 2;
- }
-@@ -70,7 +72,7 @@ int connect_to_host(const char *peername)
- if ( conn_open(peername) ) return -1;
-
- if ( use_ssl ) {
--#if HAVE_LIBGNUTLS_OPENSSL
-+#if HAVE_LIBGNUTLS
- conn_printf("SSL\n");
- if ( read_conn_status(0, peername) ) {
- csync_debug(1, "SSL command failed.\n");
-@@ -196,7 +198,8 @@ auto_resolve_entry_point:
- csync_debug(1, "File is already up to date on peer.\n");
- if ( dry_run ) {
- printf("?S: %-15s %s\n", peername, filename);
-- return;
-+ // DS Remove local dirty, even in dry run
-+ // return;
- }
- goto skip_action;
- }
-@@ -332,7 +335,8 @@ auto_resolve_entry_point:
- csync_debug(1, "File is already up to date on peer.\n");
- if ( dry_run ) {
- printf("?S: %-15s %s\n", peername, filename);
-- return;
-+ // DS also skip on dry_run
-+ // return;
- }
- goto skip_action;
- }
-@@ -540,17 +544,16 @@ void csync_update_host(const char *peername,
- struct textlist *tl_mod = 0, **last_tn=&tl;
- char *current_name = 0;
- struct stat st;
--
- SQL_BEGIN("Get files for host from dirty table",
-- "SELECT filename, myname, force FROM dirty WHERE peername = '%s' "
-+ "SELECT filename, myname, forced FROM dirty WHERE peername = '%s' "
- "ORDER by filename ASC", url_encode(peername))
- {
-- const char *filename = url_decode(SQL_V[0]);
-+ const char *filename = url_decode(SQL_V(0));
- int i, use_this = patnum == 0;
- for (i=0; i<patnum && !use_this; i++)
- if ( compare_files(filename, patlist[i], recursive) ) use_this = 1;
- if (use_this)
-- textlist_add2(&tl, filename, url_decode(SQL_V[1]), atoi(SQL_V[2]));
-+ textlist_add2(&tl, filename, url_decode(SQL_V(1)), atoi(SQL_V(2)));
- } SQL_END;
-
- /* just return if there are no files to update */
-@@ -558,7 +561,7 @@ void csync_update_host(const char *peername,
-
- if ( connect_to_host(peername) ) {
- csync_error_count++;
-- csync_debug(0, "ERROR: Connection to remote host failed.\n");
-+ csync_debug(0, "ERROR: Connection to remote host `%s' failed.\n", peername);
- csync_debug(1, "Host stays in dirty state. "
- "Try again later...\n");
- return;
-@@ -584,6 +587,7 @@ void csync_update_host(const char *peername,
- t->next = tl_mod;
- tl_mod = t;
- } else {
-+ csync_debug(3, "Dirty item %s %s %d \n", t->value, t->value2, t->intvalue);
- if ( !current_name || strcmp(current_name, t->value2) ) {
- conn_printf("HELLO %s\n", url_encode(t->value2));
- if ( read_conn_status(t->value, peername) )
-@@ -600,6 +604,7 @@ ident_failed_1:
-
- for (t = tl_mod; t != 0; t = t->next) {
- if ( !current_name || strcmp(current_name, t->value2) ) {
-+ csync_debug(3, "Dirty item %s %s %d ", t->value, t->value2, t->intvalue);
- conn_printf("HELLO %s\n", url_encode(t->value2));
- if ( read_conn_status(t->value, peername) )
- goto ident_failed_2;
-@@ -624,9 +629,9 @@ void csync_update(const char ** patlist, int patnum, int recursive, int dry_run)
- struct textlist *tl = 0, *t;
-
- SQL_BEGIN("Get hosts from dirty table",
-- "SELECT peername FROM dirty GROUP BY peername ORDER BY random()")
-+ "SELECT peername FROM dirty GROUP BY peername")
- {
-- textlist_add(&tl, url_decode(SQL_V[0]), 0);
-+ textlist_add(&tl, url_decode(SQL_V(0)), 0);
- } SQL_END;
-
- for (t = tl; t != 0; t = t->next) {
-@@ -672,7 +677,7 @@ found_host_check:
-
- if ( connect_to_host(peername) ) {
- csync_error_count++;
-- csync_debug(0, "ERROR: Connection to remote host failed.\n");
-+ csync_debug(0, "ERROR: Connection to remote host `%s' failed.\n", peername);
- return 0;
- }
-
-@@ -774,7 +779,7 @@ found_host_check:
-
- if ( connect_to_host(peername) ) {
- csync_error_count++;
-- csync_debug(0, "ERROR: Connection to remote host failed.\n");
-+ csync_debug(0, "ERROR: Connection to remote host `%s' failed.\n", peername);
- return 0;
- }
-
-@@ -798,7 +803,7 @@ found_host:
- filename ? url_encode(filename) : "",
- filename ? "'" : "")
- {
-- char *l_file = strdup(url_decode(SQL_V[1])), *l_checktxt = strdup(url_decode(SQL_V[0]));
-+ char *l_file = strdup(url_decode(SQL_V(1))), *l_checktxt = strdup(url_decode(SQL_V(0)));
- if ( csync_match_file_host(l_file, myname, peername, 0) ) {
- if ( remote_eof ) {
- got_remote_eof:
-@@ -936,17 +941,17 @@ void csync_remove_old()
- const struct csync_group *g = 0;
- const struct csync_group_host *h;
-
-- const char *filename = url_decode(SQL_V[0]);
-+ const char *filename = url_decode(SQL_V(0));
-
- while ((g=csync_find_next(g, filename)) != 0) {
-- if (!strcmp(g->myname, SQL_V[1]))
-+ if (!strcmp(g->myname, SQL_V(1)))
- for (h = g->host; h; h = h->next) {
-- if (!strcmp(h->hostname, SQL_V[2]))
-+ if (!strcmp(h->hostname, SQL_V(2)))
- goto this_dirty_record_is_ok;
- }
- }
-
-- textlist_add2(&tl, SQL_V[0], SQL_V[2], 0);
-+ textlist_add2(&tl, SQL_V(0), SQL_V(2), 0);
-
- this_dirty_record_is_ok:
- ;
-@@ -962,8 +967,8 @@ this_dirty_record_is_ok:
- SQL_BEGIN("Query file DB",
- "SELECT filename FROM file")
- {
-- if (!csync_find_next(0, url_decode(SQL_V[0])))
-- textlist_add(&tl, SQL_V[0], 0);
-+ if (!csync_find_next(0, url_decode(SQL_V(0))))
-+ textlist_add(&tl, SQL_V(0), 0);
- } SQL_END;
- for (t = tl; t != 0; t = t->next) {
- csync_debug(1, "Removing %s from file db.\n", t->value);
diff --git a/testing/csync2/longlong-format.patch b/testing/csync2/longlong-format.patch
new file mode 100644
index 0000000000..a37488d7de
--- /dev/null
+++ b/testing/csync2/longlong-format.patch
@@ -0,0 +1,31 @@
+--- old/checktxt.c
++++ new/checktxt.c
+@@ -49,7 +49,7 @@
+ xxprintf("v1");
+
+ if ( !S_ISLNK(st->st_mode) && !S_ISDIR(st->st_mode) )
+- xxprintf(":mtime=%Ld", ign_mtime ? (long long)0 : (long long)st->st_mtime);
++ xxprintf(":mtime=%lld", ign_mtime ? (long long)0 : (long long)st->st_mtime);
+
+ if ( !csync_ignore_mod )
+ xxprintf(":mode=%d", (int)st->st_mode);
+@@ -61,7 +61,7 @@
+ xxprintf(":gid=%d", (int)st->st_gid);
+
+ if ( S_ISREG(st->st_mode) )
+- xxprintf(":type=reg:size=%Ld", (long long)st->st_size);
++ xxprintf(":type=reg:size=%lld", (long long)st->st_size);
+
+ if ( S_ISDIR(st->st_mode) )
+ xxprintf(":type=dir");
+--- old/update.c
++++ new/update.c
+@@ -469,7 +469,7 @@
+
+ skip_action:
+ if ( !S_ISLNK(st.st_mode) ) {
+- conn_printf("SETIME %s %s %Ld\n",
++ conn_printf("SETIME %s %s %lld\n",
+ url_encode(key), url_encode(filename),
+ (long long)st.st_mtime);
+ last_conn_status = read_conn_status(filename, peername);
diff --git a/testing/csync2/rsync-strlcpy-disable.patch b/testing/csync2/rsync-strlcpy-disable.patch
new file mode 100644
index 0000000000..5d28e1da29
--- /dev/null
+++ b/testing/csync2/rsync-strlcpy-disable.patch
@@ -0,0 +1,20 @@
+--- old/rsync.c
++++ new/rsync.c
+@@ -48,7 +48,7 @@
+ * @param bufsize is the size of the destination buffer.
+ *
+ * @return index of the terminating byte.
+- **/
++ * disabled
+ static size_t strlcpy(char *d, const char *s, size_t bufsize)
+ {
+ size_t len = strlen(s);
+@@ -61,7 +61,7 @@
+ }
+ return ret;
+ }
+-
++ **/
+ /* splits filepath at the last '/', if any, like so:
+ * dirname basename filepath
+ * "/" "" "/"