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