A little more cleanup.
[rsync/rsync-patches.git] / ODBC-dblog.diff
CommitLineData
2c40f06f
WD
1Add support for logging daemon messages to an SQL database.
2
03019e41 3To use this patch, run these commands for a successful build:
2c40f06f 4
03019e41 5 patch -p1 <patches/ODBC-dblog.diff
27e96866 6 ./prepare-source
a9eff38d 7 ./configure --enable-ODBC
8a529471 8 make
2c40f06f 9
03019e41 10See the newly-created file "instructions" for more info.
8a529471 11
9a7eef96
WD
12--- old/Makefile.in
13+++ new/Makefile.in
60a8bf36 14@@ -32,7 +32,7 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/
1680e814
WD
15 ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
16 zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
590329e5
WD
17 OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \
18- util.o main.o checksum.o match.o syscall.o log.o backup.o
19+ util.o main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@
9c25eef5 20 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
5ff5e82f 21 fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
103bcb1d 22 OBJS3=progress.o pipe.o
9a7eef96
WD
23--- old/cleanup.c
24+++ new/cleanup.c
5ff5e82f 25@@ -26,6 +26,7 @@ extern int am_server;
55c1a3b7 26 extern int am_daemon;
a9eff38d
WD
27 extern int io_error;
28 extern int keep_partial;
29+extern int am_generator;
30 extern int log_got_error;
31 extern char *partial_dir;
55c1a3b7 32 extern char *logfile_name;
5ff5e82f 33@@ -173,8 +174,13 @@ NORETURN void _exit_cleanup(int code, co
b3ea4757
WD
34 code = exit_code = RERR_PARTIAL;
35 }
2c40f06f 36
b3ea4757
WD
37- if (code || am_daemon || (logfile_name && (am_server || !verbose)))
38+ if (code || am_daemon || (logfile_name && (am_server || !verbose))) {
39 log_exit(code, file, line);
637ba3ff 40+#ifdef HAVE_LIBODBC
b3ea4757
WD
41+ db_log_exit(code, file, line);
42+ db_log_close();
2c40f06f 43+#endif
b3ea4757 44+ }
2c40f06f 45
b3ea4757 46 /* FALLTHROUGH */
03019e41 47 #include "case_N.h"
9a7eef96
WD
48--- old/clientserver.c
49+++ new/clientserver.c
2ac9130a 50@@ -410,6 +410,9 @@ static int rsync_module(int f_in, int f_
6849cd84 51 XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES);
2c40f06f 52
0ffcefad 53 log_init(1);
637ba3ff 54+#ifdef HAVE_LIBODBC
2c40f06f
WD
55+ db_log_open();
56+#endif
57
ff55cce0 58 #ifdef HAVE_PUTENV
6849cd84 59 if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
2ac9130a 60@@ -649,6 +652,9 @@ static int rsync_module(int f_in, int f_
545864f1
WD
61 rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n",
62 am_sender ? "on" : "to",
2c40f06f 63 request, auth_user, host, addr);
637ba3ff 64+#ifdef HAVE_LIBODBC
2c40f06f
WD
65+ db_log_session();
66+#endif
67 } else {
545864f1
WD
68 rprintf(FLOG, "rsync %s %s from %s (%s)\n",
69 am_sender ? "on" : "to",
9a7eef96
WD
70--- old/configure.in
71+++ new/configure.in
2ac9130a 72@@ -659,6 +659,12 @@ if test x"$with_included_popt" != x"yes"
2c40f06f 73 AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
545864f1
WD
74 fi
75
a9eff38d
WD
76+AC_ARG_ENABLE(ODBC, AC_HELP_STRING([--enable-ODBC], [compile in support for ODBC database logging]),
77+ [ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h)
2c40f06f
WD
78+ AC_CHECK_LIB(odbc,SQLExecDirect)
79+ EXTRA_OBJECT="$EXTRA_OBJECT dblog.o"
a9eff38d 80+ AC_SUBST(EXTRA_OBJECT) ])
545864f1 81+
2c40f06f 82 AC_MSG_CHECKING([whether to use included libpopt])
93ca4d27
WD
83 if test x"$with_included_popt" = x"yes"; then
84 AC_MSG_RESULT($srcdir/popt)
9a7eef96
WD
85--- old/db_log_error-list.txt
86+++ new/db_log_error-list.txt
a9eff38d
WD
87@@ -0,0 +1,35 @@
88+error type description
89+0 not an error.
90+1 authentication
91+2 file/dir deletion failed
92+3 connection closed
93+4 read error
94+5 multiplexing overflow
95+6 unexpected tag
96+7 over long v-string received
97+8 invalid block length
98+9 invalid checksum length
99+10 invalid remainder length
100+11 failed to write error
101+12 attempting to send over-long vstring
102+13 temporary filename too long
103+14 lseek failed
104+15 write failed
105+16 rename failed
106+17 rsync hack failed
107+18 "invalid basis_dir index
108+19 fstat failed
109+20 is a directory
110+21 open file failed
111+22 mkstemp failed
112+23 close failed
113+24 failed verification
114+25 IO error, skipping deletion.
115+26 directory creation failed
116+27 ignoring unsafe symbolic link
117+28 symbolic link failed
118+29 mknod failed
119+30 failed to stat
120+31 unlink
121+32 failed to open file/directory
122+33 open?
9a7eef96
WD
123--- old/dblog-tables-mysql.sql
124+++ new/dblog-tables-mysql.sql
a9eff38d 125@@ -0,0 +1,64 @@
103bcb1d
WD
126+drop table transfer;
127+drop table exit;
128+drop table session;
129+
130+CREATE TABLE session (
131+ id int auto_increment NOT NULL,
132+ date timestamp NOT NULL,
133+ ip_address varchar(15) NOT NULL,
134+ username varchar(20) NOT NULL,
135+ module_name varchar(20) NOT NULL,
136+ module_path varchar(255) NOT NULL,
137+ process_id int NOT NULL,
138+ Primary Key (id)
139+);
140+
141+CREATE TABLE transfer (
142+ id int auto_increment NOT NULL,
143+ session_id int NOT NULL,
144+ date timestamp NOT NULL,
103bcb1d
WD
145+ file_name varchar(255) NOT NULL,
146+ file_size bigint NOT NULL,
147+ bytes_transferred bigint NOT NULL,
148+ checksum_bytes_transferred bigint NOT NULL,
149+ operation varchar(20),
150+ Primary Key (id),
151+ foreign key (session_id) references session (id)
152+);
153+
154+CREATE TABLE exit (
155+ id int auto_increment NOT NULL,
156+ session_id int NOT NULL,
157+ date timestamp NOT NULL,
158+ total_bytes_written bigint NOT NULL,
159+ total_bytes_read bigint NOT NULL,
160+ total_size bigint NOT NULL,
161+ error_text varchar(128) NOT NULL,
162+ error_code int NOT NULL,
163+ error_file varchar(64) NOT NULL,
164+ error_line int NOT NULL,
637ba3ff 165+ process_id int NOT NULL,
103bcb1d
WD
166+ Primary Key (id),
167+ foreign key (session_id) references session (id)
168+);
a9eff38d
WD
169+
170+CREATE TABLE error (
171+ id int auto_increment NOT NULL,
172+ session_id int NOT NULL,
173+ date timestamp NOT NULL,
174+ logcode bigint NOT NULL,
175+ error_number bigint NOT NULL,
176+ error_text varchar(512),
177+ PrimaryKey (id),
178+ foreign key (session_id) references session (id)
179+);
180+
181+CREATE TABLE delete (
182+ id serial NOT NULL,
183+ session_id int NOT NULL,
184+ date timestamp NOT NULL,
185+ path varchar(512) NOT NULL,
186+ mode int NOT NULL,
187+ PrimaryKey (id),
188+ foreign key (session_id) references session (id)
189+);
9a7eef96
WD
190--- old/dblog-tables-postgresql.sql
191+++ new/dblog-tables-postgresql.sql
a9eff38d 192@@ -0,0 +1,67 @@
103bcb1d
WD
193+drop table transfer;
194+drop table exit;
195+drop table session;
196+drop sequence session_id_seq;
197+create sequence session_id_seq;
198+
199+CREATE TABLE "session" (
200+ "id" int NOT NULL,
201+ "date" timestamp NOT NULL default now(),
202+ "ip_address" varchar(15) NOT NULL,
203+ "username" varchar(20) NOT NULL,
204+ "module_name" varchar(20) NOT NULL,
205+ "module_path" varchar(255) NOT NULL,
206+ "process_id" int NOT NULL,
207+ Primary Key (id)
208+);
209+
210+CREATE TABLE "transfer" (
211+ "id" serial NOT NULL,
212+ "session_id" int NOT NULL,
213+ "date" timestamp NOT NULL default now(),
103bcb1d
WD
214+ "file_name" varchar(512) NOT NULL,
215+ "file_size" bigint NOT NULL,
216+ "bytes_transferred" bigint NOT NULL,
217+ "checksum_bytes_transferred" bigint NOT NULL,
218+ "operation" varchar(20),
219+ Primary Key (id),
220+ foreign key (session_id) references session (id)
221+);
222+
223+CREATE TABLE "exit" (
224+ "id" serial NOT NULL,
225+ "session_id" int NOT NULL,
226+ "date" timestamp NOT NULL default now(),
227+ "total_bytes_written" bigint NOT NULL,
228+ "total_bytes_read" bigint NOT NULL,
229+ "total_size" bigint NOT NULL,
230+ "error_text" varchar(128) NOT NULL,
231+ "error_code" int NOT NULL,
232+ "error_file" varchar(64) NOT NULL,
233+ "error_line" int NOT NULL,
637ba3ff 234+ "process_id" int NOT NULL,
103bcb1d
WD
235+ Primary Key (id),
236+ foreign key (session_id) references session (id)
237+);
a9eff38d
WD
238+
239+CREATE TABLE "error" (
240+ "id" serial NOT NULL,
241+ "session_id" int NOT NULL,
242+ "date" timestamp NOT NULL default now(),
243+ "logcode" int NOT NULL,
244+ "error_number" int NOT NULL,
245+ "error_text" varchar(512),
246+ Primary Key (id),
247+ foreign key (session_id) references session (id)
248+
249+);
250+
251+CREATE TABLE "delete" (
252+ "id" serial NOT NULL,
253+ "session_id" int NOT NULL,
254+ "date" timestamp NOT NULL default now(),
255+ "path" varchar(512) NOT NULL,
256+ "mode" int NOT NULL,
257+ Primary Key (id),
258+ foreign key (session_id) references session (id)
259+);
9a7eef96
WD
260--- old/dblog.c
261+++ new/dblog.c
a9eff38d 262@@ -0,0 +1,549 @@
2c40f06f
WD
263+/*
264+ * ODBC Database logging functions
265+ *
266+ * Written by Steve Sether, April 2004
267+ * steve@vellmont.com
268+ */
269+
270+#include "rsync.h"
271+
637ba3ff 272+#ifdef HAVE_SQL_H
2c40f06f
WD
273+#include <sql.h>
274+#else
637ba3ff 275+#ifdef HAVE_ODBC_SQL_H
2c40f06f
WD
276+#include <odbc/sql.h>
277+#endif
278+#endif
279+
637ba3ff 280+#ifdef HAVE_SQLEXT_H
2c40f06f
WD
281+#include <sqlext.h>
282+#else
637ba3ff 283+#ifdef HAVE_ODBC_SQLEXT_H
2c40f06f
WD
284+#include <odbc/sqlext.h>
285+#endif
286+#endif
287+
637ba3ff 288+#ifdef HAVE_SQLTYPES_H
2c40f06f
WD
289+#include <sqltypes.h>
290+#else
637ba3ff 291+#ifdef HAVE_ODBC_SQLTYPES_H
2c40f06f
WD
292+#include <odbc/sqltypes.h>
293+#endif
294+#endif
295+
296+SQLHENV db_environ_handle; /* Handle ODBC environment */
297+long result; /* result of functions */
a9eff38d
WD
298+SQLHDBC db_handle_g = NULL; /* database connection handle for generator*/
299+SQLHDBC db_handle_r = NULL; /* database connection handle for sender */
300+SQLHSTMT sql_statement_handle_g; /* SQL statement handle for generator*/
301+SQLHSTMT sql_statement_handle_r; /* SQL statement handle for receiver*/
302+extern int am_daemon;
2c40f06f 303+extern int am_sender;
a9eff38d 304+extern int am_generator;
2c40f06f
WD
305+extern char *auth_user;
306+extern int module_id;
a9eff38d
WD
307+extern int dry_run;
308+
2c40f06f
WD
309+
310+char sql_status[10]; /* Status SQL */
311+SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id;
312+SQLSMALLINT V_OD_mlen, V_OD_colanz;
313+char V_OD_msg[200], V_OD_buffer[200];
314+SQLINTEGER session_id;
315+
316+
317+/* This function simply removes invalid characters from the SQL statement
318+ * to prevent SQL injection attacks. */
319+char *sanitizeSql(const char *input)
320+{
321+ char *out, *ptr;
322+ const char *c;
323+
324+ if (strlen(input) > ((~(unsigned int)0)>>1)-3)
325+ return 0;
326+ if (!(out = ptr = new_array(char, strlen(input) * 2 + 1)))
327+ return 0;
328+
329+ for (c = input; *c; c++) {
330+ switch (*c) {
331+ case '\'':
332+ *ptr++ = '\'';
333+ *ptr++ = '\'';
334+ break;
335+ case '\b':
336+ *ptr++ = '\\';
337+ *ptr++ = 'b';
338+ break;
339+ case '\n':
340+ *ptr++ = '\\';
341+ *ptr++ = 'n';
342+ break;
343+ case '\r':
344+ *ptr++ = '\\';
345+ *ptr++ = 'r';
346+ break;
347+ case '\t':
348+ *ptr++ = '\\';
349+ *ptr++ = 't';
350+ break;
351+ default:
352+ *ptr++ = *c;
353+ break;
354+ }
355+ }
356+ *ptr = '\0';
357+ return out;
358+}
359+
360+void db_log_open(void)
361+{
a9eff38d
WD
362+ if (!lp_database_logging(module_id))
363+ return;
2c40f06f 364+
a9eff38d
WD
365+ /* get ODBC environment handle */
366+ result = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&db_environ_handle);
367+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
368+ rprintf(FERROR, "Error: couldn't get database environment handle\n");
369+ return;
370+ }
2c40f06f 371+
a9eff38d
WD
372+ /* Setting database enviroment */
373+ result = SQLSetEnvAttr(db_environ_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
374+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
375+ rprintf(FERROR, "Error: couldn't set database environment.\n");
376+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
377+ db_environ_handle = NULL;
378+ return;
379+ }
380+ if (db_handle_g == NULL) {
381+ /* Get a database handle for the generator*/
382+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_g);
383+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
384+ rprintf(FERROR, "Error: couldn't allocate database handle for generator\n");
385+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
386+ db_environ_handle = NULL;
387+ return;
388+ }
2c40f06f 389+
a9eff38d
WD
390+ /* Set connection attributes for the generator db connection */
391+ SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
2c40f06f 392+
a9eff38d
WD
393+ /* get the database connection for the generator. */
394+ result = SQLConnect(db_handle_g, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
395+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
396+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
2c40f06f 397+
a9eff38d
WD
398+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
399+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g, 1,
400+ sql_status, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
401+ rprintf(FERROR,"Error Connecting to Database (generator) %s\n",V_OD_msg);
402+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
403+ db_handle_g = NULL;
404+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
405+ db_environ_handle = NULL;
406+ return;
407+ }
408+ rprintf(FLOG,"Connected to database for generator!\n");
409+ } else {
410+ rprintf(FERROR,"Already connected to database for generator\n");
411+ }
412+ if (db_handle_r == NULL) {
413+ /* Get a database handle for the receiver */
414+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_r);
415+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
416+ rprintf(FERROR, "Error: couldn't allocate database handle for receiver\n");
417+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
418+ db_environ_handle = NULL;
419+ return;
420+ }
2c40f06f 421+
a9eff38d
WD
422+ /* Set connection attributes for the receiver db connection */
423+ SQLSetConnectAttr(db_handle_r, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
424+
425+ /* get the generator connection for the receiver. */
426+ result = SQLConnect(db_handle_r, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
427+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
428+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
429+
430+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
431+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1,
432+ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen);
433+ rprintf(FERROR,"Error Connecting to Database (receiver) %s\n",V_OD_msg);
434+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
435+ db_handle_r = NULL;
436+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
437+ db_environ_handle = NULL;
438+ return;
2c40f06f 439+ }
a9eff38d
WD
440+ rprintf(FLOG,"Connected to database for receiver!\n");
441+ } else {
442+ rprintf(FERROR,"Already connected to database for receiver\n");
443+ }
444+
445+ /* get SQL statement handle for generator */
446+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_g, &sql_statement_handle_g);
447+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
448+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
449+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
450+ SQLDisconnect(db_handle_g);
451+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
452+ db_handle_g = NULL;
453+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
454+ db_environ_handle = NULL;
455+ return;
456+ }
457+
458+ /* get SQL statement handle for receiver */
459+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_r, &sql_statement_handle_r);
460+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
461+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
462+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
463+ SQLDisconnect(db_handle_r);
464+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
465+ db_handle_r = NULL;
466+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
467+ db_environ_handle = NULL;
468+ return;
2c40f06f
WD
469+ }
470+}
471+
472+void db_log_close()
473+{
a9eff38d
WD
474+ if (!lp_database_logging(module_id))
475+ return;
476+
477+ if (am_generator) {
478+ if (sql_statement_handle_g != NULL) {
2c40f06f 479+ /* free the statement handle first */
a9eff38d
WD
480+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g);
481+ sql_statement_handle_g = NULL;
2c40f06f 482+ } else {
a9eff38d 483+ rprintf(FERROR,"No generator sql statement handle to close\n");
2c40f06f 484+ }
a9eff38d
WD
485+
486+ if (db_handle_g != NULL) {
2c40f06f 487+ /* disconnect, and free the database handle. */
a9eff38d
WD
488+ SQLDisconnect(db_handle_g);
489+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
490+ db_handle_g = NULL;
2c40f06f 491+ } else {
a9eff38d 492+ rprintf(FERROR,"Generator database connection already closed\n");
2c40f06f 493+ }
a9eff38d
WD
494+ } else { /* must be receiver */
495+ if (sql_statement_handle_r != NULL) {
496+ /* free the statement handle first */
497+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_r);
498+ sql_statement_handle_r = NULL;
2c40f06f 499+ } else {
a9eff38d 500+ rprintf(FERROR,"No receiver sql statement handle to close\n");
2c40f06f 501+ }
a9eff38d
WD
502+
503+ if (db_handle_r != NULL) {
504+ /* disconnect, and free the database handle. */
505+ SQLDisconnect(db_handle_r);
506+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
507+ db_handle_r = NULL;
508+ } else {
509+ rprintf(FERROR,"Receiver database connection already closed\n");
510+ }
511+ }
512+
513+ if (db_environ_handle != NULL) {
514+ /* free the environment handle */
515+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
516+ db_environ_handle = NULL;
517+ } else {
518+ rprintf(FERROR,"No environment handle to close\n");
2c40f06f
WD
519+ }
520+}
521+
522+static long get_unique_session_id()
523+{
524+ long unique;
525+ char strSqlStatement[1024];
a9eff38d
WD
526+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
527+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
2c40f06f
WD
528+
529+ if (db_handle != NULL) {
530+ /* choose the appropriate select statement based upon which DBMS we're using.
531+ * different datbases use different methods to get a unique ID. Some use a counter
532+ * object (sequence), others use an auto increment datatype and have a method
533+ * to get the last ID inserted using this connection. */
534+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) {
a9eff38d
WD
535+ snprintf(strSqlStatement, sizeof strSqlStatement,
536+ "SELECT NEXTVAL('%s');", lp_sequence_name(module_id));
2c40f06f 537+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) {
a9eff38d
WD
538+ snprintf(strSqlStatement, sizeof strSqlStatement,
539+ "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id));
2c40f06f 540+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) {
a9eff38d
WD
541+ snprintf(strSqlStatement, sizeof strSqlStatement,
542+ "VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id));
543+ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */
544+ snprintf(strSqlStatement, sizeof strSqlStatement,
545+ "SELECT LAST_INSERT_ID()");
546+ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */
547+ snprintf(strSqlStatement, sizeof strSqlStatement,
548+ "SELECT @@IDENTITY");
549+ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */
550+ snprintf(strSqlStatement, sizeof strSqlStatement,
551+ lp_custom_unique_id_select(module_id));
2c40f06f
WD
552+ }
553+
554+ /* bind the 1st column to unique */
555+ SQLBindCol(sql_statement_handle,1,SQL_C_LONG,&unique,150,&V_OD_err);
556+ /* execute the SQL statement */
557+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
558+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
559+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
560+ rprintf(FERROR,"Error at get_sequence: Error in Select! %s %s\n",strSqlStatement,V_OD_msg);
561+ } else {
562+ result = SQLFetch(sql_statement_handle);
563+ if (result != SQL_NO_DATA && unique != 0) {
564+ rprintf(FINFO,"Got unique sequence! %ld\n",unique);
565+ } else {
566+ rprintf(FERROR,"Error at get_sequence: Didn't get unique session ID\n");
567+ }
568+ /* Close the cursor so the statement can be re-used */
569+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
570+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
571+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
572+ rprintf(FERROR,"Error at get_sequence: Error in closing SQL statement handle %s\n",V_OD_msg);
573+ return unique;
574+ }
575+ return unique;
576+ }
577+ }
578+ rprintf(FERROR,"Error at get_sequence: Not connected to database\n");
579+ return -1;
580+}
581+
2c40f06f
WD
582+void db_log_session()
583+{
584+ char strSqlStatement[1024];
585+ int gotSessionID = 0;
a9eff38d
WD
586+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
587+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
588+
589+ if (!lp_database_logging(module_id))
590+ return;
591+
592+ if (db_handle != NULL) {
593+ /* if we're using a sequence via the nextval command to
594+ * get a unique ID, we need to get it before we do the
595+ * insert. We also get the unique ID now if custom,
596+ * and get_custom_id_before_insert is set. */
2c40f06f 597+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0
a9eff38d
WD
598+ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0
599+ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0
600+ || (strcmp(lp_unique_id_method(module_id),"custom") == 0
601+ && lp_get_custom_id_before_insert(module_id))) {
2c40f06f
WD
602+ session_id = get_unique_session_id();
603+ gotSessionID = 1;
a9eff38d
WD
604+ snprintf(strSqlStatement, sizeof strSqlStatement,
605+ "INSERT INTO %s (id, date, ip_address, username, module_name, module_path, process_id) VALUES ('%ld', '%s', '%s', '%s','%s','%s','%d');",
606+ lp_session_table_name(module_id), session_id, timestring(time(NULL)), client_addr(0),
607+ auth_user, lp_name(module_id), lp_path(module_id), getpid());
2c40f06f
WD
608+ } else {
609+ /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */
a9eff38d
WD
610+ snprintf(strSqlStatement, sizeof strSqlStatement,
611+ "INSERT INTO %s (date, ip_address, username, module_name, module_path, process_id) VALUES ('%s', '%s', '%s', '%s','%s','%d');",
612+ lp_session_table_name(module_id), timestring(time(NULL)), client_addr(0), auth_user,
613+ lp_name(module_id), lp_path(module_id), getpid());
2c40f06f
WD
614+ }
615+
616+ /* Insert the new session into the database */
617+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
618+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
619+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
620+ rprintf(FERROR,"Error at db_log_session: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
621+ }
622+
623+ /* close the cursor so the statement handle can be re-used. */
624+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
625+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
626+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
627+ rprintf(FERROR,"Error in resetting SQL statement handle %s\n",V_OD_msg);
628+ }
629+ /* get the session ID for databases that give the unique ID after an insert */
630+ if (gotSessionID == 0) {
631+ session_id = get_unique_session_id();
632+ }
a9eff38d 633+ } else {
2c40f06f
WD
634+ rprintf(FERROR,"Error at db_log_session: Not connected to database!\n");
635+ }
636+}
637+
638+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation)
639+{
640+ extern struct stats stats;
637ba3ff
WD
641+ char strSqlStatement[2048];
642+ char strFileName[MAXPATHLEN];
643+ char *strFileNamePtr;
2c40f06f
WD
644+ char strFileSize[255];
645+ int64 intBytesTransferred;
646+ int64 intCheckSumBytes;
a9eff38d
WD
647+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
648+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
2c40f06f 649+
a9eff38d
WD
650+ if (!lp_database_logging(module_id))
651+ return;
637ba3ff 652+
a9eff38d 653+ if (db_handle != NULL) {
bd68c3c2 654+ strFileNamePtr = f_name(file, NULL);
a9eff38d
WD
655+ if (am_sender && file->dir.root) {
656+ pathjoin(strFileName, sizeof strFileName,
657+ file->dir.root, strFileNamePtr);
658+ strFileNamePtr = strFileName;
659+ }
660+ clean_fname(strFileNamePtr, 0);
661+ if (*strFileNamePtr == '/')
662+ strFileNamePtr++;
2c40f06f 663+
1aa236e1 664+ snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)F_LENGTH(file));
a9eff38d
WD
665+ if (am_sender) {
666+ intBytesTransferred = stats.total_written - initial_stats->total_written;
667+ } else {
668+ intBytesTransferred = stats.total_read - initial_stats->total_read;
669+ }
2c40f06f 670+
a9eff38d
WD
671+ if (!am_sender) {
672+ intCheckSumBytes = stats.total_written - initial_stats->total_written;
2c40f06f 673+ } else {
a9eff38d 674+ intCheckSumBytes = stats.total_read - initial_stats->total_read;
2c40f06f 675+ }
a9eff38d
WD
676+
677+ snprintf(strSqlStatement, sizeof strSqlStatement,
678+ "INSERT INTO %s (session_id,date, file_name, file_size, bytes_transferred, checksum_bytes_transferred, operation) VALUES ('%ld','%s','%s','%s','%Ld','%Ld','%s');",
679+ lp_transfer_table_name(module_id), session_id, timestring(time(NULL)),
680+ sanitizeSql(strFileNamePtr), strFileSize, intBytesTransferred,
681+ intCheckSumBytes, operation);
682+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
683+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
684+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
685+ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
686+ if (result == SQL_INVALID_HANDLE)
687+ rprintf(FERROR,"INVALID HANDLE\n");
688+ }
689+ } else {
690+ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n");
2c40f06f
WD
691+ }
692+}
693+
2c40f06f
WD
694+void db_log_exit(int code, const char *file, int line)
695+{
696+ char strSqlStatement[2048];
697+ const char *error_text;
698+ extern struct stats stats;
a9eff38d
WD
699+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
700+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
701+
702+ if (!lp_database_logging(module_id))
703+ return;
704+
2c40f06f 705+ if (db_handle != NULL) {
a9eff38d
WD
706+ if (code != 0) {
707+ error_text = rerr_name(code);
708+ if (!error_text) {
709+ error_text = "unexplained error";
2c40f06f 710+ }
a9eff38d
WD
711+ } else {
712+ error_text = "";
713+ }
714+ snprintf(strSqlStatement, sizeof strSqlStatement,
715+ "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');",
716+ lp_exit_table_name(module_id), session_id, timestring(time(NULL)), stats.total_written,
717+ stats.total_read, stats.total_size, error_text, code, file, line, getpid());
2c40f06f 718+
a9eff38d 719+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
2c40f06f 720+
a9eff38d
WD
721+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
722+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
723+ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
2c40f06f
WD
724+ }
725+ } else {
726+ rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
727+ }
728+}
a9eff38d
WD
729+
730+void db_log_delete(char *fname, int mode)
731+{
732+ char strSqlStatement[2048];
733+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
734+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
735+
736+ if (!am_daemon || dry_run || !lp_database_logging(module_id))
737+ return;
738+
739+ if (db_handle != NULL) {
740+ snprintf(strSqlStatement, sizeof strSqlStatement,
741+ "INSERT INTO %s (session_id, date, path, mode) VALUES ('%ld','%s','%s','%d');",
742+ lp_delete_table_name(module_id), session_id, timestring(time(NULL)), sanitizeSql(fname), mode);
743+
744+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
745+
746+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
747+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
748+ rprintf(FERROR,"Error at db_log_delete: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
749+ }
750+ } else {
751+ rprintf(FERROR,"Error at db_log_delete: Not connected to database!\n");
752+ }
753+}
754+
755+void db_log_error(enum logcode code, int errcode, const char *format,...)
756+{
757+ char strSqlStatement[MAXPATHLEN+1024];
758+ va_list ap;
759+ char buf[MAXPATHLEN+512];
760+ size_t len;
761+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
762+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
763+
764+ if (!lp_database_logging(module_id))
765+ return;
766+
767+ va_start(ap, format);
768+ len = vsnprintf(buf, sizeof buf, format, ap);
769+ va_end(ap);
770+
771+ /* Deal with buffer overruns. Instead of panicking, just
772+ * truncate the resulting string. (Note that configure ensures
773+ * that we have a vsnprintf() that doesn't ever return -1.) */
774+ if (len > sizeof buf - 1) {
775+ const char ellipsis[] = "[...]";
776+
777+ /* Reset length, and zero-terminate the end of our buffer */
778+ len = sizeof buf - 1;
779+ buf[len] = '\0';
780+
781+ /* Copy the ellipsis to the end of the string, but give
782+ * us one extra character:
783+ *
784+ * v--- null byte at buf[sizeof buf - 1]
785+ * abcdefghij0
786+ * -> abcd[...]00 <-- now two null bytes at end
787+ *
788+ * If the input format string has a trailing newline,
789+ * we copy it into that extra null; if it doesn't, well,
790+ * all we lose is one byte. */
791+ strncpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis);
792+ if (format[strlen(format)-1] == '\n') {
793+ buf[len-1] = '\n';
794+ }
795+ }
796+
797+ if (db_handle != NULL) {
798+ snprintf(strSqlStatement, sizeof strSqlStatement,
799+ "INSERT INTO %s (session_id, date, logcode, error_number, error_text) VALUES ('%ld','%s','%d','%d','%s');",
800+ lp_error_table_name(module_id), session_id, timestring(time(NULL)), code, errcode, sanitizeSql(buf));
801+
802+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
803+
804+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
805+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
806+ rprintf(FERROR,"Error at db_log_error: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
807+ }
808+ } else {
809+ rprintf(FERROR,"Error at db_log_error: Not connected to database!\n");
810+ }
811+}
9a7eef96
WD
812--- old/instructions
813+++ new/instructions
a9eff38d
WD
814@@ -0,0 +1,84 @@
815+This patch adds the following options:
816+
817+"database logging"
818+ If set to True, rsync will attempt to connect to
819+ the specified datasource and write to the named tables.
820+ Defaults to False.
821+
822+"database datasource"
823+ Specifies the name of the ODBC data source to use.
824+
825+"database username"
826+ The username to use when connecting to the database.
827+
828+"database password"
829+ The password to use when connecting to the database.
830+
831+"transfer table name"
832+ The name of the transfer table to log to. This table contains individual
833+ filenames, file sizes, bytes transferred, checksum bytes transferred,
834+ operation (send or receive), and a timestamp.
835+
836+"session table name"
837+ The name of the session table to log to. This table contains the username,
838+ module name, module path, ip address, process ID, and a timestamp.
839+
840+"exit table name"
841+
842+ The name of the exit table to log to. This table contains the total bytes
843+ read, total bytes written, total size of all files, error code, line the
844+ error occured at, file the error occured at and the text of the error.
845+ (most of which will be blank if the program exited normally).
846+
847+"delete table name"
848+
849+ The name of the table to log deleted files/directories to.
850+
851+"error table name"
852+
853+ The name of the table errors will be logged to.
854+
855+"unique id method"
856+ Different databases use different methods to get a unique identifier.
857+ Some databases support sequence objects, and use various forms of the
858+ nextval command to retrieve a unique identifier from it. Other databases
859+ support an autonumber field, and support different methds of retrieving
860+ the ID used in the last insert. Valid values for this option are:
861+
862+ nextval-postgres
863+ uses the syntax of nextval for PostgreSQL databases
864+
865+ nextval-oracle
866+ uses the syntax of nextval for Oracle databases
867+
868+ nextval-db2
869+ uses the syntax of nextval for DB2 databases
870+
871+ last_insert_id
872+ uses the last_insert_id() command for the MySQL databases
873+
874+ @@IDENTITY
875+ uses the @@IDENTITY command for Sybase databases
876+
877+ custom
878+ Define your own method to get a unique identifier. See the
879+ "custom unique id select", and the "get custom id before select"
880+ parameters.
881+
882+"sequence name"
883+ If your database supports sequences, list the name of the sequence to use
884+ for the session unique identifier.
885+
886+"custom unique id select"
887+ Only used if you specify the custom method in "unique id method". This is
888+ a SQL statement to be executed to get a unique ID. This SQL statement must
889+ return one column with the unique ID to use for the session ID. Should be
890+ used in concert with the "get custom id before select" parameter.
891+
892+"get custom id before insert"
893+ This parameter is ignored unless the "unique id method" is set to custom.
894+ If set to true, the "custom unique id select" statement will be executed
895+ BEFORE the session row is inserted into the database. (as is done when a
896+ sequence is used for unique IDs). If False the statement will be executed
897+ after the session row is inserted (as is done when the session ID is
898+ automatically generates unique IDs). Defaults to True.
9a7eef96
WD
899--- old/loadparm.c
900+++ new/loadparm.c
2ac9130a 901@@ -124,9 +124,16 @@ typedef struct
51b0132f
WD
902 {
903 char *auth_users;
904 char *comment;
905+ char *custom_unique_id_select;
2c40f06f 906+ char *database_datasource;
2c40f06f 907+ char *database_password;
51b0132f 908+ char *database_username;
a9eff38d 909+ char *delete_table_name;
51b0132f 910 char *dont_compress;
a9eff38d 911+ char *error_table_name;
51b0132f
WD
912 char *exclude;
913 char *exclude_from;
2c40f06f 914+ char *exit_table_name;
51b0132f
WD
915 char *filter;
916 char *gid;
917 char *hosts_allow;
2ac9130a 918@@ -144,15 +151,21 @@ typedef struct
51b0132f
WD
919 char *prexfer_exec;
920 char *refuse_options;
921 char *secrets_file;
2c40f06f 922+ char *sequence_name;
51b0132f
WD
923+ char *session_table_name;
924 char *temp_dir;
925+ char *transfer_table_name;
926 char *uid;
2c40f06f 927+ char *unique_id_method;
51b0132f
WD
928
929 int max_connections;
930 int max_verbosity;
0ffcefad 931 int syslog_facility;
51b0132f
WD
932 int timeout;
933
934+ BOOL database_logging;
12b04b40 935 BOOL fake_super;
2c40f06f
WD
936+ BOOL get_custom_id_before_insert;
937 BOOL ignore_errors;
51b0132f
WD
938 BOOL ignore_nonreadable;
939 BOOL list;
2ac9130a 940@@ -172,9 +185,16 @@ static service sDefault =
51b0132f 941 {
32f6df4b
WD
942 /* auth_users; */ NULL,
943 /* comment; */ NULL,
944+ /* custom_unique_id_select; */ NULL,
945+ /* database_datasource; */ NULL,
946+ /* database_password; */ NULL,
947+ /* database_username; */ NULL,
a9eff38d 948+ /* delete_table_name; */ NULL,
2ac9130a 949 /* dont_compress; */ DEFAULT_DONT_COMPRESS,
a9eff38d 950+ /* error_table_name; */ NULL,
32f6df4b
WD
951 /* exclude; */ NULL,
952 /* exclude_from; */ NULL,
953+ /* exit_table_name; */ NULL,
954 /* filter; */ NULL,
955 /* gid; */ NOBODY_GROUP,
956 /* hosts_allow; */ NULL,
2ac9130a 957@@ -192,15 +212,21 @@ static service sDefault =
32f6df4b
WD
958 /* prexfer_exec; */ NULL,
959 /* refuse_options; */ NULL,
960 /* secrets_file; */ NULL,
961+ /* sequence_name; */ NULL,
962+ /* session_table_name; */ NULL,
963 /* temp_dir; */ NULL,
964+ /* transfer_table_name; */ NULL,
965 /* uid; */ NOBODY_USER,
966+ /* unique_id_method; */ NULL,
51b0132f 967
32f6df4b
WD
968 /* max_connections; */ 0,
969 /* max_verbosity; */ 1,
0ffcefad 970 /* syslog_facility; */ LOG_DAEMON,
32f6df4b 971 /* timeout; */ 0,
51b0132f 972
32f6df4b 973+ /* database_logging; */ False,
12b04b40 974 /* fake_super; */ False,
32f6df4b
WD
975+ /* get_custom_id_before_insert; */ True,
976 /* ignore_errors; */ False,
977 /* ignore_nonreadable; */ False,
978 /* list; */ True,
2ac9130a 979@@ -299,11 +325,20 @@ static struct parm_struct parm_table[] =
51b0132f
WD
980
981 {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0},
982 {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL,0},
983+ {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0},
984+ {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0},
985+ {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging, NULL,0},
986+ {"database password", P_STRING, P_LOCAL, &sDefault.database_password, NULL,0},
987+ {"database username", P_STRING, P_LOCAL, &sDefault.database_username, NULL,0},
a9eff38d 988+ {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0},
51b0132f 989 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
a9eff38d 990+ {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0},
51b0132f
WD
991 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
992 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
993+ {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name, NULL,0},
12b04b40 994 {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
51b0132f
WD
995 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
996+ {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0},
997 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
998 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
999 {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL,0},
2ac9130a 1000@@ -328,12 +363,16 @@ static struct parm_struct parm_table[] =
a9eff38d
WD
1001 {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0},
1002 {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0},
1003 {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0},
51b0132f
WD
1004+ {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name, NULL,0},
1005+ {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0},
a9eff38d 1006 {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0},
0ffcefad 1007 {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0},
a9eff38d
WD
1008 {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0},
1009 {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0},
1010 {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0},
51b0132f 1011+ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0},
a9eff38d 1012 {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0},
51b0132f 1013+ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0},
a9eff38d
WD
1014 {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0},
1015 {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL,0},
1016 {NULL, P_BOOL, P_NONE, NULL, NULL,0}
2ac9130a 1017@@ -389,9 +428,16 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Global
51b0132f
WD
1018
1019 FN_LOCAL_STRING(lp_auth_users, auth_users)
1020 FN_LOCAL_STRING(lp_comment, comment)
1021+FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select)
2c40f06f 1022+FN_LOCAL_STRING(lp_database_datasource, database_datasource)
2c40f06f 1023+FN_LOCAL_STRING(lp_database_password, database_password)
51b0132f 1024+FN_LOCAL_STRING(lp_database_username, database_username)
a9eff38d 1025+FN_LOCAL_STRING(lp_delete_table_name,delete_table_name)
51b0132f 1026 FN_LOCAL_STRING(lp_dont_compress, dont_compress)
a9eff38d 1027+FN_LOCAL_STRING(lp_error_table_name,error_table_name)
51b0132f
WD
1028 FN_LOCAL_STRING(lp_exclude, exclude)
1029 FN_LOCAL_STRING(lp_exclude_from, exclude_from)
2c40f06f 1030+FN_LOCAL_STRING(lp_exit_table_name, exit_table_name)
51b0132f
WD
1031 FN_LOCAL_STRING(lp_filter, filter)
1032 FN_LOCAL_STRING(lp_gid, gid)
1033 FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
2ac9130a 1034@@ -409,15 +455,21 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf
51b0132f
WD
1035 FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
1036 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
1037 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
2c40f06f 1038+FN_LOCAL_STRING(lp_sequence_name,sequence_name)
51b0132f 1039+FN_LOCAL_STRING(lp_session_table_name,session_table_name)
0ffcefad 1040 FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
51b0132f
WD
1041 FN_LOCAL_STRING(lp_temp_dir, temp_dir)
1042+FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name)
1043 FN_LOCAL_STRING(lp_uid, uid)
2c40f06f 1044+FN_LOCAL_STRING(lp_unique_id_method,unique_id_method)
51b0132f
WD
1045
1046 FN_LOCAL_INTEGER(lp_max_connections, max_connections)
1047 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
1048 FN_LOCAL_INTEGER(lp_timeout, timeout)
1049
1050+FN_LOCAL_BOOL(lp_database_logging, database_logging)
12b04b40 1051 FN_LOCAL_BOOL(lp_fake_super, fake_super)
2c40f06f
WD
1052+FN_LOCAL_BOOL(lp_get_custom_id_before_insert,get_custom_id_before_insert)
1053 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
1054 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
51b0132f 1055 FN_LOCAL_BOOL(lp_list, list)
9a7eef96
WD
1056--- old/log.c
1057+++ new/log.c
2ac9130a 1058@@ -96,7 +96,7 @@ struct {
2c40f06f
WD
1059 /*
1060 * Map from rsync error code to name, or return NULL.
1061 */
1062-static char const *rerr_name(int code)
1063+char const *rerr_name(int code)
1064 {
1065 int i;
1066 for (i = 0; rerr_names[i].name; i++) {
9a7eef96
WD
1067--- old/receiver.c
1068+++ new/receiver.c
5ff5e82f 1069@@ -111,6 +111,10 @@ int get_tmpname(char *fnametmp, char *fn
93ca4d27 1070
a9eff38d 1071 if (maxname < 1) {
93ca4d27 1072 rprintf(FERROR, "temporary filename too long: %s\n", fname);
a9eff38d
WD
1073+#ifdef HAVE_LIBODBC
1074+ db_log_error(FERROR,13, "temporary filename too long: %s\n",
93ca4d27 1075+ fname);
a9eff38d
WD
1076+#endif
1077 fnametmp[0] = '\0';
1078 return 0;
1079 }
9c25eef5 1080@@ -176,6 +180,10 @@ static int receive_data(int f_in, char *
88f7513d
WD
1081 if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) {
1082 rsyserr(FERROR, errno, "lseek of %s returned %.0f, not %.0f",
1083 full_fname(fname), (double)j, (double)offset);
1084+#ifdef HAVE_LIBODBC
1085+ db_log_error(FERROR, 14, "lseek failed on %s",
1086+ full_fname(fname));
1087+#endif
1088 exit_cleanup(RERR_FILEIO);
1089 }
1090 }
9c25eef5 1091@@ -233,6 +241,11 @@ static int receive_data(int f_in, char *
88f7513d
WD
1092 "lseek of %s returned %.0f, not %.0f",
1093 full_fname(fname),
1094 (double)pos, (double)offset);
a9eff38d
WD
1095+#ifdef HAVE_LIBODBC
1096+ db_log_error(FERROR, 14,
1097+ "lseek failed on %s",
1098+ full_fname(fname));
1099+#endif
1100 exit_cleanup(RERR_FILEIO);
1101 }
1102 continue;
9c25eef5 1103@@ -258,6 +271,9 @@ static int receive_data(int f_in, char *
a9eff38d
WD
1104 report_write_error:
1105 rsyserr(FERROR, errno, "write failed on %s",
1106 full_fname(fname));
1107+#ifdef HAVE_LIBODBC
1108+ db_log_error(FERROR, 15, "write failed on %s",full_fname(fname));
1109+#endif
1110 exit_cleanup(RERR_FILEIO);
1111 }
1112
9c25eef5 1113@@ -301,6 +317,12 @@ static void handle_delayed_updates(char
93ca4d27 1114 rsyserr(FERROR, errno,
a9eff38d 1115 "rename failed for %s (from %s)",
93ca4d27 1116 full_fname(fname), partialptr);
a9eff38d
WD
1117+#ifdef HAVE_LIBODBC
1118+ db_log_error(FERROR, 16,
1119+ "rename failed for %s (from %s)",
1120+ full_fname(fname),
93ca4d27 1121+ partialptr);
a9eff38d
WD
1122+#endif
1123 } else {
60a8bf36 1124 if (remove_source_files
1aa236e1 1125 || (preserve_hard_links && F_IS_HLINKED(file)))
9c25eef5 1126@@ -454,6 +476,9 @@ int recv_files(int f_in, char *local_nam
a9eff38d
WD
1127 if (server_filter_list.head
1128 && check_filter(&server_filter_list, fname, 0) < 0) {
1129 rprintf(FERROR, "attempt to hack rsync failed.\n");
1130+#ifdef HAVE_LIBODBC
88f7513d 1131+ db_log_error(FERROR,17,"attempt to hack rsync failed.");
a9eff38d
WD
1132+#endif
1133 exit_cleanup(RERR_PROTOCOL);
1134 }
1135
9c25eef5 1136@@ -512,6 +537,11 @@ int recv_files(int f_in, char *local_nam
a9eff38d
WD
1137 rprintf(FERROR,
1138 "invalid basis_dir index: %d.\n",
1139 fnamecmp_type);
1140+#ifdef HAVE_LIBODBC
1141+ db_log_error(FERROR, 18,
1142+ "invalid basis_dir index: %d.\n",
1143+ fnamecmp_type);
1144+#endif
1145 exit_cleanup(RERR_PROTOCOL);
1146 }
1147 pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
9c25eef5 1148@@ -560,6 +590,9 @@ int recv_files(int f_in, char *local_nam
b3ea4757 1149 } else if (do_fstat(fd1,&st) != 0) {
a9eff38d
WD
1150 rsyserr(FERROR, errno, "fstat %s failed",
1151 full_fname(fnamecmp));
1152+#ifdef HAVE_LIBODBC
88f7513d 1153+ db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp));
a9eff38d 1154+#endif
1aa236e1 1155 discard_receive_data(f_in, F_LENGTH(file));
a9eff38d 1156 close(fd1);
2ac9130a 1157 if (inc_recurse)
9c25eef5 1158@@ -575,6 +608,9 @@ int recv_files(int f_in, char *local_nam
a9eff38d
WD
1159 */
1160 rprintf(FERROR,"recv_files: %s is a directory\n",
1161 full_fname(fnamecmp));
1162+#ifdef HAVE_LIBODBC
88f7513d 1163+ db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp));
a9eff38d 1164+#endif
1aa236e1 1165 discard_receive_data(f_in, F_LENGTH(file));
a9eff38d 1166 close(fd1);
2ac9130a 1167 if (inc_recurse)
9c25eef5 1168@@ -609,6 +645,9 @@ int recv_files(int f_in, char *local_nam
a9eff38d
WD
1169 if (fd2 == -1) {
1170 rsyserr(FERROR, errno, "open %s failed",
1171 full_fname(fname));
1172+#ifdef HAVE_LIBODBC
88f7513d 1173+ db_log_error(FERROR,22, "open %s failed", full_fname(fname));
a9eff38d 1174+#endif
1aa236e1 1175 discard_receive_data(f_in, F_LENGTH(file));
a9eff38d
WD
1176 if (fd1 != -1)
1177 close(fd1);
9c25eef5 1178@@ -646,6 +685,10 @@ int recv_files(int f_in, char *local_nam
a9eff38d
WD
1179 if (fd2 == -1) {
1180 rsyserr(FERROR, errno, "mkstemp %s failed",
1181 full_fname(fnametmp));
1182+#ifdef HAVE_LIBODBC
88f7513d 1183+ db_log_error(FERROR, 22, "mkstemp %s failed",
a9eff38d
WD
1184+ full_fname(fnametmp));
1185+#endif
1aa236e1 1186 discard_receive_data(f_in, F_LENGTH(file));
a9eff38d
WD
1187 if (fd1 != -1)
1188 close(fd1);
9c25eef5 1189@@ -668,12 +711,19 @@ int recv_files(int f_in, char *local_nam
1aa236e1 1190 fname, fd2, F_LENGTH(file));
2c40f06f 1191
a859733e 1192 log_item(log_code, file, &initial_stats, iflags, NULL);
637ba3ff 1193+#ifdef HAVE_LIBODBC
13bed3dd 1194+ db_log_transfer(file, &initial_stats, "receive");
2c40f06f 1195+#endif
dc3ae351 1196
13bed3dd 1197 if (fd1 != -1)
dc3ae351 1198 close(fd1);
a9eff38d
WD
1199 if (close(fd2) < 0) {
1200 rsyserr(FERROR, errno, "close failed on %s",
1201 full_fname(fnametmp));
1202+#ifdef HAVE_LIBODBC
1203+ db_log_error(FERROR, 23, "close failed on %s",
1204+ full_fname(fnametmp));
1205+#endif
1206 exit_cleanup(RERR_FILEIO);
1207 }
1208
9c25eef5 1209@@ -730,6 +780,12 @@ int recv_files(int f_in, char *local_nam
93ca4d27 1210 rprintf(msgtype,
a9eff38d 1211 "%s: %s failed verification -- update %s%s.\n",
93ca4d27 1212 errstr, fname, keptstr, redostr);
a9eff38d 1213+#ifdef HAVE_LIBODBC
88f7513d 1214+ db_log_error(msgtype,24,
a9eff38d 1215+ "%s: %s failed verification -- update %s%s.\n",
93ca4d27 1216+ errstr, fname,
a9eff38d
WD
1217+ keptstr, redostr);
1218+#endif
1219 }
5ff5e82f 1220 if (!redoing) {
590329e5 1221 send_msg_int(MSG_REDO, ndx);
9a7eef96
WD
1222--- old/sender.c
1223+++ new/sender.c
2ac9130a 1224@@ -344,6 +344,9 @@ void send_files(int f_in, int f_out)
a859733e 1225 end_progress(st.st_size);
36bbf3d1 1226
a859733e 1227 log_item(log_code, file, &initial_stats, iflags, NULL);
637ba3ff 1228+#ifdef HAVE_LIBODBC
9be39c35 1229+ db_log_transfer(file, &initial_stats,"send");
2c40f06f 1230+#endif
2c40f06f 1231
9be39c35
WD
1232 if (mbuf) {
1233 j = unmap_file(mbuf);