-Add support for logging daemon messages to an SQL database.
-
-To use this patch, run these commands for a successful build:
-
- patch -p1 <patches/ODBC-dblog.diff
- ./prepare-source
- ./configure --enable-ODBC
- make
-
-See the newly-created file "instructions" for more info.
-
---- old/Makefile.in
-+++ new/Makefile.in
-@@ -32,7 +32,7 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/
- ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
- zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
- OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
-- util.o main.o checksum.o match.o syscall.o log.o backup.o
-+ util.o main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@
- OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
- fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
- OBJS3=progress.o pipe.o
---- old/cleanup.c
-+++ new/cleanup.c
-@@ -26,6 +26,7 @@ extern int am_server;
- extern int am_daemon;
- extern int io_error;
- extern int keep_partial;
-+extern int am_generator;
- extern int log_got_error;
- extern char *partial_dir;
- extern char *logfile_name;
-@@ -178,8 +179,13 @@ NORETURN void _exit_cleanup(int code, co
- code = exit_code = RERR_PARTIAL;
- }
-
-- if (code || am_daemon || (logfile_name && (am_server || !verbose)))
-+ if (code || am_daemon || (logfile_name && (am_server || !verbose))) {
- log_exit(code, file, line);
-+#ifdef HAVE_LIBODBC
-+ db_log_exit(code, file, line);
-+ db_log_close();
-+#endif
-+ }
-
- /* FALLTHROUGH */
- #include "case_N.h"
---- old/clientserver.c
-+++ new/clientserver.c
-@@ -499,6 +499,9 @@ static int rsync_module(int f_in, int f_
- XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES);
-
- log_init(1);
-+#ifdef HAVE_LIBODBC
-+ db_log_open();
-+#endif
-
- #ifdef HAVE_PUTENV
- if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
-@@ -695,6 +698,9 @@ static int rsync_module(int f_in, int f_
- rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n",
- am_sender ? "on" : "to",
- request, auth_user, host, addr);
-+#ifdef HAVE_LIBODBC
-+ db_log_session();
-+#endif
- } else {
- rprintf(FLOG, "rsync %s %s from %s (%s)\n",
- am_sender ? "on" : "to",
---- old/configure.in
-+++ new/configure.in
-@@ -656,6 +656,12 @@ if test x"$with_included_popt" != x"yes"
- AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
- fi
-
-+AC_ARG_ENABLE(ODBC, AC_HELP_STRING([--enable-ODBC], [compile in support for ODBC database logging]),
-+ [ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h)
-+ AC_CHECK_LIB(odbc,SQLExecDirect)
-+ EXTRA_OBJECT="$EXTRA_OBJECT dblog.o"
-+ AC_SUBST(EXTRA_OBJECT) ])
-+
- AC_MSG_CHECKING([whether to use included libpopt])
- if test x"$with_included_popt" = x"yes"; then
- AC_MSG_RESULT($srcdir/popt)
---- old/db_log_error-list.txt
-+++ new/db_log_error-list.txt
-@@ -0,0 +1,35 @@
-+error type description
-+0 not an error.
-+1 authentication
-+2 file/dir deletion failed
-+3 connection closed
-+4 read error
-+5 multiplexing overflow
-+6 unexpected tag
-+7 over long v-string received
-+8 invalid block length
-+9 invalid checksum length
-+10 invalid remainder length
-+11 failed to write error
-+12 attempting to send over-long vstring
-+13 temporary filename too long
-+14 lseek failed
-+15 write failed
-+16 rename failed
-+17 rsync hack failed
-+18 "invalid basis_dir index
-+19 fstat failed
-+20 is a directory
-+21 open file failed
-+22 mkstemp failed
-+23 close failed
-+24 failed verification
-+25 IO error, skipping deletion.
-+26 directory creation failed
-+27 ignoring unsafe symbolic link
-+28 symbolic link failed
-+29 mknod failed
-+30 failed to stat
-+31 unlink
-+32 failed to open file/directory
-+33 open?
---- old/dblog-tables-mysql.sql
-+++ new/dblog-tables-mysql.sql
-@@ -0,0 +1,64 @@
-+drop table transfer;
-+drop table exit;
-+drop table session;
-+
-+CREATE TABLE session (
-+ id int auto_increment NOT NULL,
-+ date timestamp NOT NULL,
-+ ip_address varchar(15) NOT NULL,
-+ username varchar(20) NOT NULL,
-+ module_name varchar(20) NOT NULL,
-+ module_path varchar(255) NOT NULL,
-+ process_id int NOT NULL,
-+ Primary Key (id)
-+);
-+
-+CREATE TABLE transfer (
-+ id int auto_increment NOT NULL,
-+ session_id int NOT NULL,
-+ date timestamp NOT NULL,
-+ file_name varchar(255) NOT NULL,
-+ file_size bigint NOT NULL,
-+ bytes_transferred bigint NOT NULL,
-+ checksum_bytes_transferred bigint NOT NULL,
-+ operation varchar(20),
-+ Primary Key (id),
-+ foreign key (session_id) references session (id)
-+);
-+
-+CREATE TABLE exit (
-+ id int auto_increment NOT NULL,
-+ session_id int NOT NULL,
-+ date timestamp NOT NULL,
-+ total_bytes_written bigint NOT NULL,
-+ total_bytes_read bigint NOT NULL,
-+ total_size bigint NOT NULL,
-+ error_text varchar(128) NOT NULL,
-+ error_code int NOT NULL,
-+ error_file varchar(64) NOT NULL,
-+ error_line int NOT NULL,
-+ process_id int NOT NULL,
-+ Primary Key (id),
-+ foreign key (session_id) references session (id)
-+);
-+
-+CREATE TABLE error (
-+ id int auto_increment NOT NULL,
-+ session_id int NOT NULL,
-+ date timestamp NOT NULL,
-+ logcode bigint NOT NULL,
-+ error_number bigint NOT NULL,
-+ error_text varchar(512),
-+ PrimaryKey (id),
-+ foreign key (session_id) references session (id)
-+);
-+
-+CREATE TABLE delete (
-+ id serial NOT NULL,
-+ session_id int NOT NULL,
-+ date timestamp NOT NULL,
-+ path varchar(512) NOT NULL,
-+ mode int NOT NULL,
-+ PrimaryKey (id),
-+ foreign key (session_id) references session (id)
-+);
---- old/dblog-tables-postgresql.sql
-+++ new/dblog-tables-postgresql.sql
-@@ -0,0 +1,67 @@
-+drop table transfer;
-+drop table exit;
-+drop table session;
-+drop sequence session_id_seq;
-+create sequence session_id_seq;
-+
-+CREATE TABLE "session" (
-+ "id" int NOT NULL,
-+ "date" timestamp NOT NULL default now(),
-+ "ip_address" varchar(15) NOT NULL,
-+ "username" varchar(20) NOT NULL,
-+ "module_name" varchar(20) NOT NULL,
-+ "module_path" varchar(255) NOT NULL,
-+ "process_id" int NOT NULL,
-+ Primary Key (id)
-+);
-+
-+CREATE TABLE "transfer" (
-+ "id" serial NOT NULL,
-+ "session_id" int NOT NULL,
-+ "date" timestamp NOT NULL default now(),
-+ "file_name" varchar(512) NOT NULL,
-+ "file_size" bigint NOT NULL,
-+ "bytes_transferred" bigint NOT NULL,
-+ "checksum_bytes_transferred" bigint NOT NULL,
-+ "operation" varchar(20),
-+ Primary Key (id),
-+ foreign key (session_id) references session (id)
-+);
-+
-+CREATE TABLE "exit" (
-+ "id" serial NOT NULL,
-+ "session_id" int NOT NULL,
-+ "date" timestamp NOT NULL default now(),
-+ "total_bytes_written" bigint NOT NULL,
-+ "total_bytes_read" bigint NOT NULL,
-+ "total_size" bigint NOT NULL,
-+ "error_text" varchar(128) NOT NULL,
-+ "error_code" int NOT NULL,
-+ "error_file" varchar(64) NOT NULL,
-+ "error_line" int NOT NULL,
-+ "process_id" int NOT NULL,
-+ Primary Key (id),
-+ foreign key (session_id) references session (id)
-+);
-+
-+CREATE TABLE "error" (
-+ "id" serial NOT NULL,
-+ "session_id" int NOT NULL,
-+ "date" timestamp NOT NULL default now(),
-+ "logcode" int NOT NULL,
-+ "error_number" int NOT NULL,
-+ "error_text" varchar(512),
-+ Primary Key (id),
-+ foreign key (session_id) references session (id)
-+
-+);
-+
-+CREATE TABLE "delete" (
-+ "id" serial NOT NULL,
-+ "session_id" int NOT NULL,
-+ "date" timestamp NOT NULL default now(),
-+ "path" varchar(512) NOT NULL,
-+ "mode" int NOT NULL,
-+ Primary Key (id),
-+ foreign key (session_id) references session (id)
-+);
---- old/dblog.c
-+++ new/dblog.c
-@@ -0,0 +1,549 @@
-+/*
-+ * ODBC Database logging functions
-+ *
-+ * Written by Steve Sether, April 2004
-+ * steve@vellmont.com
-+ */
-+
-+#include "rsync.h"
-+
-+#ifdef HAVE_SQL_H
-+#include <sql.h>
-+#else
-+#ifdef HAVE_ODBC_SQL_H
-+#include <odbc/sql.h>
-+#endif
-+#endif
-+
-+#ifdef HAVE_SQLEXT_H
-+#include <sqlext.h>
-+#else
-+#ifdef HAVE_ODBC_SQLEXT_H
-+#include <odbc/sqlext.h>
-+#endif
-+#endif
-+
-+#ifdef HAVE_SQLTYPES_H
-+#include <sqltypes.h>
-+#else
-+#ifdef HAVE_ODBC_SQLTYPES_H
-+#include <odbc/sqltypes.h>
-+#endif
-+#endif
-+
-+SQLHENV db_environ_handle; /* Handle ODBC environment */
-+long result; /* result of functions */
-+SQLHDBC db_handle_g = NULL; /* database connection handle for generator*/
-+SQLHDBC db_handle_r = NULL; /* database connection handle for sender */
-+SQLHSTMT sql_statement_handle_g; /* SQL statement handle for generator*/
-+SQLHSTMT sql_statement_handle_r; /* SQL statement handle for receiver*/
-+extern int am_daemon;
-+extern int am_sender;
-+extern int am_generator;
-+extern char *auth_user;
-+extern int module_id;
-+extern int dry_run;
-+
-+
-+char sql_status[10]; /* Status SQL */
-+SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id;
-+SQLSMALLINT V_OD_mlen, V_OD_colanz;
-+char V_OD_msg[200], V_OD_buffer[200];
-+SQLINTEGER session_id;
-+
-+
-+/* This function simply removes invalid characters from the SQL statement
-+ * to prevent SQL injection attacks. */
-+char *sanitizeSql(const char *input)
-+{
-+ char *out, *ptr;
-+ const char *c;
-+
-+ if (strlen(input) > ((~(unsigned int)0)>>1)-3)
-+ return 0;
-+ if (!(out = ptr = new_array(char, strlen(input) * 2 + 1)))
-+ return 0;
-+
-+ for (c = input; *c; c++) {
-+ switch (*c) {
-+ case '\'':
-+ *ptr++ = '\'';
-+ *ptr++ = '\'';
-+ break;
-+ case '\b':
-+ *ptr++ = '\\';
-+ *ptr++ = 'b';
-+ break;
-+ case '\n':
-+ *ptr++ = '\\';
-+ *ptr++ = 'n';
-+ break;
-+ case '\r':
-+ *ptr++ = '\\';
-+ *ptr++ = 'r';
-+ break;
-+ case '\t':
-+ *ptr++ = '\\';
-+ *ptr++ = 't';
-+ break;
-+ default:
-+ *ptr++ = *c;
-+ break;
-+ }
-+ }
-+ *ptr = '\0';
-+ return out;
-+}
-+
-+void db_log_open(void)
-+{
-+ if (!lp_database_logging(module_id))
-+ return;
-+
-+ /* get ODBC environment handle */
-+ result = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&db_environ_handle);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ rprintf(FERROR, "Error: couldn't get database environment handle\n");
-+ return;
-+ }
-+
-+ /* Setting database enviroment */
-+ result = SQLSetEnvAttr(db_environ_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ rprintf(FERROR, "Error: couldn't set database environment.\n");
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+ if (db_handle_g == NULL) {
-+ /* Get a database handle for the generator*/
-+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_g);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ rprintf(FERROR, "Error: couldn't allocate database handle for generator\n");
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+
-+ /* Set connection attributes for the generator db connection */
-+ SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
-+
-+ /* get the database connection for the generator. */
-+ result = SQLConnect(db_handle_g, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
-+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
-+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
-+
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g, 1,
-+ sql_status, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
-+ rprintf(FERROR,"Error Connecting to Database (generator) %s\n",V_OD_msg);
-+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
-+ db_handle_g = NULL;
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+ rprintf(FLOG,"Connected to database for generator!\n");
-+ } else {
-+ rprintf(FERROR,"Already connected to database for generator\n");
-+ }
-+ if (db_handle_r == NULL) {
-+ /* Get a database handle for the receiver */
-+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_r);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ rprintf(FERROR, "Error: couldn't allocate database handle for receiver\n");
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+
-+ /* Set connection attributes for the receiver db connection */
-+ SQLSetConnectAttr(db_handle_r, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
-+
-+ /* get the generator connection for the receiver. */
-+ result = SQLConnect(db_handle_r, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
-+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
-+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
-+
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1,
-+ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error Connecting to Database (receiver) %s\n",V_OD_msg);
-+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
-+ db_handle_r = NULL;
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+ rprintf(FLOG,"Connected to database for receiver!\n");
-+ } else {
-+ rprintf(FERROR,"Already connected to database for receiver\n");
-+ }
-+
-+ /* get SQL statement handle for generator */
-+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_g, &sql_statement_handle_g);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
-+ SQLDisconnect(db_handle_g);
-+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
-+ db_handle_g = NULL;
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+
-+ /* get SQL statement handle for receiver */
-+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_r, &sql_statement_handle_r);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
-+ SQLDisconnect(db_handle_r);
-+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
-+ db_handle_r = NULL;
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ return;
-+ }
-+}
-+
-+void db_log_close()
-+{
-+ if (!lp_database_logging(module_id))
-+ return;
-+
-+ if (am_generator) {
-+ if (sql_statement_handle_g != NULL) {
-+ /* free the statement handle first */
-+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g);
-+ sql_statement_handle_g = NULL;
-+ } else {
-+ rprintf(FERROR,"No generator sql statement handle to close\n");
-+ }
-+
-+ if (db_handle_g != NULL) {
-+ /* disconnect, and free the database handle. */
-+ SQLDisconnect(db_handle_g);
-+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
-+ db_handle_g = NULL;
-+ } else {
-+ rprintf(FERROR,"Generator database connection already closed\n");
-+ }
-+ } else { /* must be receiver */
-+ if (sql_statement_handle_r != NULL) {
-+ /* free the statement handle first */
-+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_r);
-+ sql_statement_handle_r = NULL;
-+ } else {
-+ rprintf(FERROR,"No receiver sql statement handle to close\n");
-+ }
-+
-+ if (db_handle_r != NULL) {
-+ /* disconnect, and free the database handle. */
-+ SQLDisconnect(db_handle_r);
-+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
-+ db_handle_r = NULL;
-+ } else {
-+ rprintf(FERROR,"Receiver database connection already closed\n");
-+ }
-+ }
-+
-+ if (db_environ_handle != NULL) {
-+ /* free the environment handle */
-+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
-+ db_environ_handle = NULL;
-+ } else {
-+ rprintf(FERROR,"No environment handle to close\n");
-+ }
-+}
-+
-+static long get_unique_session_id()
-+{
-+ long unique;
-+ char strSqlStatement[1024];
-+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
-+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
-+
-+ if (db_handle != NULL) {
-+ /* choose the appropriate select statement based upon which DBMS we're using.
-+ * different datbases use different methods to get a unique ID. Some use a counter
-+ * object (sequence), others use an auto increment datatype and have a method
-+ * to get the last ID inserted using this connection. */
-+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) {
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "SELECT NEXTVAL('%s');", lp_sequence_name(module_id));
-+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) {
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id));
-+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) {
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id));
-+ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "SELECT LAST_INSERT_ID()");
-+ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "SELECT @@IDENTITY");
-+ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ lp_custom_unique_id_select(module_id));
-+ }
-+
-+ /* bind the 1st column to unique */
-+ SQLBindCol(sql_statement_handle,1,SQL_C_LONG,&unique,150,&V_OD_err);
-+ /* execute the SQL statement */
-+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at get_sequence: Error in Select! %s %s\n",strSqlStatement,V_OD_msg);
-+ } else {
-+ result = SQLFetch(sql_statement_handle);
-+ if (result != SQL_NO_DATA && unique != 0) {
-+ rprintf(FINFO,"Got unique sequence! %ld\n",unique);
-+ } else {
-+ rprintf(FERROR,"Error at get_sequence: Didn't get unique session ID\n");
-+ }
-+ /* Close the cursor so the statement can be re-used */
-+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at get_sequence: Error in closing SQL statement handle %s\n",V_OD_msg);
-+ return unique;
-+ }
-+ return unique;
-+ }
-+ }
-+ rprintf(FERROR,"Error at get_sequence: Not connected to database\n");
-+ return -1;
-+}
-+
-+void db_log_session()
-+{
-+ char strSqlStatement[1024];
-+ int gotSessionID = 0;
-+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
-+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
-+
-+ if (!lp_database_logging(module_id))
-+ return;
-+
-+ if (db_handle != NULL) {
-+ /* if we're using a sequence via the nextval command to
-+ * get a unique ID, we need to get it before we do the
-+ * insert. We also get the unique ID now if custom,
-+ * and get_custom_id_before_insert is set. */
-+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0
-+ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0
-+ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0
-+ || (strcmp(lp_unique_id_method(module_id),"custom") == 0
-+ && lp_get_custom_id_before_insert(module_id))) {
-+ session_id = get_unique_session_id();
-+ gotSessionID = 1;
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "INSERT INTO %s (id, date, ip_address, username, module_name, module_path, process_id) VALUES ('%ld', '%s', '%s', '%s','%s','%s','%d');",
-+ lp_session_table_name(module_id), session_id, timestring(time(NULL)), client_addr(0),
-+ auth_user, lp_name(module_id), lp_path(module_id), getpid());
-+ } else {
-+ /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "INSERT INTO %s (date, ip_address, username, module_name, module_path, process_id) VALUES ('%s', '%s', '%s', '%s','%s','%d');",
-+ lp_session_table_name(module_id), timestring(time(NULL)), client_addr(0), auth_user,
-+ lp_name(module_id), lp_path(module_id), getpid());
-+ }
-+
-+ /* Insert the new session into the database */
-+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at db_log_session: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
-+ }
-+
-+ /* close the cursor so the statement handle can be re-used. */
-+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error in resetting SQL statement handle %s\n",V_OD_msg);
-+ }
-+ /* get the session ID for databases that give the unique ID after an insert */
-+ if (gotSessionID == 0) {
-+ session_id = get_unique_session_id();
-+ }
-+ } else {
-+ rprintf(FERROR,"Error at db_log_session: Not connected to database!\n");
-+ }
-+}
-+
-+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation)
-+{
-+ extern struct stats stats;
-+ char strSqlStatement[2048];
-+ char strFileName[MAXPATHLEN];
-+ char *strFileNamePtr;
-+ char strFileSize[255];
-+ int64 intBytesTransferred;
-+ int64 intCheckSumBytes;
-+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
-+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
-+
-+ if (!lp_database_logging(module_id))
-+ return;
-+
-+ if (db_handle != NULL) {
-+ strFileNamePtr = f_name(file, NULL);
-+ if (am_sender && file->dir.root) {
-+ pathjoin(strFileName, sizeof strFileName,
-+ file->dir.root, strFileNamePtr);
-+ strFileNamePtr = strFileName;
-+ }
-+ clean_fname(strFileNamePtr, 0);
-+ if (*strFileNamePtr == '/')
-+ strFileNamePtr++;
-+
-+ snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)F_LENGTH(file));
-+ if (am_sender) {
-+ intBytesTransferred = stats.total_written - initial_stats->total_written;
-+ } else {
-+ intBytesTransferred = stats.total_read - initial_stats->total_read;
-+ }
-+
-+ if (!am_sender) {
-+ intCheckSumBytes = stats.total_written - initial_stats->total_written;
-+ } else {
-+ intCheckSumBytes = stats.total_read - initial_stats->total_read;
-+ }
-+
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "INSERT INTO %s (session_id,date, file_name, file_size, bytes_transferred, checksum_bytes_transferred, operation) VALUES ('%ld','%s','%s','%s','%Ld','%Ld','%s');",
-+ lp_transfer_table_name(module_id), session_id, timestring(time(NULL)),
-+ sanitizeSql(strFileNamePtr), strFileSize, intBytesTransferred,
-+ intCheckSumBytes, operation);
-+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
-+ if (result == SQL_INVALID_HANDLE)
-+ rprintf(FERROR,"INVALID HANDLE\n");
-+ }
-+ } else {
-+ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n");
-+ }
-+}
-+
-+void db_log_exit(int code, const char *file, int line)
-+{
-+ char strSqlStatement[2048];
-+ const char *error_text;
-+ extern struct stats stats;
-+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
-+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
-+
-+ if (!lp_database_logging(module_id))
-+ return;
-+
-+ if (db_handle != NULL) {
-+ if (code != 0) {
-+ error_text = rerr_name(code);
-+ if (!error_text) {
-+ error_text = "unexplained error";
-+ }
-+ } else {
-+ error_text = "";
-+ }
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "INSERT INTO %s (session_id, date, total_bytes_written,total_bytes_read,total_size,error_text,error_code,error_file,error_line,process_id) VALUES ('%ld','%s','%Ld','%Ld','%Ld','%s','%d','%s','%d','%d');",
-+ lp_exit_table_name(module_id), session_id, timestring(time(NULL)), stats.total_written,
-+ stats.total_read, stats.total_size, error_text, code, file, line, getpid());
-+
-+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
-+
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
-+ }
-+ } else {
-+ rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
-+ }
-+}
-+
-+void db_log_delete(char *fname, int mode)
-+{
-+ char strSqlStatement[2048];
-+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
-+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
-+
-+ if (!am_daemon || dry_run || !lp_database_logging(module_id))
-+ return;
-+
-+ if (db_handle != NULL) {
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "INSERT INTO %s (session_id, date, path, mode) VALUES ('%ld','%s','%s','%d');",
-+ lp_delete_table_name(module_id), session_id, timestring(time(NULL)), sanitizeSql(fname), mode);
-+
-+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
-+
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at db_log_delete: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
-+ }
-+ } else {
-+ rprintf(FERROR,"Error at db_log_delete: Not connected to database!\n");
-+ }
-+}
-+
-+void db_log_error(enum logcode code, int errcode, const char *format,...)
-+{
-+ char strSqlStatement[MAXPATHLEN+1024];
-+ va_list ap;
-+ char buf[MAXPATHLEN+512];
-+ size_t len;
-+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
-+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
-+
-+ if (!lp_database_logging(module_id))
-+ return;
-+
-+ va_start(ap, format);
-+ len = vsnprintf(buf, sizeof buf, format, ap);
-+ va_end(ap);
-+
-+ /* Deal with buffer overruns. Instead of panicking, just
-+ * truncate the resulting string. (Note that configure ensures
-+ * that we have a vsnprintf() that doesn't ever return -1.) */
-+ if (len > sizeof buf - 1) {
-+ const char ellipsis[] = "[...]";
-+
-+ /* Reset length, and zero-terminate the end of our buffer */
-+ len = sizeof buf - 1;
-+ buf[len] = '\0';
-+
-+ /* Copy the ellipsis to the end of the string, but give
-+ * us one extra character:
-+ *
-+ * v--- null byte at buf[sizeof buf - 1]
-+ * abcdefghij0
-+ * -> abcd[...]00 <-- now two null bytes at end
-+ *
-+ * If the input format string has a trailing newline,
-+ * we copy it into that extra null; if it doesn't, well,
-+ * all we lose is one byte. */
-+ strncpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis);
-+ if (format[strlen(format)-1] == '\n') {
-+ buf[len-1] = '\n';
-+ }
-+ }
-+
-+ if (db_handle != NULL) {
-+ snprintf(strSqlStatement, sizeof strSqlStatement,
-+ "INSERT INTO %s (session_id, date, logcode, error_number, error_text) VALUES ('%ld','%s','%d','%d','%s');",
-+ lp_error_table_name(module_id), session_id, timestring(time(NULL)), code, errcode, sanitizeSql(buf));
-+
-+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
-+
-+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
-+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
-+ rprintf(FERROR,"Error at db_log_error: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
-+ }
-+ } else {
-+ rprintf(FERROR,"Error at db_log_error: Not connected to database!\n");
-+ }
-+}
---- old/instructions
-+++ new/instructions
-@@ -0,0 +1,84 @@
-+This patch adds the following options:
-+
-+"database logging"
-+ If set to True, rsync will attempt to connect to
-+ the specified datasource and write to the named tables.
-+ Defaults to False.
-+
-+"database datasource"
-+ Specifies the name of the ODBC data source to use.
-+
-+"database username"
-+ The username to use when connecting to the database.
-+
-+"database password"
-+ The password to use when connecting to the database.
-+
-+"transfer table name"
-+ The name of the transfer table to log to. This table contains individual
-+ filenames, file sizes, bytes transferred, checksum bytes transferred,
-+ operation (send or receive), and a timestamp.
-+
-+"session table name"
-+ The name of the session table to log to. This table contains the username,
-+ module name, module path, ip address, process ID, and a timestamp.
-+
-+"exit table name"
-+
-+ The name of the exit table to log to. This table contains the total bytes
-+ read, total bytes written, total size of all files, error code, line the
-+ error occured at, file the error occured at and the text of the error.
-+ (most of which will be blank if the program exited normally).
-+
-+"delete table name"
-+
-+ The name of the table to log deleted files/directories to.
-+
-+"error table name"
-+
-+ The name of the table errors will be logged to.
-+
-+"unique id method"
-+ Different databases use different methods to get a unique identifier.
-+ Some databases support sequence objects, and use various forms of the
-+ nextval command to retrieve a unique identifier from it. Other databases
-+ support an autonumber field, and support different methds of retrieving
-+ the ID used in the last insert. Valid values for this option are:
-+
-+ nextval-postgres
-+ uses the syntax of nextval for PostgreSQL databases
-+
-+ nextval-oracle
-+ uses the syntax of nextval for Oracle databases
-+
-+ nextval-db2
-+ uses the syntax of nextval for DB2 databases
-+
-+ last_insert_id
-+ uses the last_insert_id() command for the MySQL databases
-+
-+ @@IDENTITY
-+ uses the @@IDENTITY command for Sybase databases
-+
-+ custom
-+ Define your own method to get a unique identifier. See the
-+ "custom unique id select", and the "get custom id before select"
-+ parameters.
-+
-+"sequence name"
-+ If your database supports sequences, list the name of the sequence to use
-+ for the session unique identifier.
-+
-+"custom unique id select"
-+ Only used if you specify the custom method in "unique id method". This is
-+ a SQL statement to be executed to get a unique ID. This SQL statement must
-+ return one column with the unique ID to use for the session ID. Should be
-+ used in concert with the "get custom id before select" parameter.
-+
-+"get custom id before insert"
-+ This parameter is ignored unless the "unique id method" is set to custom.
-+ If set to true, the "custom unique id select" statement will be executed
-+ BEFORE the session row is inserted into the database. (as is done when a
-+ sequence is used for unique IDs). If False the statement will be executed
-+ after the session row is inserted (as is done when the session ID is
-+ automatically generates unique IDs). Defaults to True.
---- old/loadparm.c
-+++ new/loadparm.c
-@@ -125,9 +125,16 @@ typedef struct
- {
- char *auth_users;
- char *comment;
-+ char *custom_unique_id_select;
-+ char *database_datasource;
-+ char *database_password;
-+ char *database_username;
-+ char *delete_table_name;
- char *dont_compress;
-+ char *error_table_name;
- char *exclude;
- char *exclude_from;
-+ char *exit_table_name;
- char *filter;
- char *gid;
- char *hosts_allow;
-@@ -145,15 +152,21 @@ typedef struct
- char *prexfer_exec;
- char *refuse_options;
- char *secrets_file;
-+ char *sequence_name;
-+ char *session_table_name;
- char *temp_dir;
-+ char *transfer_table_name;
- char *uid;
-+ char *unique_id_method;
-
- int max_connections;
- int max_verbosity;
- int syslog_facility;
- int timeout;
-
-+ BOOL database_logging;
- BOOL fake_super;
-+ BOOL get_custom_id_before_insert;
- BOOL ignore_errors;
- BOOL ignore_nonreadable;
- BOOL list;
-@@ -173,9 +186,16 @@ static service sDefault =
- {
- /* auth_users; */ NULL,
- /* comment; */ NULL,
-+ /* custom_unique_id_select; */ NULL,
-+ /* database_datasource; */ NULL,
-+ /* database_password; */ NULL,
-+ /* database_username; */ NULL,
-+ /* delete_table_name; */ NULL,
- /* dont_compress; */ DEFAULT_DONT_COMPRESS,
-+ /* error_table_name; */ NULL,
- /* exclude; */ NULL,
- /* exclude_from; */ NULL,
-+ /* exit_table_name; */ NULL,
- /* filter; */ NULL,
- /* gid; */ NOBODY_GROUP,
- /* hosts_allow; */ NULL,
-@@ -193,15 +213,21 @@ static service sDefault =
- /* prexfer_exec; */ NULL,
- /* refuse_options; */ NULL,
- /* secrets_file; */ NULL,
-+ /* sequence_name; */ NULL,
-+ /* session_table_name; */ NULL,
- /* temp_dir; */ NULL,
-+ /* transfer_table_name; */ NULL,
- /* uid; */ NOBODY_USER,
-+ /* unique_id_method; */ NULL,
-
- /* max_connections; */ 0,
- /* max_verbosity; */ 1,
- /* syslog_facility; */ LOG_DAEMON,
- /* timeout; */ 0,
-
-+ /* database_logging; */ False,
- /* fake_super; */ False,
-+ /* get_custom_id_before_insert; */ True,
- /* ignore_errors; */ False,
- /* ignore_nonreadable; */ False,
- /* list; */ True,
-@@ -300,11 +326,20 @@ static struct parm_struct parm_table[] =
-
- {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0},
- {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL,0},
-+ {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0},
-+ {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0},
-+ {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging, NULL,0},
-+ {"database password", P_STRING, P_LOCAL, &sDefault.database_password, NULL,0},
-+ {"database username", P_STRING, P_LOCAL, &sDefault.database_username, NULL,0},
-+ {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0},
- {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
-+ {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0},
- {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
- {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
-+ {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name, NULL,0},
- {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
- {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
-+ {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0},
- {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
- {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
- {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL,0},
-@@ -329,12 +364,16 @@ static struct parm_struct parm_table[] =
- {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0},
- {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0},
- {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0},
-+ {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name, NULL,0},
-+ {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0},
- {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0},
- {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0},
- {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0},
- {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0},
- {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0},
-+ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0},
- {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0},
-+ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0},
- {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0},
- {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL,0},
- {NULL, P_BOOL, P_NONE, NULL, NULL,0}
-@@ -390,9 +429,16 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Global
-
- FN_LOCAL_STRING(lp_auth_users, auth_users)
- FN_LOCAL_STRING(lp_comment, comment)
-+FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select)
-+FN_LOCAL_STRING(lp_database_datasource, database_datasource)
-+FN_LOCAL_STRING(lp_database_password, database_password)
-+FN_LOCAL_STRING(lp_database_username, database_username)
-+FN_LOCAL_STRING(lp_delete_table_name,delete_table_name)
- FN_LOCAL_STRING(lp_dont_compress, dont_compress)
-+FN_LOCAL_STRING(lp_error_table_name,error_table_name)
- FN_LOCAL_STRING(lp_exclude, exclude)
- FN_LOCAL_STRING(lp_exclude_from, exclude_from)
-+FN_LOCAL_STRING(lp_exit_table_name, exit_table_name)
- FN_LOCAL_STRING(lp_filter, filter)
- FN_LOCAL_STRING(lp_gid, gid)
- FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
-@@ -410,15 +456,21 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf
- FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
- FN_LOCAL_STRING(lp_refuse_options, refuse_options)
- FN_LOCAL_STRING(lp_secrets_file, secrets_file)
-+FN_LOCAL_STRING(lp_sequence_name,sequence_name)
-+FN_LOCAL_STRING(lp_session_table_name,session_table_name)
- FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
- FN_LOCAL_STRING(lp_temp_dir, temp_dir)
-+FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name)
- FN_LOCAL_STRING(lp_uid, uid)
-+FN_LOCAL_STRING(lp_unique_id_method,unique_id_method)
-
- FN_LOCAL_INTEGER(lp_max_connections, max_connections)
- FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
- FN_LOCAL_INTEGER(lp_timeout, timeout)
-
-+FN_LOCAL_BOOL(lp_database_logging, database_logging)
- FN_LOCAL_BOOL(lp_fake_super, fake_super)
-+FN_LOCAL_BOOL(lp_get_custom_id_before_insert,get_custom_id_before_insert)
- FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
- FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
- FN_LOCAL_BOOL(lp_list, list)
---- old/log.c
-+++ new/log.c
-@@ -97,7 +97,7 @@ struct {
- /*
- * Map from rsync error code to name, or return NULL.
- */
--static char const *rerr_name(int code)
-+char const *rerr_name(int code)
- {
- int i;
- for (i = 0; rerr_names[i].name; i++) {
---- old/receiver.c
-+++ new/receiver.c
-@@ -111,6 +111,10 @@ int get_tmpname(char *fnametmp, const ch
-
- if (maxname < 1) {
- rprintf(FERROR, "temporary filename too long: %s\n", fname);
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR,13, "temporary filename too long: %s\n",
-+ fname);
-+#endif
- fnametmp[0] = '\0';
- return 0;
- }
-@@ -155,6 +159,9 @@ int open_tmpfile(char *fnametmp, const c
- if (fd == -1) {
- rsyserr(FERROR, errno, "mkstemp %s failed",
- full_fname(fnametmp));
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 22, "mkstemp %s failed", full_fname(fnametmp));
-+#endif
- return -1;
- }
-
-@@ -213,6 +220,10 @@ static int receive_data(int f_in, char *
- if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) {
- rsyserr(FERROR, errno, "lseek of %s returned %.0f, not %.0f",
- full_fname(fname), (double)j, (double)offset);
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 14, "lseek failed on %s",
-+ full_fname(fname));
-+#endif
- exit_cleanup(RERR_FILEIO);
- }
- }
-@@ -270,6 +281,11 @@ static int receive_data(int f_in, char *
- "lseek of %s returned %.0f, not %.0f",
- full_fname(fname),
- (double)pos, (double)offset);
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 14,
-+ "lseek failed on %s",
-+ full_fname(fname));
-+#endif
- exit_cleanup(RERR_FILEIO);
- }
- continue;
-@@ -295,6 +311,9 @@ static int receive_data(int f_in, char *
- report_write_error:
- rsyserr(FERROR, errno, "write failed on %s",
- full_fname(fname));
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 15, "write failed on %s",full_fname(fname));
-+#endif
- exit_cleanup(RERR_FILEIO);
- }
-
-@@ -338,6 +357,12 @@ static void handle_delayed_updates(char
- rsyserr(FERROR, errno,
- "rename failed for %s (from %s)",
- full_fname(fname), partialptr);
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 16,
-+ "rename failed for %s (from %s)",
-+ full_fname(fname),
-+ partialptr);
-+#endif
- } else {
- if (remove_source_files
- || (preserve_hard_links && F_IS_HLINKED(file)))
-@@ -489,6 +514,9 @@ int recv_files(int f_in, char *local_nam
- if (server_filter_list.head
- && check_filter(&server_filter_list, fname, 0) < 0) {
- rprintf(FERROR, "attempt to hack rsync failed.\n");
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR,17,"attempt to hack rsync failed.");
-+#endif
- exit_cleanup(RERR_PROTOCOL);
- }
-
-@@ -545,6 +573,11 @@ int recv_files(int f_in, char *local_nam
- rprintf(FERROR,
- "invalid basis_dir index: %d.\n",
- fnamecmp_type);
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 18,
-+ "invalid basis_dir index: %d.\n",
-+ fnamecmp_type);
-+#endif
- exit_cleanup(RERR_PROTOCOL);
- }
- pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
-@@ -594,6 +627,9 @@ int recv_files(int f_in, char *local_nam
- } else if (do_fstat(fd1,&st) != 0) {
- rsyserr(FERROR, errno, "fstat %s failed",
- full_fname(fnamecmp));
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp));
-+#endif
- discard_receive_data(f_in, F_LENGTH(file));
- close(fd1);
- if (inc_recurse)
-@@ -609,6 +645,9 @@ int recv_files(int f_in, char *local_nam
- */
- rprintf(FERROR,"recv_files: %s is a directory\n",
- full_fname(fnamecmp));
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp));
-+#endif
- discard_receive_data(f_in, F_LENGTH(file));
- close(fd1);
- if (inc_recurse)
-@@ -643,6 +682,9 @@ int recv_files(int f_in, char *local_nam
- if (fd2 == -1) {
- rsyserr(FERROR, errno, "open %s failed",
- full_fname(fname));
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR,22, "open %s failed", full_fname(fname));
-+#endif
- }
- } else {
- fd2 = open_tmpfile(fnametmp, fname, file);
-@@ -670,12 +712,19 @@ int recv_files(int f_in, char *local_nam
- fname, fd2, F_LENGTH(file));
-
- log_item(log_code, file, &initial_stats, iflags, NULL);
-+#ifdef HAVE_LIBODBC
-+ db_log_transfer(file, &initial_stats, "receive");
-+#endif
-
- if (fd1 != -1)
- close(fd1);
- if (close(fd2) < 0) {
- rsyserr(FERROR, errno, "close failed on %s",
- full_fname(fnametmp));
-+#ifdef HAVE_LIBODBC
-+ db_log_error(FERROR, 23, "close failed on %s",
-+ full_fname(fnametmp));
-+#endif
- exit_cleanup(RERR_FILEIO);
- }
-
-@@ -732,6 +781,12 @@ int recv_files(int f_in, char *local_nam
- rprintf(msgtype,
- "%s: %s failed verification -- update %s%s.\n",
- errstr, fname, keptstr, redostr);
-+#ifdef HAVE_LIBODBC
-+ db_log_error(msgtype,24,
-+ "%s: %s failed verification -- update %s%s.\n",
-+ errstr, fname,
-+ keptstr, redostr);
-+#endif
- }
- if (!redoing) {
- send_msg_int(MSG_REDO, ndx);
---- old/sender.c
-+++ new/sender.c
-@@ -344,6 +344,9 @@ void send_files(int f_in, int f_out)
- end_progress(st.st_size);
-
- log_item(log_code, file, &initial_stats, iflags, NULL);
-+#ifdef HAVE_LIBODBC
-+ db_log_transfer(file, &initial_stats,"send");
-+#endif
-
- if (mbuf) {
- j = unmap_file(mbuf);