1 Add support for logging daemon messages to an SQL database.
3 After applying this patch, run these commands for a successful build:
7 ./configure --enable-ODBC
11 See the file "instructions" (after applying this patch) for more info.
13 --- orig/Makefile.in 2006-01-14 08:14:29
14 +++ Makefile.in 2005-09-23 15:59:02
15 @@ -31,7 +31,7 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/
16 ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \
17 zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o
18 OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \
19 - main.o checksum.o match.o syscall.o log.o backup.o
20 + main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@
21 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \
22 fileio.o batch.o clientname.o chmod.o
23 OBJS3=progress.o pipe.o
24 --- orig/cleanup.c 2006-01-14 08:14:29
25 +++ cleanup.c 2005-09-23 15:59:19
29 extern int keep_partial;
30 +extern int am_generator;
31 extern int log_got_error;
32 extern char *partial_dir;
34 @@ -144,8 +145,13 @@ void _exit_cleanup(int code, const char
40 log_exit(code, file, line);
42 + db_log_exit(code,file,line);
48 rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n",
49 --- orig/clientserver.c 2006-01-14 08:14:29
50 +++ clientserver.c 2005-09-23 15:59:36
51 @@ -387,6 +387,9 @@ static int rsync_module(int f_in, int f_
52 XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES);
60 if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) {
61 @@ -624,6 +627,9 @@ static int rsync_module(int f_in, int f_
62 rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n",
63 am_sender ? "on" : "to",
64 request, auth_user, host, addr);
69 rprintf(FLOG, "rsync %s %s from %s (%s)\n",
70 am_sender ? "on" : "to",
71 --- orig/configure.in 2006-01-15 14:52:33
72 +++ configure.in 2005-09-24 18:38:47
73 @@ -552,6 +552,12 @@ if test x"$with_included_popt" != x"yes"
74 AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes])
77 +AC_ARG_ENABLE(ODBC, AC_HELP_STRING([--enable-ODBC], [compile in support for ODBC database logging]),
78 + [ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h)
79 + AC_CHECK_LIB(odbc,SQLExecDirect)
80 + EXTRA_OBJECT="$EXTRA_OBJECT dblog.o"
81 + AC_SUBST(EXTRA_OBJECT) ])
83 AC_MSG_CHECKING([whether to use included libpopt])
84 if test x"$with_included_popt" = x"yes"; then
85 AC_MSG_RESULT($srcdir/popt)
86 --- orig/db_log_error-list.txt 2005-09-23 15:58:32
87 +++ db_log_error-list.txt 2005-09-23 15:58:32
89 +error type description
92 +2 file/dir deletion failed
95 +5 multiplexing overflow
97 +7 over long v-string received
98 +8 invalid block length
99 +9 invalid checksum length
100 +10 invalid remainder length
101 +11 failed to write error
102 +12 attempting to send over-long vstring
103 +13 temporary filename too long
107 +17 rsync hack failed
108 +18 "invalid basis_dir index
114 +24 failed verification
115 +25 IO error, skipping deletion.
116 +26 directory creation failed
117 +27 ignoring unsafe symbolic link
118 +28 symbolic link failed
122 +32 failed to open file/directory
124 --- orig/dblog-tables-mysql.sql 2005-09-23 16:08:39
125 +++ dblog-tables-mysql.sql 2005-09-23 16:08:39
127 +drop table transfer;
131 +CREATE TABLE session (
132 + id int auto_increment NOT NULL,
133 + date timestamp NOT NULL,
134 + ip_address varchar(15) NOT NULL,
135 + username varchar(20) NOT NULL,
136 + module_name varchar(20) NOT NULL,
137 + module_path varchar(255) NOT NULL,
138 + process_id int NOT NULL,
142 +CREATE TABLE transfer (
143 + id int auto_increment NOT NULL,
144 + session_id int NOT NULL,
145 + date timestamp NOT NULL,
146 + file_name varchar(255) NOT NULL,
147 + file_size bigint NOT NULL,
148 + bytes_transferred bigint NOT NULL,
149 + checksum_bytes_transferred bigint NOT NULL,
150 + operation varchar(20),
152 + foreign key (session_id) references session (id)
156 + id int auto_increment NOT NULL,
157 + session_id int NOT NULL,
158 + date timestamp NOT NULL,
159 + total_bytes_written bigint NOT NULL,
160 + total_bytes_read bigint NOT NULL,
161 + total_size bigint NOT NULL,
162 + error_text varchar(128) NOT NULL,
163 + error_code int NOT NULL,
164 + error_file varchar(64) NOT NULL,
165 + error_line int NOT NULL,
166 + process_id int NOT NULL,
168 + foreign key (session_id) references session (id)
171 +CREATE TABLE error (
172 + id int auto_increment NOT NULL,
173 + session_id int NOT NULL,
174 + date timestamp NOT NULL,
175 + logcode bigint NOT NULL,
176 + error_number bigint NOT NULL,
177 + error_text varchar(512),
179 + foreign key (session_id) references session (id)
182 +CREATE TABLE delete (
183 + id serial NOT NULL,
184 + session_id int NOT NULL,
185 + date timestamp NOT NULL,
186 + path varchar(512) NOT NULL,
189 + foreign key (session_id) references session (id)
191 --- orig/dblog-tables-postgresql.sql 2005-09-23 16:00:34
192 +++ dblog-tables-postgresql.sql 2005-09-23 16:00:34
194 +drop table transfer;
197 +drop sequence session_id_seq;
198 +create sequence session_id_seq;
200 +CREATE TABLE "session" (
202 + "date" timestamp NOT NULL default now(),
203 + "ip_address" varchar(15) NOT NULL,
204 + "username" varchar(20) NOT NULL,
205 + "module_name" varchar(20) NOT NULL,
206 + "module_path" varchar(255) NOT NULL,
207 + "process_id" int NOT NULL,
211 +CREATE TABLE "transfer" (
212 + "id" serial NOT NULL,
213 + "session_id" int NOT NULL,
214 + "date" timestamp NOT NULL default now(),
215 + "file_name" varchar(512) NOT NULL,
216 + "file_size" bigint NOT NULL,
217 + "bytes_transferred" bigint NOT NULL,
218 + "checksum_bytes_transferred" bigint NOT NULL,
219 + "operation" varchar(20),
221 + foreign key (session_id) references session (id)
224 +CREATE TABLE "exit" (
225 + "id" serial NOT NULL,
226 + "session_id" int NOT NULL,
227 + "date" timestamp NOT NULL default now(),
228 + "total_bytes_written" bigint NOT NULL,
229 + "total_bytes_read" bigint NOT NULL,
230 + "total_size" bigint NOT NULL,
231 + "error_text" varchar(128) NOT NULL,
232 + "error_code" int NOT NULL,
233 + "error_file" varchar(64) NOT NULL,
234 + "error_line" int NOT NULL,
235 + "process_id" int NOT NULL,
237 + foreign key (session_id) references session (id)
240 +CREATE TABLE "error" (
241 + "id" serial NOT NULL,
242 + "session_id" int NOT NULL,
243 + "date" timestamp NOT NULL default now(),
244 + "logcode" int NOT NULL,
245 + "error_number" int NOT NULL,
246 + "error_text" varchar(512),
248 + foreign key (session_id) references session (id)
252 +CREATE TABLE "delete" (
253 + "id" serial NOT NULL,
254 + "session_id" int NOT NULL,
255 + "date" timestamp NOT NULL default now(),
256 + "path" varchar(512) NOT NULL,
257 + "mode" int NOT NULL,
259 + foreign key (session_id) references session (id)
261 --- orig/dblog.c 2005-09-24 18:25:52
262 +++ dblog.c 2005-09-24 18:25:52
265 + * ODBC Database logging functions
267 + * Written by Steve Sether, April 2004
268 + * steve@vellmont.com
276 +#ifdef HAVE_ODBC_SQL_H
277 +#include <odbc/sql.h>
281 +#ifdef HAVE_SQLEXT_H
284 +#ifdef HAVE_ODBC_SQLEXT_H
285 +#include <odbc/sqlext.h>
289 +#ifdef HAVE_SQLTYPES_H
290 +#include <sqltypes.h>
292 +#ifdef HAVE_ODBC_SQLTYPES_H
293 +#include <odbc/sqltypes.h>
297 +SQLHENV db_environ_handle; /* Handle ODBC environment */
298 +long result; /* result of functions */
299 +SQLHDBC db_handle_g = NULL; /* database connection handle for generator*/
300 +SQLHDBC db_handle_r = NULL; /* database connection handle for sender */
301 +SQLHSTMT sql_statement_handle_g; /* SQL statement handle for generator*/
302 +SQLHSTMT sql_statement_handle_r; /* SQL statement handle for receiver*/
303 +extern int am_daemon;
304 +extern int am_sender;
305 +extern int am_generator;
306 +extern char *auth_user;
307 +extern int module_id;
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;
318 +/* This function simply removes invalid characters from the SQL statement
319 + * to prevent SQL injection attacks. */
320 +char *sanitizeSql(const char *input)
325 + if (strlen(input) > ((~(unsigned int)0)>>1)-3)
327 + if (!(out = ptr = new_array(char, strlen(input) * 2 + 1)))
330 + for (c = input; *c; c++) {
361 +void db_log_open(void)
363 + if (!lp_database_logging(module_id))
366 + /* get ODBC environment handle */
367 + result = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&db_environ_handle);
368 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
369 + rprintf(FERROR, "Error: couldn't get database environment handle\n");
373 + /* Setting database enviroment */
374 + result = SQLSetEnvAttr(db_environ_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
375 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
376 + rprintf(FERROR, "Error: couldn't set database environment.\n");
377 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
378 + db_environ_handle = NULL;
381 + if (db_handle_g == NULL) {
382 + /* Get a database handle for the generator*/
383 + result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_g);
384 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
385 + rprintf(FERROR, "Error: couldn't allocate database handle for generator\n");
386 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
387 + db_environ_handle = NULL;
391 + /* Set connection attributes for the generator db connection */
392 + SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
394 + /* get the database connection for the generator. */
395 + result = SQLConnect(db_handle_g, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
396 + (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
397 + (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
399 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
400 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g, 1,
401 + sql_status, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
402 + rprintf(FERROR,"Error Connecting to Database (generator) %s\n",V_OD_msg);
403 + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
404 + db_handle_g = NULL;
405 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
406 + db_environ_handle = NULL;
409 + rprintf(FLOG,"Connected to database for generator!\n");
411 + rprintf(FERROR,"Already connected to database for generator\n");
413 + if (db_handle_r == NULL) {
414 + /* Get a database handle for the receiver */
415 + result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_r);
416 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
417 + rprintf(FERROR, "Error: couldn't allocate database handle for receiver\n");
418 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
419 + db_environ_handle = NULL;
423 + /* Set connection attributes for the receiver db connection */
424 + SQLSetConnectAttr(db_handle_r, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
426 + /* get the generator connection for the receiver. */
427 + result = SQLConnect(db_handle_r, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS,
428 + (SQLCHAR*) lp_database_username(module_id), SQL_NTS,
429 + (SQLCHAR*) lp_database_password(module_id), SQL_NTS);
431 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
432 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1,
433 + sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen);
434 + rprintf(FERROR,"Error Connecting to Database (receiver) %s\n",V_OD_msg);
435 + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
436 + db_handle_r = NULL;
437 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
438 + db_environ_handle = NULL;
441 + rprintf(FLOG,"Connected to database for receiver!\n");
443 + rprintf(FERROR,"Already connected to database for receiver\n");
446 + /* get SQL statement handle for generator */
447 + result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_g, &sql_statement_handle_g);
448 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
449 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
450 + rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
451 + SQLDisconnect(db_handle_g);
452 + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
453 + db_handle_g = NULL;
454 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
455 + db_environ_handle = NULL;
459 + /* get SQL statement handle for receiver */
460 + result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_r, &sql_statement_handle_r);
461 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
462 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
463 + rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg);
464 + SQLDisconnect(db_handle_r);
465 + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
466 + db_handle_r = NULL;
467 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
468 + db_environ_handle = NULL;
475 + if (!lp_database_logging(module_id))
478 + if (am_generator) {
479 + if (sql_statement_handle_g != NULL) {
480 + /* free the statement handle first */
481 + SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g);
482 + sql_statement_handle_g = NULL;
484 + rprintf(FERROR,"No generator sql statement handle to close\n");
487 + if (db_handle_g != NULL) {
488 + /* disconnect, and free the database handle. */
489 + SQLDisconnect(db_handle_g);
490 + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g);
491 + db_handle_g = NULL;
493 + rprintf(FERROR,"Generator database connection already closed\n");
495 + } else { /* must be receiver */
496 + if (sql_statement_handle_r != NULL) {
497 + /* free the statement handle first */
498 + SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_r);
499 + sql_statement_handle_r = NULL;
501 + rprintf(FERROR,"No receiver sql statement handle to close\n");
504 + if (db_handle_r != NULL) {
505 + /* disconnect, and free the database handle. */
506 + SQLDisconnect(db_handle_r);
507 + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r);
508 + db_handle_r = NULL;
510 + rprintf(FERROR,"Receiver database connection already closed\n");
514 + if (db_environ_handle != NULL) {
515 + /* free the environment handle */
516 + SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
517 + db_environ_handle = NULL;
519 + rprintf(FERROR,"No environment handle to close\n");
523 +static long get_unique_session_id()
526 + char strSqlStatement[1024];
527 + SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
528 + SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
530 + if (db_handle != NULL) {
531 + /* choose the appropriate select statement based upon which DBMS we're using.
532 + * different datbases use different methods to get a unique ID. Some use a counter
533 + * object (sequence), others use an auto increment datatype and have a method
534 + * to get the last ID inserted using this connection. */
535 + if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) {
536 + snprintf(strSqlStatement, sizeof strSqlStatement,
537 + "SELECT NEXTVAL('%s');", lp_sequence_name(module_id));
538 + } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) {
539 + snprintf(strSqlStatement, sizeof strSqlStatement,
540 + "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id));
541 + } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) {
542 + snprintf(strSqlStatement, sizeof strSqlStatement,
543 + "VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id));
544 + } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */
545 + snprintf(strSqlStatement, sizeof strSqlStatement,
546 + "SELECT LAST_INSERT_ID()");
547 + } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */
548 + snprintf(strSqlStatement, sizeof strSqlStatement,
549 + "SELECT @@IDENTITY");
550 + } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */
551 + snprintf(strSqlStatement, sizeof strSqlStatement,
552 + lp_custom_unique_id_select(module_id));
555 + /* bind the 1st column to unique */
556 + SQLBindCol(sql_statement_handle,1,SQL_C_LONG,&unique,150,&V_OD_err);
557 + /* execute the SQL statement */
558 + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
559 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
560 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
561 + rprintf(FERROR,"Error at get_sequence: Error in Select! %s %s\n",strSqlStatement,V_OD_msg);
563 + result = SQLFetch(sql_statement_handle);
564 + if (result != SQL_NO_DATA && unique != 0) {
565 + rprintf(FINFO,"Got unique sequence! %ld\n",unique);
567 + rprintf(FERROR,"Error at get_sequence: Didn't get unique session ID\n");
569 + /* Close the cursor so the statement can be re-used */
570 + result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
571 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
572 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
573 + rprintf(FERROR,"Error at get_sequence: Error in closing SQL statement handle %s\n",V_OD_msg);
579 + rprintf(FERROR,"Error at get_sequence: Not connected to database\n");
583 +void db_log_session()
585 + char strSqlStatement[1024];
586 + int gotSessionID = 0;
587 + SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
588 + SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
590 + if (!lp_database_logging(module_id))
593 + if (db_handle != NULL) {
594 + /* if we're using a sequence via the nextval command to
595 + * get a unique ID, we need to get it before we do the
596 + * insert. We also get the unique ID now if custom,
597 + * and get_custom_id_before_insert is set. */
598 + if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0
599 + || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0
600 + || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0
601 + || (strcmp(lp_unique_id_method(module_id),"custom") == 0
602 + && lp_get_custom_id_before_insert(module_id))) {
603 + session_id = get_unique_session_id();
605 + snprintf(strSqlStatement, sizeof strSqlStatement,
606 + "INSERT INTO %s (id, date, ip_address, username, module_name, module_path, process_id) VALUES ('%ld', '%s', '%s', '%s','%s','%s','%d');",
607 + lp_session_table_name(module_id), session_id, timestring(time(NULL)), client_addr(0),
608 + auth_user, lp_name(module_id), lp_path(module_id), getpid());
610 + /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */
611 + snprintf(strSqlStatement, sizeof strSqlStatement,
612 + "INSERT INTO %s (date, ip_address, username, module_name, module_path, process_id) VALUES ('%s', '%s', '%s', '%s','%s','%d');",
613 + lp_session_table_name(module_id), timestring(time(NULL)), client_addr(0), auth_user,
614 + lp_name(module_id), lp_path(module_id), getpid());
617 + /* Insert the new session into the database */
618 + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
619 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
620 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
621 + rprintf(FERROR,"Error at db_log_session: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
624 + /* close the cursor so the statement handle can be re-used. */
625 + result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE);
626 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
627 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
628 + rprintf(FERROR,"Error in resetting SQL statement handle %s\n",V_OD_msg);
630 + /* get the session ID for databases that give the unique ID after an insert */
631 + if (gotSessionID == 0) {
632 + session_id = get_unique_session_id();
635 + rprintf(FERROR,"Error at db_log_session: Not connected to database!\n");
639 +void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation)
641 + extern struct stats stats;
642 + char strSqlStatement[2048];
643 + char strFileName[MAXPATHLEN];
644 + char *strFileNamePtr;
645 + char strFileSize[255];
646 + int64 intBytesTransferred;
647 + int64 intCheckSumBytes;
648 + SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
649 + SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
651 + if (!lp_database_logging(module_id))
654 + if (db_handle != NULL) {
655 + strFileNamePtr = f_name(file, NULL);
656 + if (am_sender && file->dir.root) {
657 + pathjoin(strFileName, sizeof strFileName,
658 + file->dir.root, strFileNamePtr);
659 + strFileNamePtr = strFileName;
661 + clean_fname(strFileNamePtr, 0);
662 + if (*strFileNamePtr == '/')
665 + snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)file->length);
667 + intBytesTransferred = stats.total_written - initial_stats->total_written;
669 + intBytesTransferred = stats.total_read - initial_stats->total_read;
673 + intCheckSumBytes = stats.total_written - initial_stats->total_written;
675 + intCheckSumBytes = stats.total_read - initial_stats->total_read;
678 + snprintf(strSqlStatement, sizeof strSqlStatement,
679 + "INSERT INTO %s (session_id,date, file_name, file_size, bytes_transferred, checksum_bytes_transferred, operation) VALUES ('%ld','%s','%s','%s','%Ld','%Ld','%s');",
680 + lp_transfer_table_name(module_id), session_id, timestring(time(NULL)),
681 + sanitizeSql(strFileNamePtr), strFileSize, intBytesTransferred,
682 + intCheckSumBytes, operation);
683 + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
684 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
685 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
686 + rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
687 + if (result == SQL_INVALID_HANDLE)
688 + rprintf(FERROR,"INVALID HANDLE\n");
691 + rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n");
695 +void db_log_exit(int code, const char *file, int line)
697 + char strSqlStatement[2048];
698 + const char *error_text;
699 + extern struct stats stats;
700 + SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
701 + SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
703 + if (!lp_database_logging(module_id))
706 + if (db_handle != NULL) {
708 + error_text = rerr_name(code);
710 + error_text = "unexplained error";
715 + snprintf(strSqlStatement, sizeof strSqlStatement,
716 + "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');",
717 + lp_exit_table_name(module_id), session_id, timestring(time(NULL)), stats.total_written,
718 + stats.total_read, stats.total_size, error_text, code, file, line, getpid());
720 + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
722 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
723 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
724 + rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
727 + rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
731 +void db_log_delete(char *fname, int mode)
733 + char strSqlStatement[2048];
734 + SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
735 + SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
737 + if (!am_daemon || dry_run || !lp_database_logging(module_id))
740 + if (db_handle != NULL) {
741 + snprintf(strSqlStatement, sizeof strSqlStatement,
742 + "INSERT INTO %s (session_id, date, path, mode) VALUES ('%ld','%s','%s','%d');",
743 + lp_delete_table_name(module_id), session_id, timestring(time(NULL)), sanitizeSql(fname), mode);
745 + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
747 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
748 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
749 + rprintf(FERROR,"Error at db_log_delete: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
752 + rprintf(FERROR,"Error at db_log_delete: Not connected to database!\n");
756 +void db_log_error(enum logcode code, int errcode, const char *format,...)
758 + char strSqlStatement[MAXPATHLEN+1024];
760 + char buf[MAXPATHLEN+512];
762 + SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r;
763 + SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r;
765 + if (!lp_database_logging(module_id))
768 + va_start(ap, format);
769 + len = vsnprintf(buf, sizeof buf, format, ap);
772 + /* Deal with buffer overruns. Instead of panicking, just
773 + * truncate the resulting string. (Note that configure ensures
774 + * that we have a vsnprintf() that doesn't ever return -1.) */
775 + if (len > sizeof buf - 1) {
776 + const char ellipsis[] = "[...]";
778 + /* Reset length, and zero-terminate the end of our buffer */
779 + len = sizeof buf - 1;
782 + /* Copy the ellipsis to the end of the string, but give
783 + * us one extra character:
785 + * v--- null byte at buf[sizeof buf - 1]
787 + * -> abcd[...]00 <-- now two null bytes at end
789 + * If the input format string has a trailing newline,
790 + * we copy it into that extra null; if it doesn't, well,
791 + * all we lose is one byte. */
792 + strncpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis);
793 + if (format[strlen(format)-1] == '\n') {
798 + if (db_handle != NULL) {
799 + snprintf(strSqlStatement, sizeof strSqlStatement,
800 + "INSERT INTO %s (session_id, date, logcode, error_number, error_text) VALUES ('%ld','%s','%d','%d','%s');",
801 + lp_error_table_name(module_id), session_id, timestring(time(NULL)), code, errcode, sanitizeSql(buf));
803 + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
805 + if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) {
806 + SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
807 + rprintf(FERROR,"Error at db_log_error: Error in Insert %s %s\n",strSqlStatement,V_OD_msg);
810 + rprintf(FERROR,"Error at db_log_error: Not connected to database!\n");
813 --- orig/instructions 2005-09-23 15:58:24
814 +++ instructions 2005-09-23 15:58:24
816 +This patch adds the following options:
819 + If set to True, rsync will attempt to connect to
820 + the specified datasource and write to the named tables.
823 +"database datasource"
824 + Specifies the name of the ODBC data source to use.
827 + The username to use when connecting to the database.
830 + The password to use when connecting to the database.
832 +"transfer table name"
833 + The name of the transfer table to log to. This table contains individual
834 + filenames, file sizes, bytes transferred, checksum bytes transferred,
835 + operation (send or receive), and a timestamp.
837 +"session table name"
838 + The name of the session table to log to. This table contains the username,
839 + module name, module path, ip address, process ID, and a timestamp.
843 + The name of the exit table to log to. This table contains the total bytes
844 + read, total bytes written, total size of all files, error code, line the
845 + error occured at, file the error occured at and the text of the error.
846 + (most of which will be blank if the program exited normally).
850 + The name of the table to log deleted files/directories to.
854 + The name of the table errors will be logged to.
857 + Different databases use different methods to get a unique identifier.
858 + Some databases support sequence objects, and use various forms of the
859 + nextval command to retrieve a unique identifier from it. Other databases
860 + support an autonumber field, and support different methds of retrieving
861 + the ID used in the last insert. Valid values for this option are:
864 + uses the syntax of nextval for PostgreSQL databases
867 + uses the syntax of nextval for Oracle databases
870 + uses the syntax of nextval for DB2 databases
873 + uses the last_insert_id() command for the MySQL databases
876 + uses the @@IDENTITY command for Sybase databases
879 + Define your own method to get a unique identifier. See the
880 + "custom unique id select", and the "get custom id before select"
884 + If your database supports sequences, list the name of the sequence to use
885 + for the session unique identifier.
887 +"custom unique id select"
888 + Only used if you specify the custom method in "unique id method". This is
889 + a SQL statement to be executed to get a unique ID. This SQL statement must
890 + return one column with the unique ID to use for the session ID. Should be
891 + used in concert with the "get custom id before select" parameter.
893 +"get custom id before insert"
894 + This parameter is ignored unless the "unique id method" is set to custom.
895 + If set to true, the "custom unique id select" statement will be executed
896 + BEFORE the session row is inserted into the database. (as is done when a
897 + sequence is used for unique IDs). If False the statement will be executed
898 + after the session row is inserted (as is done when the session ID is
899 + automatically generates unique IDs). Defaults to True.
900 --- orig/loadparm.c 2005-09-19 17:21:10
901 +++ loadparm.c 2005-09-24 18:29:59
902 @@ -120,9 +120,16 @@ typedef struct
906 + char *custom_unique_id_select;
907 + char *database_datasource;
908 + char *database_password;
909 + char *database_username;
910 + char *delete_table_name;
912 + char *error_table_name;
915 + char *exit_table_name;
919 @@ -137,13 +144,19 @@ typedef struct
921 char *refuse_options;
923 + char *sequence_name;
924 + char *session_table_name;
926 + char *transfer_table_name;
928 + char *unique_id_method;
934 + BOOL database_logging;
935 + BOOL get_custom_id_before_insert;
937 BOOL ignore_nonreadable;
939 @@ -163,9 +176,16 @@ static service sDefault =
941 /* auth_users; */ NULL,
943 + /* custom_unique_id_select; */ NULL,
944 + /* database_datasource; */ NULL,
945 + /* database_password; */ NULL,
946 + /* database_username; */ NULL,
947 + /* delete_table_name; */ NULL,
948 /* dont_compress; */ "*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz",
949 + /* error_table_name; */ NULL,
951 /* exclude_from; */ NULL,
952 + /* exit_table_name; */ NULL,
954 /* gid; */ NOBODY_GROUP,
955 /* hosts_allow; */ NULL,
956 @@ -180,13 +200,19 @@ static service sDefault =
957 /* prexfer_exec; */ NULL,
958 /* refuse_options; */ NULL,
959 /* secrets_file; */ NULL,
960 + /* sequence_name; */ NULL,
961 + /* session_table_name; */ NULL,
962 /* temp_dir; */ NULL,
963 + /* transfer_table_name; */ NULL,
964 /* uid; */ NOBODY_USER,
965 + /* unique_id_method; */ NULL,
967 /* max_connections; */ 0,
968 /* max_verbosity; */ 1,
971 + /* database_logging; */ False,
972 + /* get_custom_id_before_insert; */ True,
973 /* ignore_errors; */ False,
974 /* ignore_nonreadable; */ False,
976 @@ -287,10 +313,19 @@ static struct parm_struct parm_table[] =
978 {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0},
979 {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL,0},
980 + {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0},
981 + {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0},
982 + {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging, NULL,0},
983 + {"database password", P_STRING, P_LOCAL, &sDefault.database_password, NULL,0},
984 + {"database username", P_STRING, P_LOCAL, &sDefault.database_username, NULL,0},
985 + {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0},
986 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0},
987 + {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0},
988 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0},
989 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0},
990 + {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name, NULL,0},
991 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0},
992 + {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0},
993 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0},
994 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0},
995 {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL,0},
996 @@ -312,11 +347,15 @@ static struct parm_struct parm_table[] =
997 {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0},
998 {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0},
999 {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0},
1000 + {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name, NULL,0},
1001 + {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0},
1002 {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0},
1003 {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0},
1004 {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0},
1005 {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0},
1006 + {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0},
1007 {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0},
1008 + {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0},
1009 {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0},
1010 {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL,0},
1011 {NULL, P_BOOL, P_NONE, NULL, NULL,0}
1012 @@ -377,9 +416,16 @@ FN_GLOBAL_INTEGER(lp_syslog_facility, &G
1014 FN_LOCAL_STRING(lp_auth_users, auth_users)
1015 FN_LOCAL_STRING(lp_comment, comment)
1016 +FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select)
1017 +FN_LOCAL_STRING(lp_database_datasource, database_datasource)
1018 +FN_LOCAL_STRING(lp_database_password, database_password)
1019 +FN_LOCAL_STRING(lp_database_username, database_username)
1020 +FN_LOCAL_STRING(lp_delete_table_name,delete_table_name)
1021 FN_LOCAL_STRING(lp_dont_compress, dont_compress)
1022 +FN_LOCAL_STRING(lp_error_table_name,error_table_name)
1023 FN_LOCAL_STRING(lp_exclude, exclude)
1024 FN_LOCAL_STRING(lp_exclude_from, exclude_from)
1025 +FN_LOCAL_STRING(lp_exit_table_name, exit_table_name)
1026 FN_LOCAL_STRING(lp_filter, filter)
1027 FN_LOCAL_STRING(lp_gid, gid)
1028 FN_LOCAL_STRING(lp_hosts_allow, hosts_allow)
1029 @@ -394,13 +440,19 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf
1030 FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec)
1031 FN_LOCAL_STRING(lp_refuse_options, refuse_options)
1032 FN_LOCAL_STRING(lp_secrets_file, secrets_file)
1033 +FN_LOCAL_STRING(lp_sequence_name,sequence_name)
1034 +FN_LOCAL_STRING(lp_session_table_name,session_table_name)
1035 FN_LOCAL_STRING(lp_temp_dir, temp_dir)
1036 +FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name)
1037 FN_LOCAL_STRING(lp_uid, uid)
1038 +FN_LOCAL_STRING(lp_unique_id_method,unique_id_method)
1040 FN_LOCAL_INTEGER(lp_max_connections, max_connections)
1041 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity)
1042 FN_LOCAL_INTEGER(lp_timeout, timeout)
1044 +FN_LOCAL_BOOL(lp_database_logging, database_logging)
1045 +FN_LOCAL_BOOL(lp_get_custom_id_before_insert,get_custom_id_before_insert)
1046 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors)
1047 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable)
1048 FN_LOCAL_BOOL(lp_list, list)
1049 --- orig/log.c 2006-01-17 02:16:40
1050 +++ log.c 2005-09-23 16:01:13
1051 @@ -87,7 +87,7 @@ struct {
1053 * Map from rsync error code to name, or return NULL.
1055 -static char const *rerr_name(int code)
1056 +char const *rerr_name(int code)
1059 for (i = 0; rerr_names[i].name; i++) {
1060 --- orig/main.c 2006-01-15 14:46:15
1061 +++ main.c 2005-09-23 16:01:22
1062 @@ -159,6 +159,9 @@ static void handle_stats(int f)
1065 log_exit(0, __FILE__, __LINE__);
1066 +#ifdef HAVE_LIBODBC
1067 + db_log_exit(0,__FILE__,__LINE__);
1069 if (f == -1 || !am_sender)
1072 --- orig/receiver.c 2006-01-14 20:27:09
1073 +++ receiver.c 2006-01-14 08:27:51
1074 @@ -174,6 +174,10 @@ static int get_tmpname(char *fnametmp, c
1077 rprintf(FERROR, "temporary filename too long: %s\n", fname);
1078 +#ifdef HAVE_LIBODBC
1079 + db_log_error(FERROR,13, "temporary filename too long: %s\n",
1085 @@ -290,6 +294,11 @@ static int receive_data(int f_in, char *
1086 rsyserr(FERROR, errno,
1087 "lseek failed on %s",
1089 +#ifdef HAVE_LIBODBC
1090 + db_log_error(FERROR, 14,
1091 + "lseek failed on %s",
1092 + full_fname(fname));
1094 exit_cleanup(RERR_FILEIO);
1097 @@ -315,6 +324,9 @@ static int receive_data(int f_in, char *
1099 rsyserr(FERROR, errno, "write failed on %s",
1101 +#ifdef HAVE_LIBODBC
1102 + db_log_error(FERROR, 15, "write failed on %s",full_fname(fname));
1104 exit_cleanup(RERR_FILEIO);
1107 @@ -358,6 +370,12 @@ static void handle_delayed_updates(struc
1108 rsyserr(FERROR, errno,
1109 "rename failed for %s (from %s)",
1110 full_fname(fname), partialptr);
1111 +#ifdef HAVE_LIBODBC
1112 + db_log_error(FERROR, 16,
1113 + "rename failed for %s (from %s)",
1114 + full_fname(fname),
1118 if (remove_sent_files
1119 || (preserve_hard_links
1120 @@ -480,6 +498,9 @@ int recv_files(int f_in, struct file_lis
1121 if (server_filter_list.head
1122 && check_filter(&server_filter_list, fname, 0) < 0) {
1123 rprintf(FERROR, "attempt to hack rsync failed.\n");
1124 +#ifdef HAVE_LIBODBC
1125 + db_log_error(FERROR,17,"attempt to hack rsync failed.");
1127 exit_cleanup(RERR_PROTOCOL);
1130 @@ -535,6 +556,11 @@ int recv_files(int f_in, struct file_lis
1132 "invalid basis_dir index: %d.\n",
1134 +#ifdef HAVE_LIBODBC
1135 + db_log_error(FERROR, 18,
1136 + "invalid basis_dir index: %d.\n",
1139 exit_cleanup(RERR_PROTOCOL);
1141 pathjoin(fnamecmpbuf, sizeof fnamecmpbuf,
1142 @@ -580,6 +606,9 @@ int recv_files(int f_in, struct file_lis
1143 if (fd1 != -1 && do_fstat(fd1,&st) != 0) {
1144 rsyserr(FERROR, errno, "fstat %s failed",
1145 full_fname(fnamecmp));
1146 +#ifdef HAVE_LIBODBC
1147 + db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp));
1149 discard_receive_data(f_in, file->length);
1152 @@ -593,6 +622,9 @@ int recv_files(int f_in, struct file_lis
1154 rprintf(FERROR,"recv_files: %s is a directory\n",
1155 full_fname(fnamecmp));
1156 +#ifdef HAVE_LIBODBC
1157 + db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp));
1159 discard_receive_data(f_in, file->length);
1162 @@ -616,6 +648,9 @@ int recv_files(int f_in, struct file_lis
1164 rsyserr(FERROR, errno, "open %s failed",
1166 +#ifdef HAVE_LIBODBC
1167 + db_log_error(FERROR,22, "open %s failed", full_fname(fname));
1169 discard_receive_data(f_in, file->length);
1172 @@ -649,6 +684,10 @@ int recv_files(int f_in, struct file_lis
1174 rsyserr(FERROR, errno, "mkstemp %s failed",
1175 full_fname(fnametmp));
1176 +#ifdef HAVE_LIBODBC
1177 + db_log_error(FERROR, 22, "mkstemp %s failed",
1178 + full_fname(fnametmp));
1180 discard_receive_data(f_in, file->length);
1183 @@ -671,12 +710,19 @@ int recv_files(int f_in, struct file_lis
1185 if (!log_before_transfer)
1186 log_item(file, &initial_stats, iflags, NULL);
1187 +#ifdef HAVE_LIBODBC
1188 + db_log_transfer(file, &initial_stats, "receive");
1193 if (close(fd2) < 0) {
1194 rsyserr(FERROR, errno, "close failed on %s",
1195 full_fname(fnametmp));
1196 +#ifdef HAVE_LIBODBC
1197 + db_log_error(FERROR, 23, "close failed on %s",
1198 + full_fname(fnametmp));
1200 exit_cleanup(RERR_FILEIO);
1203 @@ -727,6 +773,12 @@ int recv_files(int f_in, struct file_lis
1205 "%s: %s failed verification -- update %s%s.\n",
1206 errstr, fname, keptstr, redostr);
1207 +#ifdef HAVE_LIBODBC
1208 + db_log_error(msgtype,24,
1209 + "%s: %s failed verification -- update %s%s.\n",
1211 + keptstr, redostr);
1215 SIVAL(numbuf, 0, i);
1216 --- orig/sender.c 2006-01-14 20:27:10
1217 +++ sender.c 2005-09-23 16:01:44
1218 @@ -352,6 +352,9 @@ void send_files(struct file_list *flist,
1220 if (!log_before_transfer)
1221 log_item(file, &initial_stats, iflags, NULL);
1222 +#ifdef HAVE_LIBODBC
1223 + db_log_transfer(file, &initial_stats,"send");
1227 j = unmap_file(mbuf);