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