More updates.
[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
bd8bf8b1 15--- /dev/null 1 Jan 1970 00:00:00 -0000
2c40f06f
WD
16+++ README-ODBC 2004-04-07 22:42:28.000000000 -0700
17@@ -0,0 +1,80 @@
18+This patch adds the following options:
19+
20+"database logging"
21+If set to True, rsync will attempt to connect to
22+the specified datasource and write to the named tables.
23+Defaults to False.
24+
25+
26+"database datasource"
27+Specifies the name of the ODBC data source to use.
28+
29+"database username"
30+The username to use when connecting to the database.
31+
32+"database password"
33+The password to use when connecting to the database.
34+
35+"transfer table name"
36+The name of the transfer table to log to.
37+This table contains individual filenames, file sizes, bytes transferred, checksum bytes transferred, operation (send or receive), and a timestamp.
38+
39+"session table name"
40+The name of the session table to log to.
41+This table contains the username, module name, module path, ip address, process ID, and a timestamp.
42+
43+"exit table name"
44+The name of the exit table to log to.
45+This table contains the total bytes read, total bytes written, total size
46+of all files, error code, line the error occured at, file the error occured at
47+and the text of the error. (most of which will be blank if the program exited normally).
48+
49+
50+"unique id method"
51+Different databases use different methods to get a unique identifier.
52+Some databases support sequence objects, and use various forms of the
53+nextval command to retrieve a unique identifier from it. Other databases
54+support an autonumber field, and support different methds of retrieving
55+the ID used in the last insert. Valid values for this option are:
56+
57+ nextval-postgres
58+ uses the syntax of nextval for PostgreSQL databases
59+
60+ nextval-oracle
61+ uses the syntax of nextval for Oracle databases
62+
63+ nextval-db2
64+ uses the syntax of nextval for DB2 databases
65+
66+ last_insert_id
67+ uses the last_insert_id() command for the MySQL databases
68+
69+ @@IDENTITY
70+ uses the @@IDENTITY command for Sybase databases
71+
72+ custom
73+ Define your own method to get a unique identifier. See the
74+ "custom unique id select", and the "get custom id before select"
75+ parameters.
76+
77+
78+"sequence name"
79+If your database supports sequences, list the name of the sequence to use
80+for the session unique identifier.
81+
82+"custom unique id select"
83+Only used if you specify the custom method in "unique id method". This
84+is a SQL statement to be executed to get a unique ID. This SQL
85+statement must return one column with the unique ID to use for
86+the session ID. Should be used in concert with the "get custom
87+id before select" parameter.
88+
89+"get custom id before insert"
90+This parameter is ignored unless the "unique id method" is set to custom.
91+If set to true, the "custom unique id select" statement will
92+be executed BEFORE the session row is inserted into the database.
93+(as is done when a sequence is used for unique IDs).
94+If False the statement will be executed after the session
95+row is inserted (as is done when the session ID is automatically generates
96+unique IDs).
97+Defaults to True.
98--- Makefile.in 10 Feb 2004 17:06:11 -0000 1.98
99+++ Makefile.in 8 Apr 2004 05:56:31 -0000
b0aa8860 100@@ -32,7 +32,7 @@ ZLIBOBJ=zlib/deflate.o zlib/infblock.o z
2c40f06f
WD
101 zlib/inflate.o zlib/inftrees.o zlib/infutil.o zlib/trees.o \
102 zlib/zutil.o zlib/adler32.o
103 OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
104- main.o checksum.o match.o syscall.o log.o backup.o
105+ main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@
106 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
107 fileio.o batch.o clientname.o
108 OBJS3=progress.o pipe.o
109--- cleanup.c 27 Jan 2004 08:14:33 -0000 1.21
110+++ cleanup.c 8 Apr 2004 05:56:31 -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
WD
125--- clientserver.c 14 Apr 2004 23:33:34 -0000 1.121
126+++ clientserver.c 15 Apr 2004 18:51:14 -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
54691942 148+++ configure.in 15 Apr 2004 18:51:14 -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
2c40f06f
WD
174+++ dblog.c 2004-04-07 23:18:56.000000000 -0700
175@@ -0,0 +1,352 @@
176+/*
177+ * ODBC Database logging functions
178+ *
179+ * Written by Steve Sether, April 2004
180+ * steve@vellmont.com
181+ */
182+
183+#include "rsync.h"
184+
185+#ifdef HAVE_SQL_H
186+#include <sql.h>
187+#else
188+#ifdef HAVE_ODBC_SQL_H
189+#include <odbc/sql.h>
190+#endif
191+#endif
192+
193+#ifdef HAVE_SQLEXT_H
194+#include <sqlext.h>
195+#else
196+#ifdef HAVE_ODBC_SQLEXT_H
197+#include <odbc/sqlext.h>
198+#endif
199+#endif
200+
201+#ifdef HAVE_SQLTYPES_H
202+#include <sqltypes.h>
203+#else
204+#ifdef HAVE_ODBC_SQLTYPES_H
205+#include <odbc/sqltypes.h>
206+#endif
207+#endif
208+
209+SQLHENV db_environ_handle; /* Handle ODBC environment */
210+long result; /* result of functions */
211+SQLHDBC db_handle= NULL; /* database connection handle */
212+SQLHSTMT sql_statement_handle; /* SQL statement handle */
213+extern int am_sender;
214+extern char *auth_user;
215+extern int module_id;
216+
217+char sql_status[10]; /* Status SQL */
218+SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id;
219+SQLSMALLINT V_OD_mlen, V_OD_colanz;
220+char V_OD_msg[200], V_OD_buffer[200];
221+SQLINTEGER session_id;
222+
223+
224+/* This function simply removes invalid characters from the SQL statement
225+ * to prevent SQL injection attacks. */
226+char *sanitizeSql(const char *input)
227+{
228+ char *out, *ptr;
229+ const char *c;
230+
231+ if (strlen(input) > ((~(unsigned int)0)>>1)-3)
232+ return 0;
233+ if (!(out = ptr = new_array(char, strlen(input) * 2 + 1)))
234+ return 0;
235+
236+ for (c = input; *c; c++) {
237+ switch (*c) {
238+ case '\'':
239+ *ptr++ = '\'';
240+ *ptr++ = '\'';
241+ break;
242+ case '\b':
243+ *ptr++ = '\\';
244+ *ptr++ = 'b';
245+ break;
246+ case '\n':
247+ *ptr++ = '\\';
248+ *ptr++ = 'n';
249+ break;
250+ case '\r':
251+ *ptr++ = '\\';
252+ *ptr++ = 'r';
253+ break;
254+ case '\t':
255+ *ptr++ = '\\';
256+ *ptr++ = 't';
257+ break;
258+ default:
259+ *ptr++ = *c;
260+ break;
261+ }
262+ }
263+ *ptr = '\0';
264+ return out;
265+}
266+
267+void db_log_open(void)
268+{
269+ if (lp_database_logging(module_id)) {
270+ if (db_handle == NULL) {
271+ /* get ODBC environment handle */
272+ result = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&db_environ_handle);
273+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
274+ rprintf(FERROR, "Error: couldn't get database environment handle\n");
275+ return;
276+ }
277+
278+ /* Setting database enviroment */
279+ result = SQLSetEnvAttr(db_environ_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
280+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
281+ rprintf(FERROR, "Error: couldn't set database environment.\n");
282+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
283+ db_environ_handle = NULL;
284+ return;
285+ }
286+
287+ /* Get a database handle */
288+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle);
289+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
290+ rprintf(FERROR, "Error: couldn't allocate database handle\n");
291+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
292+ db_environ_handle = NULL;
293+ return;
294+ }
295+
296+ /* Set connection attributes */
297+ SQLSetConnectAttr(db_handle, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
298+
299+ /* Connect to the database. */
300+ result = SQLConnect(db_handle, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
301+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
302+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
303+
304+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
305+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1,
306+ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen);
307+ rprintf(FERROR,"Error Connecting to Database %s\n",V_OD_msg);
308+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle);
309+ db_handle = NULL;
310+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
311+ db_environ_handle = NULL;
312+ return;
313+ }
314+ rprintf(FLOG,"Connected to database!\n");
315+
316+ /* get SQL statement handle */
317+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle, &sql_statement_handle);
318+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
319+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
320+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
321+ SQLDisconnect(db_handle);
322+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle);
323+ db_handle = NULL;
324+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
325+ db_environ_handle = NULL;
326+ return;
327+ }
328+ } else {
329+ rprintf(FERROR,"Already connected to database\n");
330+ }
331+ }
332+}
333+
334+void db_log_close()
335+{
336+ if (lp_database_logging(module_id)) {
337+ if (sql_statement_handle != NULL) {
338+ /* free the statement handle first */
339+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle);
340+ sql_statement_handle = NULL;
341+ } else {
342+ rprintf(FERROR,"No sql statement handle to close\n");
343+ }
344+ if (db_handle != NULL) {
345+ /* disconnect, and free the database handle. */
346+ SQLDisconnect(db_handle);
347+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle);
348+ db_handle = NULL;
349+ } else {
350+ rprintf(FERROR,"Database already closed");
351+ }
352+ if (db_environ_handle != NULL) {
353+ /* free the environment handle */
354+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
355+ db_environ_handle = NULL;
356+ } else {
357+ rprintf(FERROR,"No environment handle to close");
358+ }
359+ }
360+}
361+
362+static long get_unique_session_id()
363+{
364+ long unique;
365+ char strSqlStatement[1024];
366+
367+ if (db_handle != NULL) {
368+ /* choose the appropriate select statement based upon which DBMS we're using.
369+ * different datbases use different methods to get a unique ID. Some use a counter
370+ * object (sequence), others use an auto increment datatype and have a method
371+ * to get the last ID inserted using this connection. */
372+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) {
373+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT NEXTVAL('%s');",lp_sequence_name(module_id));
374+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) {
375+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT %s.NEXTVAL FROM dual;",lp_sequence_name(module_id));
376+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) {
377+ snprintf(strSqlStatement,sizeof(strSqlStatement),"VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id));
378+ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */
379+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT LAST_INSERT_ID()");
380+ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */
381+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT @@IDENTITY");
382+ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */
383+ snprintf(strSqlStatement,sizeof(strSqlStatement),lp_custom_unique_id_select(module_id));
384+ }
385+
386+ /* bind the 1st column to unique */
387+ SQLBindCol(sql_statement_handle,1,SQL_C_LONG,&unique,150,&V_OD_err);
388+ /* execute the SQL statement */
389+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
390+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
391+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
392+ rprintf(FERROR,"Error at get_sequence: Error in Select! %s %s\n",strSqlStatement,V_OD_msg);
393+ } else {
394+ result = SQLFetch(sql_statement_handle);
395+ if (result != SQL_NO_DATA && unique != 0) {
396+ rprintf(FINFO,"Got unique sequence! %ld\n",unique);
397+ } else {
398+ rprintf(FERROR,"Error at get_sequence: Didn't get unique session ID\n");
399+ }
400+ /* Close the cursor so the statement can be re-used */
401+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
402+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
403+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
404+ rprintf(FERROR,"Error at get_sequence: Error in closing SQL statement handle %s\n",V_OD_msg);
405+ return unique;
406+ }
407+ return unique;
408+ }
409+ }
410+ rprintf(FERROR,"Error at get_sequence: Not connected to database\n");
411+ return -1;
412+}
413+
414+
415+void db_log_session()
416+{
417+ char strSqlStatement[1024];
418+ int gotSessionID = 0;
419+ if (lp_database_logging(module_id)) {
420+ /* if we're using a sequence via the nextval command to get a unique ID, we need to get it before
421+ * we do the insert. We also get the unique ID now if custom, and get_custom_id_before_insert is set. */
422+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0
423+ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0
424+ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0
425+ || (strcmp(lp_unique_id_method(module_id),"custom") == 0
426+ && lp_get_custom_id_before_insert(module_id))) {
427+ session_id = get_unique_session_id();
428+ gotSessionID = 1;
429+ 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());
430+ } else {
431+ /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */
432+ 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());
433+ }
434+
435+ /* Insert the new session into the database */
436+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
437+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
438+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
439+ rprintf(FERROR,"Error at db_log_session: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
440+ }
441+
442+ /* close the cursor so the statement handle can be re-used. */
443+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
444+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
445+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
446+ rprintf(FERROR,"Error in resetting SQL statement handle %s\n",V_OD_msg);
447+ }
448+ /* get the session ID for databases that give the unique ID after an insert */
449+ if (gotSessionID == 0) {
450+ session_id = get_unique_session_id();
451+ }
452+ }
453+ else {
454+ rprintf(FERROR,"Error at db_log_session: Not connected to database!\n");
455+ }
456+}
457+
458+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation)
459+{
460+ extern struct stats stats;
461+ char strSqlStatement[1024];
462+ char strFilePath[255];
463+ char strFileName[255];
464+ char strFileSize[255];
465+ int64 intBytesTransferred;
466+ int64 intCheckSumBytes;
467+
468+ if (lp_database_logging(module_id)) {
469+ if (db_handle != NULL) {
470+ snprintf(strFileName,sizeof(strFileName), "%s",f_name(file));
471+ snprintf(strFilePath, sizeof(strFilePath), "%s", file->basedir?file->basedir:"");
472+ snprintf(strFileSize,sizeof(strFileSize),"%.0f", (double)file->length);
473+ if (am_sender) {
474+ intBytesTransferred = stats.total_written - initial_stats->total_written;
475+ } else {
476+ intBytesTransferred = stats.total_read - initial_stats->total_read;
477+ }
478+
479+ if (!am_sender) {
480+ intCheckSumBytes = stats.total_written - initial_stats->total_written;
481+ } else {
482+ intCheckSumBytes = stats.total_read - initial_stats->total_read;
483+ }
484+
485+ 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);
486+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
487+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
488+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
489+ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
490+ if (result == SQL_INVALID_HANDLE)
491+ rprintf(FERROR,"INVALID HANDLE\n");
492+ }
493+ } else {
494+ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n");
495+ }
496+ }
497+}
498+
499+
500+void db_log_exit(int code, const char *file, int line)
501+{
502+ char strSqlStatement[2048];
503+ const char *error_text;
504+ extern struct stats stats;
505+ if (db_handle != NULL) {
506+ if (lp_database_logging(module_id)) {
507+ if (code != 0) {
508+ error_text = rerr_name(code);
509+ if (!error_text) {
510+ error_text = "unexplained error";
511+ }
512+ } else {
513+ error_text = "";
514+ }
515+ 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);
516+
517+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
518+
519+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
520+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
521+ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
522+ }
523+ }
524+ } else {
525+ rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
526+ }
527+}
bd8bf8b1 528--- /dev/null 1 Jan 1970 00:00:00 -0000
2c40f06f
WD
529+++ dblog-tables-mysql.sql 2004-04-07 23:00:13.000000000 -0700
530@@ -0,0 +1,43 @@
531+drop table transfer;
532+drop table exit;
533+drop table session;
534+
535+CREATE TABLE session (
536+ id int auto_increment NOT NULL,
537+ date timestamp NOT NULL,
538+ ip_address varchar(15) NOT NULL,
539+ username varchar(20) NOT NULL,
540+ module_name varchar(20) NOT NULL,
541+ module_path varchar(255) NOT NULL,
542+ process_id int NOT NULL,
543+ Primary Key (id)
544+);
545+
546+CREATE TABLE transfer (
547+ id int auto_increment NOT NULL,
548+ session_id int NOT NULL,
549+ date timestamp NOT NULL,
550+ file_path varchar(255) NOT NULL,
551+ file_name varchar(255) NOT NULL,
552+ file_size bigint NOT NULL,
553+ bytes_transferred bigint NOT NULL,
554+ checksum_bytes_transferred bigint NOT NULL,
555+ operation varchar(20),
556+ Primary Key (id),
557+ foreign key (session_id) references session (id)
558+);
559+
560+CREATE TABLE exit (
561+ id int auto_increment NOT NULL,
562+ session_id int NOT NULL,
563+ date timestamp NOT NULL,
564+ total_bytes_written bigint NOT NULL,
565+ total_bytes_read bigint NOT NULL,
566+ total_size bigint NOT NULL,
567+ error_text varchar(128) NOT NULL,
568+ error_code int NOT NULL,
569+ error_file varchar(64) NOT NULL,
570+ error_line int NOT NULL,
571+ Primary Key (id),
572+ foreign key (session_id) references session (id)
573+);
bd8bf8b1 574--- /dev/null 1 Jan 1970 00:00:00 -0000
2c40f06f
WD
575+++ dblog-tables-postgresql.sql 2004-04-07 22:59:55.000000000 -0700
576@@ -0,0 +1,45 @@
577+drop table transfer;
578+drop table exit;
579+drop table session;
580+drop sequence session_id_seq;
581+create sequence session_id_seq;
582+
583+CREATE TABLE "session" (
584+ "id" int NOT NULL,
585+ "date" timestamp NOT NULL default now(),
586+ "ip_address" varchar(15) NOT NULL,
587+ "username" varchar(20) NOT NULL,
588+ "module_name" varchar(20) NOT NULL,
589+ "module_path" varchar(255) NOT NULL,
590+ "process_id" int NOT NULL,
591+ Primary Key (id)
592+);
593+
594+CREATE TABLE "transfer" (
595+ "id" serial NOT NULL,
596+ "session_id" int NOT NULL,
597+ "date" timestamp NOT NULL default now(),
598+ "file_path" varchar(512) NOT NULL,
599+ "file_name" varchar(512) NOT NULL,
600+ "file_size" bigint NOT NULL,
601+ "bytes_transferred" bigint NOT NULL,
602+ "checksum_bytes_transferred" bigint NOT NULL,
603+ "operation" varchar(20),
604+ Primary Key (id),
605+ foreign key (session_id) references session (id)
606+);
607+
608+CREATE TABLE "exit" (
609+ "id" serial NOT NULL,
610+ "session_id" int NOT NULL,
611+ "date" timestamp NOT NULL default now(),
612+ "total_bytes_written" bigint NOT NULL,
613+ "total_bytes_read" bigint NOT NULL,
614+ "total_size" bigint NOT NULL,
615+ "error_text" varchar(128) NOT NULL,
616+ "error_code" int NOT NULL,
617+ "error_file" varchar(64) NOT NULL,
618+ "error_line" int NOT NULL,
619+ Primary Key (id),
620+ foreign key (session_id) references session (id)
621+);
622--- loadparm.c 4 Feb 2004 07:31:29 -0000 1.50
623+++ loadparm.c 8 Apr 2004 06:31:18 -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
697+++ log.c 8 Apr 2004 05:56:32 -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
708+++ main.c 8 Apr 2004 05:56:32 -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
54691942 720+++ proto.h 15 Apr 2004 18:51:15 -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
761+++ receiver.c 8 Apr 2004 05:56:32 -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
774+++ sender.c 8 Apr 2004 05:56:32 -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) {