Craig Barratt's --checksum-seed option.
[rsync/rsync-patches.git] / ODBC-dblog.diff
CommitLineData
2c40f06f
WD
1Add support for logging daemon messages to an SQL database.
2
3After applying this patch you'll need to run autoconf and autoheader to
4generate updated versions of "configure" and "config.h.in".
5
6You'll need to run configure with the --with-ODBC option in order for the
7extended features to be active.
8
9Patch provided by Steve Sether.
10
11(Tweaked by Wayne Davison for rsync-style purposes but not compiled, so if the
12dblog.c file has a compile problem, it's probably my fault...)
13
14
103bcb1d
WD
15--- Makefile.in 10 Feb 2004 17:06:11 -0000 1.98
16+++ Makefile.in 24 Apr 2004 08:14:37 -0000
17@@ -32,7 +32,7 @@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o z
18 zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \
19 zlib/zutil.o zlib/adler32.o
20 OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
21- main.o checksum.o match.o syscall.o log.o backup.o
22+ main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@
23 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
24 fileio.o batch.o clientname.o
25 OBJS3=progress.o pipe.o
bd8bf8b1 26--- /dev/null 1 Jan 1970 00:00:00 -0000
103bcb1d 27+++ README-ODBC 24 Apr 2004 08:14:37 -0000
2c40f06f
WD
28@@ -0,0 +1,80 @@
29+This patch adds the following options:
30+
31+"database logging"
32+If set to True, rsync will attempt to connect to
33+the specified datasource and write to the named tables.
34+Defaults to False.
35+
36+
37+"database datasource"
38+Specifies the name of the ODBC data source to use.
39+
40+"database username"
41+The username to use when connecting to the database.
42+
43+"database password"
44+The password to use when connecting to the database.
45+
46+"transfer table name"
47+The name of the transfer table to log to.
48+This table contains individual filenames, file sizes, bytes transferred, checksum bytes transferred, operation (send or receive), and a timestamp.
49+
50+"session table name"
51+The name of the session table to log to.
52+This table contains the username, module name, module path, ip address, process ID, and a timestamp.
53+
54+"exit table name"
55+The name of the exit table to log to.
56+This table contains the total bytes read, total bytes written, total size
57+of all files, error code, line the error occured at, file the error occured at
58+and the text of the error. (most of which will be blank if the program exited normally).
59+
60+
61+"unique id method"
62+Different databases use different methods to get a unique identifier.
63+Some databases support sequence objects, and use various forms of the
64+nextval command to retrieve a unique identifier from it. Other databases
65+support an autonumber field, and support different methds of retrieving
66+the ID used in the last insert. Valid values for this option are:
67+
68+ nextval-postgres
69+ uses the syntax of nextval for PostgreSQL databases
70+
71+ nextval-oracle
72+ uses the syntax of nextval for Oracle databases
73+
74+ nextval-db2
75+ uses the syntax of nextval for DB2 databases
76+
77+ last_insert_id
78+ uses the last_insert_id() command for the MySQL databases
79+
80+ @@IDENTITY
81+ uses the @@IDENTITY command for Sybase databases
82+
83+ custom
84+ Define your own method to get a unique identifier. See the
85+ "custom unique id select", and the "get custom id before select"
86+ parameters.
87+
88+
89+"sequence name"
90+If your database supports sequences, list the name of the sequence to use
91+for the session unique identifier.
92+
93+"custom unique id select"
94+Only used if you specify the custom method in "unique id method". This
95+is a SQL statement to be executed to get a unique ID. This SQL
96+statement must return one column with the unique ID to use for
97+the session ID. Should be used in concert with the "get custom
98+id before select" parameter.
99+
100+"get custom id before insert"
101+This parameter is ignored unless the "unique id method" is set to custom.
102+If set to true, the "custom unique id select" statement will
103+be executed BEFORE the session row is inserted into the database.
104+(as is done when a sequence is used for unique IDs).
105+If False the statement will be executed after the session
106+row is inserted (as is done when the session ID is automatically generates
107+unique IDs).
108+Defaults to True.
2c40f06f 109--- cleanup.c 27 Jan 2004 08:14:33 -0000 1.21
103bcb1d 110+++ cleanup.c 24 Apr 2004 08:14:37 -0000
b0aa8860 111@@ -138,7 +138,12 @@ void _exit_cleanup(int code, const char
2c40f06f
WD
112 code = RERR_VANISHED;
113 }
114
115- if (code) log_exit(code, file, line);
116+ if (code) {
117+ log_exit(code, file, line);
118+#ifdef HAVE_LIBODBC
119+ db_log_exit(code,file,line);
120+#endif
121+ }
122
123 if (verbose > 2)
124 rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n",
54691942 125--- clientserver.c 14 Apr 2004 23:33:34 -0000 1.121
103bcb1d 126+++ clientserver.c 24 Apr 2004 08:14:37 -0000
b0aa8860 127@@ -311,6 +311,9 @@ static int rsync_module(int f_in, int f_
2c40f06f
WD
128 exclude_path_prefix = NULL;
129
130 log_init();
131+#ifdef HAVE_LIBODBC
132+ db_log_open();
133+#endif
134
135 if (use_chroot) {
136 /*
b0aa8860 137@@ -429,6 +432,9 @@ static int rsync_module(int f_in, int f_
2c40f06f
WD
138 rprintf(FINFO,"rsync %s %s from %s@%s (%s)\n",
139 am_sender?"on":"to",
140 request, auth_user, host, addr);
141+#ifdef HAVE_LIBODBC
142+ db_log_session();
143+#endif
144 } else {
145 rprintf(FINFO,"rsync %s %s from %s (%s)\n",
146 am_sender?"on":"to",
bd8bf8b1 147--- configure.in 17 Apr 2004 18:40:16 -0000 1.191
103bcb1d 148+++ configure.in 24 Apr 2004 08:14:38 -0000
b0aa8860 149@@ -94,6 +94,8 @@ AC_ARG_WITH(rsync-path,
2c40f06f
WD
150 [ --with-rsync-path=PATH set default --rsync-path to PATH (default: rsync)],
151 [ RSYNC_PATH="$with_rsync_path" ],
152 [ RSYNC_PATH="rsync" ])
153+AC_ARG_WITH(ODBC,
154+ [ --with-ODBC compile in support for ODBC database logging])
155
156 AC_DEFINE_UNQUOTED(RSYNC_PATH, "$RSYNC_PATH", [location of rsync on remote machine])
157
b0aa8860 158@@ -458,6 +460,14 @@ fi
2c40f06f
WD
159 if test x"$with_included_popt" != x"yes"
160 then
161 AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
162+fi
163+
164+if test x"$with_ODBC" = x"yes"
165+then
166+ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h)
167+ AC_CHECK_LIB(odbc,SQLExecDirect)
168+ EXTRA_OBJECT="$EXTRA_OBJECT dblog.o"
169+ AC_SUBST(EXTRA_OBJECT)
170 fi
171
172 AC_MSG_CHECKING([whether to use included libpopt])
bd8bf8b1 173--- /dev/null 1 Jan 1970 00:00:00 -0000
103bcb1d
WD
174+++ dblog-tables-mysql.sql 24 Apr 2004 08:14:38 -0000
175@@ -0,0 +1,43 @@
176+drop table transfer;
177+drop table exit;
178+drop table session;
179+
180+CREATE TABLE session (
181+ id int auto_increment NOT NULL,
182+ date timestamp NOT NULL,
183+ ip_address varchar(15) NOT NULL,
184+ username varchar(20) NOT NULL,
185+ module_name varchar(20) NOT NULL,
186+ module_path varchar(255) NOT NULL,
187+ process_id int NOT NULL,
188+ Primary Key (id)
189+);
190+
191+CREATE TABLE transfer (
192+ id int auto_increment NOT NULL,
193+ session_id int NOT NULL,
194+ date timestamp NOT NULL,
195+ file_path varchar(255) NOT NULL,
196+ file_name varchar(255) NOT NULL,
197+ file_size bigint NOT NULL,
198+ bytes_transferred bigint NOT NULL,
199+ checksum_bytes_transferred bigint NOT NULL,
200+ operation varchar(20),
201+ Primary Key (id),
202+ foreign key (session_id) references session (id)
203+);
204+
205+CREATE TABLE exit (
206+ id int auto_increment NOT NULL,
207+ session_id int NOT NULL,
208+ date timestamp NOT NULL,
209+ total_bytes_written bigint NOT NULL,
210+ total_bytes_read bigint NOT NULL,
211+ total_size bigint NOT NULL,
212+ error_text varchar(128) NOT NULL,
213+ error_code int NOT NULL,
214+ error_file varchar(64) NOT NULL,
215+ error_line int NOT NULL,
216+ Primary Key (id),
217+ foreign key (session_id) references session (id)
218+);
219--- /dev/null 1 Jan 1970 00:00:00 -0000
220+++ dblog-tables-postgresql.sql 24 Apr 2004 08:14:38 -0000
221@@ -0,0 +1,45 @@
222+drop table transfer;
223+drop table exit;
224+drop table session;
225+drop sequence session_id_seq;
226+create sequence session_id_seq;
227+
228+CREATE TABLE "session" (
229+ "id" int NOT NULL,
230+ "date" timestamp NOT NULL default now(),
231+ "ip_address" varchar(15) NOT NULL,
232+ "username" varchar(20) NOT NULL,
233+ "module_name" varchar(20) NOT NULL,
234+ "module_path" varchar(255) NOT NULL,
235+ "process_id" int NOT NULL,
236+ Primary Key (id)
237+);
238+
239+CREATE TABLE "transfer" (
240+ "id" serial NOT NULL,
241+ "session_id" int NOT NULL,
242+ "date" timestamp NOT NULL default now(),
243+ "file_path" varchar(512) NOT NULL,
244+ "file_name" varchar(512) NOT NULL,
245+ "file_size" bigint NOT NULL,
246+ "bytes_transferred" bigint NOT NULL,
247+ "checksum_bytes_transferred" bigint NOT NULL,
248+ "operation" varchar(20),
249+ Primary Key (id),
250+ foreign key (session_id) references session (id)
251+);
252+
253+CREATE TABLE "exit" (
254+ "id" serial NOT NULL,
255+ "session_id" int NOT NULL,
256+ "date" timestamp NOT NULL default now(),
257+ "total_bytes_written" bigint NOT NULL,
258+ "total_bytes_read" bigint NOT NULL,
259+ "total_size" bigint NOT NULL,
260+ "error_text" varchar(128) NOT NULL,
261+ "error_code" int NOT NULL,
262+ "error_file" varchar(64) NOT NULL,
263+ "error_line" int NOT NULL,
264+ Primary Key (id),
265+ foreign key (session_id) references session (id)
266+);
267--- /dev/null 1 Jan 1970 00:00:00 -0000
268+++ dblog.c 24 Apr 2004 08:14:38 -0000
2c40f06f
WD
269@@ -0,0 +1,352 @@
270+/*
271+ * ODBC Database logging functions
272+ *
273+ * Written by Steve Sether, April 2004
274+ * steve@vellmont.com
275+ */
276+
277+#include "rsync.h"
278+
279+#ifdef HAVE_SQL_H
280+#include <sql.h>
281+#else
282+#ifdef HAVE_ODBC_SQL_H
283+#include <odbc/sql.h>
284+#endif
285+#endif
286+
287+#ifdef HAVE_SQLEXT_H
288+#include <sqlext.h>
289+#else
290+#ifdef HAVE_ODBC_SQLEXT_H
291+#include <odbc/sqlext.h>
292+#endif
293+#endif
294+
295+#ifdef HAVE_SQLTYPES_H
296+#include <sqltypes.h>
297+#else
298+#ifdef HAVE_ODBC_SQLTYPES_H
299+#include <odbc/sqltypes.h>
300+#endif
301+#endif
302+
303+SQLHENV db_environ_handle; /* Handle ODBC environment */
304+long result; /* result of functions */
305+SQLHDBC db_handle= NULL; /* database connection handle */
306+SQLHSTMT sql_statement_handle; /* SQL statement handle */
307+extern int am_sender;
308+extern char *auth_user;
309+extern int module_id;
310+
311+char sql_status[10]; /* Status SQL */
312+SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id;
313+SQLSMALLINT V_OD_mlen, V_OD_colanz;
314+char V_OD_msg[200], V_OD_buffer[200];
315+SQLINTEGER session_id;
316+
317+
318+/* This function simply removes invalid characters from the SQL statement
319+ * to prevent SQL injection attacks. */
320+char *sanitizeSql(const char *input)
321+{
322+ char *out, *ptr;
323+ const char *c;
324+
325+ if (strlen(input) > ((~(unsigned int)0)>>1)-3)
326+ return 0;
327+ if (!(out = ptr = new_array(char, strlen(input) * 2 + 1)))
328+ return 0;
329+
330+ for (c = input; *c; c++) {
331+ switch (*c) {
332+ case '\'':
333+ *ptr++ = '\'';
334+ *ptr++ = '\'';
335+ break;
336+ case '\b':
337+ *ptr++ = '\\';
338+ *ptr++ = 'b';
339+ break;
340+ case '\n':
341+ *ptr++ = '\\';
342+ *ptr++ = 'n';
343+ break;
344+ case '\r':
345+ *ptr++ = '\\';
346+ *ptr++ = 'r';
347+ break;
348+ case '\t':
349+ *ptr++ = '\\';
350+ *ptr++ = 't';
351+ break;
352+ default:
353+ *ptr++ = *c;
354+ break;
355+ }
356+ }
357+ *ptr = '\0';
358+ return out;
359+}
360+
361+void db_log_open(void)
362+{
363+ if (lp_database_logging(module_id)) {
364+ if (db_handle == NULL) {
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+
381+ /* Get a database handle */
382+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle);
383+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
384+ rprintf(FERROR, "Error: couldn't allocate database handle\n");
385+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
386+ db_environ_handle = NULL;
387+ return;
388+ }
389+
390+ /* Set connection attributes */
391+ SQLSetConnectAttr(db_handle, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
392+
393+ /* Connect to the database. */
394+ result = SQLConnect(db_handle, (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,1,
400+ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen);
401+ rprintf(FERROR,"Error Connecting to Database %s\n",V_OD_msg);
402+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle);
403+ db_handle = NULL;
404+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
405+ db_environ_handle = NULL;
406+ return;
407+ }
408+ rprintf(FLOG,"Connected to database!\n");
409+
410+ /* get SQL statement handle */
411+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle, &sql_statement_handle);
412+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
413+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
414+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
415+ SQLDisconnect(db_handle);
416+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle);
417+ db_handle = NULL;
418+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
419+ db_environ_handle = NULL;
420+ return;
421+ }
422+ } else {
423+ rprintf(FERROR,"Already connected to database\n");
424+ }
425+ }
426+}
427+
428+void db_log_close()
429+{
430+ if (lp_database_logging(module_id)) {
431+ if (sql_statement_handle != NULL) {
432+ /* free the statement handle first */
433+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle);
434+ sql_statement_handle = NULL;
435+ } else {
436+ rprintf(FERROR,"No sql statement handle to close\n");
437+ }
438+ if (db_handle != NULL) {
439+ /* disconnect, and free the database handle. */
440+ SQLDisconnect(db_handle);
441+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle);
442+ db_handle = NULL;
443+ } else {
444+ rprintf(FERROR,"Database already closed");
445+ }
446+ if (db_environ_handle != NULL) {
447+ /* free the environment handle */
448+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
449+ db_environ_handle = NULL;
450+ } else {
451+ rprintf(FERROR,"No environment handle to close");
452+ }
453+ }
454+}
455+
456+static long get_unique_session_id()
457+{
458+ long unique;
459+ char strSqlStatement[1024];
460+
461+ if (db_handle != NULL) {
462+ /* choose the appropriate select statement based upon which DBMS we're using.
463+ * different datbases use different methods to get a unique ID. Some use a counter
464+ * object (sequence), others use an auto increment datatype and have a method
465+ * to get the last ID inserted using this connection. */
466+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) {
467+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT NEXTVAL('%s');",lp_sequence_name(module_id));
468+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) {
469+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT %s.NEXTVAL FROM dual;",lp_sequence_name(module_id));
470+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) {
471+ snprintf(strSqlStatement,sizeof(strSqlStatement),"VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id));
472+ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */
473+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT LAST_INSERT_ID()");
474+ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */
475+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT @@IDENTITY");
476+ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */
477+ snprintf(strSqlStatement,sizeof(strSqlStatement),lp_custom_unique_id_select(module_id));
478+ }
479+
480+ /* bind the 1st column to unique */
481+ SQLBindCol(sql_statement_handle,1,SQL_C_LONG,&unique,150,&V_OD_err);
482+ /* execute the SQL statement */
483+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
484+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
485+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
486+ rprintf(FERROR,"Error at get_sequence: Error in Select! %s %s\n",strSqlStatement,V_OD_msg);
487+ } else {
488+ result = SQLFetch(sql_statement_handle);
489+ if (result != SQL_NO_DATA && unique != 0) {
490+ rprintf(FINFO,"Got unique sequence! %ld\n",unique);
491+ } else {
492+ rprintf(FERROR,"Error at get_sequence: Didn't get unique session ID\n");
493+ }
494+ /* Close the cursor so the statement can be re-used */
495+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
496+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
497+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
498+ rprintf(FERROR,"Error at get_sequence: Error in closing SQL statement handle %s\n",V_OD_msg);
499+ return unique;
500+ }
501+ return unique;
502+ }
503+ }
504+ rprintf(FERROR,"Error at get_sequence: Not connected to database\n");
505+ return -1;
506+}
507+
508+
509+void db_log_session()
510+{
511+ char strSqlStatement[1024];
512+ int gotSessionID = 0;
513+ if (lp_database_logging(module_id)) {
514+ /* if we're using a sequence via the nextval command to get a unique ID, we need to get it before
515+ * we do the insert. We also get the unique ID now if custom, and get_custom_id_before_insert is set. */
516+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0
517+ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0
518+ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0
519+ || (strcmp(lp_unique_id_method(module_id),"custom") == 0
520+ && lp_get_custom_id_before_insert(module_id))) {
521+ session_id = get_unique_session_id();
522+ gotSessionID = 1;
523+ snprintf(strSqlStatement,sizeof(strSqlStatement),"INSERT INTO %s (id, date, ip_address, username, module_name, module_path, process_id) VALUES ('%ld', '%s', '%s', '%s','%s','%s','%d');",lp_session_table_name(module_id),session_id,timestring(time(NULL)),client_addr(0),auth_user,lp_name(module_id),lp_path(module_id),getpid());
524+ } else {
525+ /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */
526+ snprintf(strSqlStatement,sizeof(strSqlStatement),"INSERT INTO %s (date, ip_address, username, module_name, module_path, process_id) VALUES ('%s', '%s', '%s', '%s','%s','%d');",lp_session_table_name(module_id),timestring(time(NULL)),client_addr(0),auth_user,lp_name(module_id),lp_path(module_id),getpid());
527+ }
528+
529+ /* Insert the new session into the database */
530+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
531+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
532+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
533+ rprintf(FERROR,"Error at db_log_session: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
534+ }
535+
536+ /* close the cursor so the statement handle can be re-used. */
537+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
538+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
539+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
540+ rprintf(FERROR,"Error in resetting SQL statement handle %s\n",V_OD_msg);
541+ }
542+ /* get the session ID for databases that give the unique ID after an insert */
543+ if (gotSessionID == 0) {
544+ session_id = get_unique_session_id();
545+ }
546+ }
547+ else {
548+ rprintf(FERROR,"Error at db_log_session: Not connected to database!\n");
549+ }
550+}
551+
552+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation)
553+{
554+ extern struct stats stats;
555+ char strSqlStatement[1024];
556+ char strFilePath[255];
557+ char strFileName[255];
558+ char strFileSize[255];
559+ int64 intBytesTransferred;
560+ int64 intCheckSumBytes;
561+
562+ if (lp_database_logging(module_id)) {
563+ if (db_handle != NULL) {
564+ snprintf(strFileName,sizeof(strFileName), "%s",f_name(file));
565+ snprintf(strFilePath, sizeof(strFilePath), "%s", file->basedir?file->basedir:"");
566+ snprintf(strFileSize,sizeof(strFileSize),"%.0f", (double)file->length);
567+ if (am_sender) {
568+ intBytesTransferred = stats.total_written - initial_stats->total_written;
569+ } else {
570+ intBytesTransferred = stats.total_read - initial_stats->total_read;
571+ }
572+
573+ if (!am_sender) {
574+ intCheckSumBytes = stats.total_written - initial_stats->total_written;
575+ } else {
576+ intCheckSumBytes = stats.total_read - initial_stats->total_read;
577+ }
578+
579+ snprintf(strSqlStatement,sizeof(strSqlStatement),"INSERT INTO %s (session_id,date,file_path, file_name, file_size, bytes_transferred, checksum_bytes_transferred, operation) VALUES ('%ld','%s','%s','%s','%s','%Ld','%Ld','%s');",lp_transfer_table_name(module_id),session_id,timestring(time(NULL)),sanitizeSql(strFilePath),sanitizeSql(strFileName),strFileSize,intBytesTransferred,intCheckSumBytes,operation);
580+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
581+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
582+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
583+ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
584+ if (result == SQL_INVALID_HANDLE)
585+ rprintf(FERROR,"INVALID HANDLE\n");
586+ }
587+ } else {
588+ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n");
589+ }
590+ }
591+}
592+
593+
594+void db_log_exit(int code, const char *file, int line)
595+{
596+ char strSqlStatement[2048];
597+ const char *error_text;
598+ extern struct stats stats;
599+ if (db_handle != NULL) {
600+ if (lp_database_logging(module_id)) {
601+ if (code != 0) {
602+ error_text = rerr_name(code);
603+ if (!error_text) {
604+ error_text = "unexplained error";
605+ }
606+ } else {
607+ error_text = "";
608+ }
609+ snprintf(strSqlStatement,sizeof(strSqlStatement),"INSERT INTO %s (session_id, date, total_bytes_written,total_bytes_read,total_size,error_text,error_code,error_file,error_line) VALUES ('%ld','%s','%Ld','%Ld','%Ld','%s','%d','%s','%d');",lp_exit_table_name(module_id),session_id,timestring(time(NULL)),stats.total_written,stats.total_read,stats.total_size,error_text,code,file,line);
610+
611+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
612+
613+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
614+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
615+ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
616+ }
617+ }
618+ } else {
619+ rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
620+ }
621+}
2c40f06f 622--- loadparm.c 4 Feb 2004 07:31:29 -0000 1.50
103bcb1d 623+++ loadparm.c 24 Apr 2004 08:14:38 -0000
b0aa8860 624@@ -122,6 +122,17 @@ typedef struct
2c40f06f
WD
625 BOOL list;
626 BOOL use_chroot;
627 BOOL transfer_logging;
628+ BOOL database_logging;
629+ char *database_datasource;
630+ char *database_username;
631+ char *database_password;
632+ char *transfer_table_name;
633+ char *exit_table_name;
634+ char *session_table_name;
635+ char *sequence_name;
636+ char *unique_id_method;
637+ char *custom_unique_id_select;
638+ BOOL get_custom_id_before_insert;
639 BOOL ignore_errors;
640 char *uid;
641 char *gid;
b0aa8860 642@@ -154,6 +165,17 @@ static service sDefault =
2c40f06f
WD
643 True, /* list */
644 True, /* use chroot */
645 False, /* transfer logging */
646+ False, /* Database Logging */
647+ NULL, /* Database datasource */
648+ NULL, /* Database username */
649+ NULL, /* Database password */
650+ NULL, /* Transfer table name */
651+ NULL, /* Exit table name */
652+ NULL, /* Session table name */
653+ NULL, /* sequence name */
654+ NULL, /* unique method */
655+ NULL, /* custom unique id select*/
656+ True, /* get custom id before insert */
657 False, /* ignore errors */
658 "nobody",/* uid */
659
b0aa8860 660@@ -292,6 +314,17 @@ static struct parm_struct parm_table[] =
2c40f06f
WD
661 {"include", P_STRING, P_LOCAL, &sDefault.include, NULL, 0},
662 {"include from", P_STRING, P_LOCAL, &sDefault.include_from,NULL, 0},
663 {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging,NULL,0},
664+ {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging,NULL,0},
665+ {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0},
666+ {"database username",P_STRING, P_LOCAL, &sDefault.database_username,NULL,0},
667+ {"database password",P_STRING, P_LOCAL, &sDefault.database_password,NULL,0},
668+ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0},
669+ {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name,NULL,0},
670+ {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0},
671+ {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name,NULL,0},
672+ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method,NULL,0},
673+ {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0},
674+ {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0},
675 {"ignore errors", P_BOOL, P_LOCAL, &sDefault.ignore_errors,NULL,0},
676 {"log format", P_STRING, P_LOCAL, &sDefault.log_format, NULL, 0},
677 {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options,NULL, 0},
b0aa8860 678@@ -359,6 +392,17 @@ FN_LOCAL_BOOL(lp_read_only, read_only)
2c40f06f
WD
679 FN_LOCAL_BOOL(lp_list, list)
680 FN_LOCAL_BOOL(lp_use_chroot, use_chroot)
681 FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging)
682+FN_LOCAL_BOOL(lp_database_logging, database_logging)
683+FN_LOCAL_STRING(lp_database_datasource, database_datasource)
684+FN_LOCAL_STRING(lp_database_username, database_username)
685+FN_LOCAL_STRING(lp_database_password, database_password)
686+FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name)
687+FN_LOCAL_STRING(lp_exit_table_name, exit_table_name)
688+FN_LOCAL_STRING(lp_session_table_name,session_table_name)
689+FN_LOCAL_STRING(lp_sequence_name,sequence_name)
690+FN_LOCAL_STRING(lp_unique_id_method,unique_id_method)
691+FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select)
692+FN_LOCAL_BOOL(lp_get_custom_id_before_insert,get_custom_id_before_insert)
693 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
694 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
695 FN_LOCAL_STRING(lp_uid, uid)
696--- log.c 20 Jan 2004 05:15:14 -0000 1.71
103bcb1d 697+++ log.c 24 Apr 2004 08:14:38 -0000
b0aa8860 698@@ -75,7 +75,7 @@ struct {
2c40f06f
WD
699 /*
700 * Map from rsync error code to name, or return NULL.
701 */
702-static char const *rerr_name(int code)
703+char const *rerr_name(int code)
704 {
705 int i;
706 for (i = 0; rerr_names[i].name; i++) {
707--- main.c 10 Feb 2004 03:54:47 -0000 1.192
103bcb1d 708+++ main.c 24 Apr 2004 08:14:38 -0000
b0aa8860 709@@ -120,6 +120,9 @@ static void report(int f)
2c40f06f
WD
710
711 if (am_daemon) {
712 log_exit(0, __FILE__, __LINE__);
713+#ifdef HAVE_LIBODBC
714+ db_log_exit(0,__FILE__,__LINE__);
715+#endif
716 if (f == -1 || !am_sender) return;
717 }
718
bd8bf8b1 719--- proto.h 22 Apr 2004 09:58:09 -0000 1.189
103bcb1d 720+++ proto.h 24 Apr 2004 08:14:38 -0000
b0aa8860 721@@ -51,6 +51,12 @@ int start_daemon(int f_in, int f_out);
2c40f06f
WD
722 int daemon_main(void);
723 void setup_protocol(int f_out,int f_in);
724 int claim_connection(char *fname,int max_connections);
54691942 725+char *sanitizeSql(const char *input);
2c40f06f
WD
726+void db_log_open(void);
727+void db_log_close();
728+void db_log_session();
729+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation);
730+void db_log_exit(int code, const char *file, int line);
54691942 731 void free_exclude_list(struct exclude_list_struct *listp);
b0aa8860
WD
732 int check_exclude(struct exclude_list_struct *listp, char *name, int name_is_dir);
733 void add_exclude(struct exclude_list_struct *listp, const char *pattern,
734@@ -135,6 +141,17 @@ BOOL lp_read_only(int );
2c40f06f
WD
735 BOOL lp_list(int );
736 BOOL lp_use_chroot(int );
737 BOOL lp_transfer_logging(int );
738+BOOL lp_database_logging(int );
739+char *lp_database_datasource(int );
740+char *lp_database_username(int );
741+char *lp_database_password(int );
742+char *lp_transfer_table_name(int );
743+char *lp_exit_table_name(int );
744+char *lp_session_table_name(int );
745+char *lp_sequence_name(int );
746+char *lp_unique_id_method(int );
747+char *lp_custom_unique_id_select(int );
748+BOOL lp_get_custom_id_before_insert(int );
749 BOOL lp_ignore_errors(int );
750 BOOL lp_ignore_nonreadable(int );
751 char *lp_uid(int );
b0aa8860 752@@ -156,6 +173,7 @@ int lp_max_connections(int );
2c40f06f
WD
753 BOOL lp_load(char *pszFname, int globals_only);
754 int lp_numservices(void);
755 int lp_number(char *name);
756+char const *rerr_name(int code);
757 void log_init(void);
758 void log_open(void);
759 void log_close(void);
760--- receiver.c 23 Mar 2004 16:50:40 -0000 1.75
103bcb1d 761+++ receiver.c 24 Apr 2004 08:14:39 -0000
b0aa8860 762@@ -453,7 +453,9 @@ int recv_files(int f_in,struct file_list
2c40f06f
WD
763 recv_ok = receive_data(f_in,mapbuf,fd2,fname,file->length);
764
765 log_recv(file, &initial_stats);
766-
767+#ifdef HAVE_LIBODBC
768+ db_log_transfer(file, &initial_stats,"receive");
769+#endif
770 if (mapbuf) unmap_file(mapbuf);
771 if (fd1 != -1) {
772 close(fd1);
773--- sender.c 17 Feb 2004 21:57:44 -0000 1.38
103bcb1d 774+++ sender.c 24 Apr 2004 08:14:39 -0000
b0aa8860 775@@ -283,6 +283,9 @@ void send_files(struct file_list *flist,
2c40f06f
WD
776 } else {
777 match_sums(f_out, s, buf, st.st_size);
778 log_send(file, &initial_stats);
779+#ifdef HAVE_LIBODBC
780+ db_log_transfer(file, &initial_stats,"send");
781+#endif
782 }
783
784 if (!read_batch) {