A little more cleanup.
[rsync/rsync-patches.git] / ODBC-dblog.diff
... / ...
CommitLineData
1Add support for logging daemon messages to an SQL database.
2
3To use this patch, run these commands for a successful build:
4
5 patch -p1 <patches/ODBC-dblog.diff
6 ./prepare-source
7 ./configure --enable-ODBC
8 make
9
10See the newly-created file "instructions" for more info.
11
12--- old/Makefile.in
13+++ new/Makefile.in
14@@ -32,7 +32,7 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/
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
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@
20 OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o hashtable.o \
21 fileio.o batch.o clientname.o chmod.o acls.o xattrs.o
22 OBJS3=progress.o pipe.o
23--- old/cleanup.c
24+++ new/cleanup.c
25@@ -26,6 +26,7 @@ extern int am_server;
26 extern int am_daemon;
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;
32 extern char *logfile_name;
33@@ -173,8 +174,13 @@ NORETURN void _exit_cleanup(int code, co
34 code = exit_code = RERR_PARTIAL;
35 }
36
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);
40+#ifdef HAVE_LIBODBC
41+ db_log_exit(code, file, line);
42+ db_log_close();
43+#endif
44+ }
45
46 /* FALLTHROUGH */
47 #include "case_N.h"
48--- old/clientserver.c
49+++ new/clientserver.c
50@@ -410,6 +410,9 @@ static int rsync_module(int f_in, int f_
51 XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES);
52
53 log_init(1);
54+#ifdef HAVE_LIBODBC
55+ db_log_open();
56+#endif
57
58 #ifdef HAVE_PUTENV
59 if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
60@@ -649,6 +652,9 @@ static int rsync_module(int f_in, int f_
61 rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n",
62 am_sender ? "on" : "to",
63 request, auth_user, host, addr);
64+#ifdef HAVE_LIBODBC
65+ db_log_session();
66+#endif
67 } else {
68 rprintf(FLOG, "rsync %s %s from %s (%s)\n",
69 am_sender ? "on" : "to",
70--- old/configure.in
71+++ new/configure.in
72@@ -659,6 +659,12 @@ if test x"$with_included_popt" != x"yes"
73 AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
74 fi
75
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)
78+ AC_CHECK_LIB(odbc,SQLExecDirect)
79+ EXTRA_OBJECT="$EXTRA_OBJECT dblog.o"
80+ AC_SUBST(EXTRA_OBJECT) ])
81+
82 AC_MSG_CHECKING([whether to use included libpopt])
83 if test x"$with_included_popt" = x"yes"; then
84 AC_MSG_RESULT($srcdir/popt)
85--- old/db_log_error-list.txt
86+++ new/db_log_error-list.txt
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?
123--- old/dblog-tables-mysql.sql
124+++ new/dblog-tables-mysql.sql
125@@ -0,0 +1,64 @@
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,
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,
165+ process_id int NOT NULL,
166+ Primary Key (id),
167+ foreign key (session_id) references session (id)
168+);
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+);
190--- old/dblog-tables-postgresql.sql
191+++ new/dblog-tables-postgresql.sql
192@@ -0,0 +1,67 @@
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(),
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,
234+ "process_id" int NOT NULL,
235+ Primary Key (id),
236+ foreign key (session_id) references session (id)
237+);
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+);
260--- old/dblog.c
261+++ new/dblog.c
262@@ -0,0 +1,549 @@
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+
272+#ifdef HAVE_SQL_H
273+#include <sql.h>
274+#else
275+#ifdef HAVE_ODBC_SQL_H
276+#include <odbc/sql.h>
277+#endif
278+#endif
279+
280+#ifdef HAVE_SQLEXT_H
281+#include <sqlext.h>
282+#else
283+#ifdef HAVE_ODBC_SQLEXT_H
284+#include <odbc/sqlext.h>
285+#endif
286+#endif
287+
288+#ifdef HAVE_SQLTYPES_H
289+#include <sqltypes.h>
290+#else
291+#ifdef HAVE_ODBC_SQLTYPES_H
292+#include <odbc/sqltypes.h>
293+#endif
294+#endif
295+
296+SQLHENV db_environ_handle; /* Handle ODBC environment */
297+long result; /* result of functions */
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;
303+extern int am_sender;
304+extern int am_generator;
305+extern char *auth_user;
306+extern int module_id;
307+extern int dry_run;
308+
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+{
362+ if (!lp_database_logging(module_id))
363+ return;
364+
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+ }
371+
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+ }
389+
390+ /* Set connection attributes for the generator db connection */
391+ SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
392+
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);
397+
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+ }
421+
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;
439+ }
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;
469+ }
470+}
471+
472+void db_log_close()
473+{
474+ if (!lp_database_logging(module_id))
475+ return;
476+
477+ if (am_generator) {
478+ if (sql_statement_handle_g != NULL) {
479+ /* free the statement handle first */
480+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g);
481+ sql_statement_handle_g = NULL;
482+ } else {
483+ rprintf(FERROR,"No generator sql statement handle to close\n");
484+ }
485+
486+ if (db_handle_g != NULL) {
487+ /* disconnect, and free the database handle. */
488+ SQLDisconnect(db_handle_g);
489+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
490+ db_handle_g = NULL;
491+ } else {
492+ rprintf(FERROR,"Generator database connection already closed\n");
493+ }
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;
499+ } else {
500+ rprintf(FERROR,"No receiver sql statement handle to close\n");
501+ }
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");
519+ }
520+}
521+
522+static long get_unique_session_id()
523+{
524+ long unique;
525+ char strSqlStatement[1024];
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;
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) {
535+ snprintf(strSqlStatement, sizeof strSqlStatement,
536+ "SELECT NEXTVAL('%s');", lp_sequence_name(module_id));
537+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) {
538+ snprintf(strSqlStatement, sizeof strSqlStatement,
539+ "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id));
540+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) {
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));
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+
582+void db_log_session()
583+{
584+ char strSqlStatement[1024];
585+ int gotSessionID = 0;
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. */
597+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0
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))) {
602+ session_id = get_unique_session_id();
603+ gotSessionID = 1;
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());
608+ } else {
609+ /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */
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());
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+ }
633+ } else {
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;
641+ char strSqlStatement[2048];
642+ char strFileName[MAXPATHLEN];
643+ char *strFileNamePtr;
644+ char strFileSize[255];
645+ int64 intBytesTransferred;
646+ int64 intCheckSumBytes;
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;
649+
650+ if (!lp_database_logging(module_id))
651+ return;
652+
653+ if (db_handle != NULL) {
654+ strFileNamePtr = f_name(file, NULL);
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++;
663+
664+ snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)F_LENGTH(file));
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+ }
670+
671+ if (!am_sender) {
672+ intCheckSumBytes = stats.total_written - initial_stats->total_written;
673+ } else {
674+ intCheckSumBytes = stats.total_read - initial_stats->total_read;
675+ }
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");
691+ }
692+}
693+
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;
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+
705+ if (db_handle != NULL) {
706+ if (code != 0) {
707+ error_text = rerr_name(code);
708+ if (!error_text) {
709+ error_text = "unexplained error";
710+ }
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());
718+
719+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
720+
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);
724+ }
725+ } else {
726+ rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
727+ }
728+}
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+}
812--- old/instructions
813+++ new/instructions
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.
899--- old/loadparm.c
900+++ new/loadparm.c
901@@ -124,9 +124,16 @@ typedef struct
902 {
903 char *auth_users;
904 char *comment;
905+ char *custom_unique_id_select;
906+ char *database_datasource;
907+ char *database_password;
908+ char *database_username;
909+ char *delete_table_name;
910 char *dont_compress;
911+ char *error_table_name;
912 char *exclude;
913 char *exclude_from;
914+ char *exit_table_name;
915 char *filter;
916 char *gid;
917 char *hosts_allow;
918@@ -144,15 +151,21 @@ typedef struct
919 char *prexfer_exec;
920 char *refuse_options;
921 char *secrets_file;
922+ char *sequence_name;
923+ char *session_table_name;
924 char *temp_dir;
925+ char *transfer_table_name;
926 char *uid;
927+ char *unique_id_method;
928
929 int max_connections;
930 int max_verbosity;
931 int syslog_facility;
932 int timeout;
933
934+ BOOL database_logging;
935 BOOL fake_super;
936+ BOOL get_custom_id_before_insert;
937 BOOL ignore_errors;
938 BOOL ignore_nonreadable;
939 BOOL list;
940@@ -172,9 +185,16 @@ static service sDefault =
941 {
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,
948+ /* delete_table_name; */ NULL,
949 /* dont_compress; */ DEFAULT_DONT_COMPRESS,
950+ /* error_table_name; */ NULL,
951 /* exclude; */ NULL,
952 /* exclude_from; */ NULL,
953+ /* exit_table_name; */ NULL,
954 /* filter; */ NULL,
955 /* gid; */ NOBODY_GROUP,
956 /* hosts_allow; */ NULL,
957@@ -192,15 +212,21 @@ static service sDefault =
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,
967
968 /* max_connections; */ 0,
969 /* max_verbosity; */ 1,
970 /* syslog_facility; */ LOG_DAEMON,
971 /* timeout; */ 0,
972
973+ /* database_logging; */ False,
974 /* fake_super; */ False,
975+ /* get_custom_id_before_insert; */ True,
976 /* ignore_errors; */ False,
977 /* ignore_nonreadable; */ False,
978 /* list; */ True,
979@@ -299,11 +325,20 @@ static struct parm_struct parm_table[] =
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},
988+ {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0},
989 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
990+ {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0},
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},
994 {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0},
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},
1000@@ -328,12 +363,16 @@ static struct parm_struct parm_table[] =
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},
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},
1006 {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0},
1007 {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0},
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},
1011+ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0},
1012 {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0},
1013+ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0},
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}
1017@@ -389,9 +428,16 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Global
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)
1022+FN_LOCAL_STRING(lp_database_datasource, database_datasource)
1023+FN_LOCAL_STRING(lp_database_password, database_password)
1024+FN_LOCAL_STRING(lp_database_username, database_username)
1025+FN_LOCAL_STRING(lp_delete_table_name,delete_table_name)
1026 FN_LOCAL_STRING(lp_dont_compress, dont_compress)
1027+FN_LOCAL_STRING(lp_error_table_name,error_table_name)
1028 FN_LOCAL_STRING(lp_exclude, exclude)
1029 FN_LOCAL_STRING(lp_exclude_from, exclude_from)
1030+FN_LOCAL_STRING(lp_exit_table_name, exit_table_name)
1031 FN_LOCAL_STRING(lp_filter, filter)
1032 FN_LOCAL_STRING(lp_gid, gid)
1033 FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
1034@@ -409,15 +455,21 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf
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)
1038+FN_LOCAL_STRING(lp_sequence_name,sequence_name)
1039+FN_LOCAL_STRING(lp_session_table_name,session_table_name)
1040 FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility)
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)
1044+FN_LOCAL_STRING(lp_unique_id_method,unique_id_method)
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)
1051 FN_LOCAL_BOOL(lp_fake_super, fake_super)
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)
1055 FN_LOCAL_BOOL(lp_list, list)
1056--- old/log.c
1057+++ new/log.c
1058@@ -96,7 +96,7 @@ struct {
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++) {
1067--- old/receiver.c
1068+++ new/receiver.c
1069@@ -111,6 +111,10 @@ int get_tmpname(char *fnametmp, char *fn
1070
1071 if (maxname < 1) {
1072 rprintf(FERROR, "temporary filename too long: %s\n", fname);
1073+#ifdef HAVE_LIBODBC
1074+ db_log_error(FERROR,13, "temporary filename too long: %s\n",
1075+ fname);
1076+#endif
1077 fnametmp[0] = '\0';
1078 return 0;
1079 }
1080@@ -176,6 +180,10 @@ static int receive_data(int f_in, char *
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 }
1091@@ -233,6 +241,11 @@ static int receive_data(int f_in, char *
1092 "lseek of %s returned %.0f, not %.0f",
1093 full_fname(fname),
1094 (double)pos, (double)offset);
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;
1103@@ -258,6 +271,9 @@ static int receive_data(int f_in, char *
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
1113@@ -301,6 +317,12 @@ static void handle_delayed_updates(char
1114 rsyserr(FERROR, errno,
1115 "rename failed for %s (from %s)",
1116 full_fname(fname), partialptr);
1117+#ifdef HAVE_LIBODBC
1118+ db_log_error(FERROR, 16,
1119+ "rename failed for %s (from %s)",
1120+ full_fname(fname),
1121+ partialptr);
1122+#endif
1123 } else {
1124 if (remove_source_files
1125 || (preserve_hard_links && F_IS_HLINKED(file)))
1126@@ -454,6 +476,9 @@ int recv_files(int f_in, char *local_nam
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
1131+ db_log_error(FERROR,17,"attempt to hack rsync failed.");
1132+#endif
1133 exit_cleanup(RERR_PROTOCOL);
1134 }
1135
1136@@ -512,6 +537,11 @@ int recv_files(int f_in, char *local_nam
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,
1148@@ -560,6 +590,9 @@ int recv_files(int f_in, char *local_nam
1149 } else if (do_fstat(fd1,&st) != 0) {
1150 rsyserr(FERROR, errno, "fstat %s failed",
1151 full_fname(fnamecmp));
1152+#ifdef HAVE_LIBODBC
1153+ db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp));
1154+#endif
1155 discard_receive_data(f_in, F_LENGTH(file));
1156 close(fd1);
1157 if (inc_recurse)
1158@@ -575,6 +608,9 @@ int recv_files(int f_in, char *local_nam
1159 */
1160 rprintf(FERROR,"recv_files: %s is a directory\n",
1161 full_fname(fnamecmp));
1162+#ifdef HAVE_LIBODBC
1163+ db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp));
1164+#endif
1165 discard_receive_data(f_in, F_LENGTH(file));
1166 close(fd1);
1167 if (inc_recurse)
1168@@ -609,6 +645,9 @@ int recv_files(int f_in, char *local_nam
1169 if (fd2 == -1) {
1170 rsyserr(FERROR, errno, "open %s failed",
1171 full_fname(fname));
1172+#ifdef HAVE_LIBODBC
1173+ db_log_error(FERROR,22, "open %s failed", full_fname(fname));
1174+#endif
1175 discard_receive_data(f_in, F_LENGTH(file));
1176 if (fd1 != -1)
1177 close(fd1);
1178@@ -646,6 +685,10 @@ int recv_files(int f_in, char *local_nam
1179 if (fd2 == -1) {
1180 rsyserr(FERROR, errno, "mkstemp %s failed",
1181 full_fname(fnametmp));
1182+#ifdef HAVE_LIBODBC
1183+ db_log_error(FERROR, 22, "mkstemp %s failed",
1184+ full_fname(fnametmp));
1185+#endif
1186 discard_receive_data(f_in, F_LENGTH(file));
1187 if (fd1 != -1)
1188 close(fd1);
1189@@ -668,12 +711,19 @@ int recv_files(int f_in, char *local_nam
1190 fname, fd2, F_LENGTH(file));
1191
1192 log_item(log_code, file, &initial_stats, iflags, NULL);
1193+#ifdef HAVE_LIBODBC
1194+ db_log_transfer(file, &initial_stats, "receive");
1195+#endif
1196
1197 if (fd1 != -1)
1198 close(fd1);
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
1209@@ -730,6 +780,12 @@ int recv_files(int f_in, char *local_nam
1210 rprintf(msgtype,
1211 "%s: %s failed verification -- update %s%s.\n",
1212 errstr, fname, keptstr, redostr);
1213+#ifdef HAVE_LIBODBC
1214+ db_log_error(msgtype,24,
1215+ "%s: %s failed verification -- update %s%s.\n",
1216+ errstr, fname,
1217+ keptstr, redostr);
1218+#endif
1219 }
1220 if (!redoing) {
1221 send_msg_int(MSG_REDO, ndx);
1222--- old/sender.c
1223+++ new/sender.c
1224@@ -344,6 +344,9 @@ void send_files(int f_in, int f_out)
1225 end_progress(st.st_size);
1226
1227 log_item(log_code, file, &initial_stats, iflags, NULL);
1228+#ifdef HAVE_LIBODBC
1229+ db_log_transfer(file, &initial_stats,"send");
1230+#endif
1231
1232 if (mbuf) {
1233 j = unmap_file(mbuf);