68b0aaad4568bb0269c47001617b770d550a6de6
[rsync/rsync-patches.git] / ODBC-dblog.diff
1 Add support for logging daemon messages to an SQL database.
2
3 After applying this patch, run these commands for a successful build:
4
5     autoconf
6     autoheader
7     ./configure --enable-ODBC
8     make proto
9     make
10
11 See the file "instructions" (after applying this patch) for more info.
12
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
26 @@ -23,6 +23,7 @@
27  
28  extern int io_error;
29  extern int keep_partial;
30 +extern int am_generator;
31  extern int log_got_error;
32  extern char *partial_dir;
33  
34 @@ -144,8 +145,13 @@ void _exit_cleanup(int code, const char 
35                         code = RERR_PARTIAL;
36         }
37  
38 -       if (code)
39 +       if (code) {
40                 log_exit(code, file, line);
41 +#ifdef HAVE_LIBODBC
42 +               db_log_exit(code,file,line);
43 +               db_log_close();
44 +#endif
45 +       }
46  
47         if (verbose > 2) {
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);
53  
54         log_init();
55 +#ifdef HAVE_LIBODBC
56 +       db_log_open();
57 +#endif
58  
59  #ifdef HAVE_PUTENV
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);
65 +#ifdef HAVE_LIBODBC
66 +                       db_log_session();
67 +#endif
68                 } else {
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])
75  fi
76  
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) ])
82 +
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
88 @@ -0,0 +1,35 @@
89 +error type             description
90 +0                      not an error.
91 +1                      authentication
92 +2                      file/dir deletion failed
93 +3                      connection closed
94 +4                      read error
95 +5                      multiplexing overflow
96 +6                      unexpected tag
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
104 +14                     lseek failed
105 +15                     write failed
106 +16                     rename failed
107 +17                     rsync hack failed
108 +18                     "invalid basis_dir index
109 +19                     fstat failed
110 +20                     is a directory
111 +21                     open file failed
112 +22                     mkstemp failed
113 +23                     close failed
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
119 +29                     mknod failed
120 +30                     failed to stat
121 +31                     unlink
122 +32                     failed to open file/directory
123 +33                     open?
124 --- orig/dblog-tables-mysql.sql 2005-09-23 16:08:39
125 +++ dblog-tables-mysql.sql      2005-09-23 16:08:39
126 @@ -0,0 +1,64 @@
127 +drop table transfer;
128 +drop table exit;
129 +drop table session;
130 +
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,
139 +       Primary Key (id)
140 +);
141 +
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),
151 +       Primary Key (id),
152 +       foreign key (session_id) references session (id)
153 +);
154 +
155 +CREATE TABLE exit (
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,
167 +       Primary Key (id),
168 +       foreign key (session_id) references session (id)
169 +);
170 +
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),
178 +       PrimaryKey (id),
179 +       foreign key (session_id) references session (id)
180 +);
181 +
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,
187 +       mode                    int NOT NULL,
188 +       PrimaryKey (id),
189 +       foreign key (session_id) references session (id)
190 +);
191 --- orig/dblog-tables-postgresql.sql    2005-09-23 16:00:34
192 +++ dblog-tables-postgresql.sql 2005-09-23 16:00:34
193 @@ -0,0 +1,67 @@
194 +drop table transfer;
195 +drop table exit;
196 +drop table session;
197 +drop sequence session_id_seq;
198 +create sequence session_id_seq;
199 +
200 +CREATE TABLE "session" (
201 +       "id"                    int NOT NULL,
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,
208 +       Primary Key (id)
209 +);
210 +
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),
220 +       Primary Key (id),
221 +       foreign key (session_id) references session (id)
222 +);
223 +
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,
236 +       Primary Key (id),
237 +       foreign key (session_id) references session (id)
238 +);
239 +
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),
247 +       Primary Key (id),
248 +       foreign key (session_id) references session (id)
249 +
250 +);
251 +
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,
258 +       Primary Key (id),
259 +       foreign key (session_id) references session (id)
260 +);
261 --- orig/dblog.c        2005-09-24 18:25:52
262 +++ dblog.c     2005-09-24 18:25:52
263 @@ -0,0 +1,549 @@
264 +/*
265 + *  ODBC Database logging functions
266 + *
267 + *  Written by Steve Sether, April 2004
268 + *  steve@vellmont.com
269 + */
270 +
271 +#include "rsync.h"
272 +
273 +#ifdef HAVE_SQL_H
274 +#include <sql.h>
275 +#else
276 +#ifdef HAVE_ODBC_SQL_H
277 +#include <odbc/sql.h>
278 +#endif
279 +#endif
280 +
281 +#ifdef HAVE_SQLEXT_H
282 +#include <sqlext.h>
283 +#else
284 +#ifdef HAVE_ODBC_SQLEXT_H
285 +#include <odbc/sqlext.h>
286 +#endif
287 +#endif
288 +
289 +#ifdef HAVE_SQLTYPES_H
290 +#include <sqltypes.h>
291 +#else
292 +#ifdef HAVE_ODBC_SQLTYPES_H
293 +#include <odbc/sqltypes.h>
294 +#endif
295 +#endif
296 +
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;
308 +extern int dry_run;
309 +
310 +
311 +char sql_status[10];                           /* Status SQL */
312 +SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id;
313 +SQLSMALLINT V_OD_mlen, V_OD_colanz;
314 +char V_OD_msg[200], V_OD_buffer[200];
315 +SQLINTEGER session_id;
316 +
317 +
318 +/* This function simply removes invalid characters from the SQL statement
319 + * to prevent SQL injection attacks. */
320 +char *sanitizeSql(const char *input)
321 +{
322 +       char *out, *ptr;
323 +       const char *c;
324 +
325 +       if (strlen(input) > ((~(unsigned int)0)>>1)-3)
326 +               return 0;
327 +       if (!(out = ptr = new_array(char, strlen(input) * 2 + 1)))
328 +               return 0;
329 +
330 +       for (c = input;  *c;  c++) {
331 +               switch (*c) {
332 +               case '\'':
333 +                       *ptr++ = '\'';
334 +                       *ptr++ = '\'';
335 +                       break;
336 +               case '\b':
337 +                       *ptr++ = '\\';
338 +                       *ptr++ = 'b';
339 +                       break;
340 +               case '\n':
341 +                       *ptr++ = '\\';
342 +                       *ptr++ = 'n';
343 +                       break;
344 +               case '\r':
345 +                       *ptr++ = '\\';
346 +                       *ptr++ = 'r';
347 +                       break;
348 +               case '\t':
349 +                       *ptr++ = '\\';
350 +                       *ptr++ = 't';
351 +                       break;
352 +               default:
353 +                       *ptr++ = *c;
354 +                       break;
355 +               }
356 +       }
357 +       *ptr = '\0';
358 +       return out;
359 +}
360 +
361 +void db_log_open(void)
362 +{
363 +       if (!lp_database_logging(module_id))
364 +               return;
365 +
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");
370 +               return;
371 +       }
372 +
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;
379 +               return;
380 +       }
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;
388 +                       return;
389 +               }
390 +
391 +               /* Set connection attributes for the generator db connection */
392 +               SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
393 +
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);
398 +
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;
407 +                       return;
408 +               }
409 +               rprintf(FLOG,"Connected to database for generator!\n");
410 +       } else {
411 +               rprintf(FERROR,"Already connected to database for generator\n");
412 +       }
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;
420 +                       return;
421 +               }
422 +
423 +               /* Set connection attributes for the receiver db connection */
424 +               SQLSetConnectAttr(db_handle_r, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
425 +
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);
430 +
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;
439 +                       return;
440 +               }
441 +               rprintf(FLOG,"Connected to database for receiver!\n");
442 +       } else {
443 +               rprintf(FERROR,"Already connected to database for receiver\n");
444 +       }
445 +
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;
456 +               return;
457 +       }
458 +
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;
469 +               return;
470 +       }
471 +}
472 +
473 +void db_log_close()
474 +{
475 +       if (!lp_database_logging(module_id))
476 +               return;
477 +
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;
483 +               } else {
484 +                       rprintf(FERROR,"No generator sql statement handle to close\n");
485 +               }
486 +
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;
492 +               } else {
493 +                       rprintf(FERROR,"Generator database connection already closed\n");
494 +               }
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;
500 +               } else {
501 +                       rprintf(FERROR,"No receiver sql statement handle to close\n");
502 +               }
503 +
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;
509 +               } else {
510 +                       rprintf(FERROR,"Receiver database connection already closed\n");
511 +               }
512 +       }
513 +
514 +       if (db_environ_handle != NULL) {
515 +               /* free the environment handle */
516 +               SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle);
517 +               db_environ_handle = NULL;
518 +       } else {
519 +               rprintf(FERROR,"No environment handle to close\n");
520 +       }
521 +}
522 +
523 +static long get_unique_session_id()
524 +{
525 +       long unique;
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;
529 +
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));
553 +               }
554 +
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);
562 +               } else {
563 +                       result = SQLFetch(sql_statement_handle);
564 +                       if (result != SQL_NO_DATA && unique != 0) {
565 +                               rprintf(FINFO,"Got unique sequence! %ld\n",unique);
566 +                       } else {
567 +                               rprintf(FERROR,"Error at get_sequence:  Didn't get unique session ID\n");
568 +                       }
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);
574 +                               return unique;
575 +                       }
576 +                       return unique;
577 +               }
578 +       }
579 +       rprintf(FERROR,"Error at get_sequence: Not connected to database\n");
580 +       return -1;
581 +}
582 +
583 +void db_log_session()
584 +{
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;
589 +
590 +       if (!lp_database_logging(module_id))
591 +               return;
592 +
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();
604 +                       gotSessionID = 1;
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());
609 +               } else {
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());
615 +               }
616 +
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);
622 +               }
623 +
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);
629 +               }
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();
633 +               }
634 +       } else {
635 +               rprintf(FERROR,"Error at db_log_session:  Not connected to database!\n");
636 +       }
637 +}
638 +
639 +void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation)
640 +{
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;
650 +
651 +       if (!lp_database_logging(module_id))
652 +               return;
653 +
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;
660 +               }
661 +               clean_fname(strFileNamePtr, 0);
662 +               if (*strFileNamePtr == '/')
663 +                       strFileNamePtr++;
664 +
665 +               snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)file->length);
666 +               if (am_sender) {
667 +                       intBytesTransferred = stats.total_written - initial_stats->total_written;
668 +               } else {
669 +                       intBytesTransferred = stats.total_read - initial_stats->total_read;
670 +               }
671 +
672 +               if (!am_sender) {
673 +                       intCheckSumBytes = stats.total_written - initial_stats->total_written;
674 +               } else {
675 +                       intCheckSumBytes = stats.total_read - initial_stats->total_read;
676 +               }
677 +
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");
689 +               }
690 +       } else {
691 +               rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n");
692 +       }
693 +}
694 +
695 +void db_log_exit(int code, const char *file, int line)
696 +{
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;
702 +
703 +       if (!lp_database_logging(module_id))
704 +               return;
705 +
706 +       if (db_handle != NULL) {
707 +               if (code != 0) {
708 +                       error_text = rerr_name(code);
709 +                       if (!error_text) {
710 +                               error_text = "unexplained error";
711 +                       }
712 +               } else {
713 +                       error_text = "";
714 +               }
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());
719 +
720 +               result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
721 +
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);
725 +               }
726 +       } else {
727 +               rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n");
728 +       }
729 +}
730 +
731 +void db_log_delete(char *fname, int mode)
732 +{
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;
736 +
737 +       if (!am_daemon || dry_run || !lp_database_logging(module_id))
738 +               return;
739 +
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);
744 +
745 +               result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
746 +
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);
750 +               }
751 +       } else {
752 +               rprintf(FERROR,"Error at db_log_delete: Not connected to database!\n");
753 +       }
754 +}
755 +
756 +void db_log_error(enum logcode code, int errcode, const char *format,...)
757 +{
758 +       char strSqlStatement[MAXPATHLEN+1024];
759 +       va_list ap;
760 +       char buf[MAXPATHLEN+512];
761 +       size_t len;
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;
764 +
765 +       if (!lp_database_logging(module_id))
766 +               return;
767 +
768 +       va_start(ap, format);
769 +       len = vsnprintf(buf, sizeof buf, format, ap);
770 +       va_end(ap);
771 +
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[] = "[...]";
777 +
778 +               /* Reset length, and zero-terminate the end of our buffer */
779 +               len = sizeof buf - 1;
780 +               buf[len] = '\0';
781 +
782 +               /* Copy the ellipsis to the end of the string, but give
783 +                * us one extra character:
784 +                *
785 +                *                  v--- null byte at buf[sizeof buf - 1]
786 +                *        abcdefghij0
787 +                *     -> abcd[...]00  <-- now two null bytes at end
788 +                *
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') {
794 +                       buf[len-1] = '\n';
795 +               }
796 +       }
797 +
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));
802 +
803 +               result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS);
804 +
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);
808 +               }
809 +       } else {
810 +               rprintf(FERROR,"Error at db_log_error: Not connected to database!\n");
811 +       }
812 +}
813 --- orig/instructions   2005-09-23 15:58:24
814 +++ instructions        2005-09-23 15:58:24
815 @@ -0,0 +1,84 @@
816 +This patch adds the following options:
817 +
818 +"database logging"
819 +    If set to True, rsync will attempt to connect to
820 +    the specified datasource and write to the named tables.
821 +    Defaults to False.
822 +
823 +"database datasource"
824 +    Specifies the name of the ODBC data source to use.
825 +
826 +"database username"
827 +    The username to use when connecting to the database.
828 +
829 +"database password"
830 +    The password to use when connecting to the database.
831 +
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.
836 +
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.
840 +
841 +"exit table name"
842 +
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).
847 +
848 +"delete table name"
849 +
850 +    The name of the table to log deleted files/directories to.
851 +
852 +"error table name"
853 +
854 +   The name of the table errors will be logged to.
855 +
856 +"unique id method"
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:
862 +
863 +       nextval-postgres
864 +           uses the syntax of nextval for PostgreSQL databases
865 +
866 +       nextval-oracle
867 +           uses the syntax of nextval for Oracle databases
868 +
869 +       nextval-db2
870 +           uses the syntax of nextval for DB2 databases
871 +
872 +       last_insert_id
873 +           uses the last_insert_id() command for the MySQL databases
874 +
875 +       @@IDENTITY
876 +           uses the @@IDENTITY command for Sybase databases
877 +
878 +       custom
879 +           Define your own method to get a unique identifier.  See the
880 +           "custom unique id select", and the "get custom id before select"
881 +           parameters.
882 +
883 +"sequence name"
884 +    If your database supports sequences, list the name of the sequence to use
885 +    for the session unique identifier.
886 +
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.
892 +
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
903  {
904         char *auth_users;
905         char *comment;
906 +       char *custom_unique_id_select;
907 +       char *database_datasource;
908 +       char *database_password;
909 +       char *database_username;
910 +       char *delete_table_name;
911         char *dont_compress;
912 +       char *error_table_name;
913         char *exclude;
914         char *exclude_from;
915 +       char *exit_table_name;
916         char *filter;
917         char *gid;
918         char *hosts_allow;
919 @@ -137,13 +144,19 @@ typedef struct
920         char *prexfer_exec;
921         char *refuse_options;
922         char *secrets_file;
923 +       char *sequence_name;
924 +       char *session_table_name;
925         char *temp_dir;
926 +       char *transfer_table_name;
927         char *uid;
928 +       char *unique_id_method;
929  
930         int max_connections;
931         int max_verbosity;
932         int timeout;
933  
934 +       BOOL database_logging;
935 +       BOOL get_custom_id_before_insert;
936         BOOL ignore_errors;
937         BOOL ignore_nonreadable;
938         BOOL list;
939 @@ -163,9 +176,16 @@ static service sDefault =
940  {
941   /* auth_users; */             NULL,
942   /* comment; */                        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,
950   /* exclude; */                        NULL,
951   /* exclude_from; */           NULL,
952 + /* exit_table_name; */                NULL,
953   /* filter; */                 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,
966  
967   /* max_connections; */                0,
968   /* max_verbosity; */          1,
969   /* timeout; */                        0,
970  
971 + /* database_logging; */       False,
972 + /* get_custom_id_before_insert; */ True,
973   /* ignore_errors; */          False,
974   /* ignore_nonreadable; */     False,
975   /* list; */                   True,
976 @@ -287,10 +313,19 @@ static struct parm_struct parm_table[] =
977  
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
1013  
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)
1039  
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)
1043  
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 {
1052  /*
1053   * Map from rsync error code to name, or return NULL.
1054   */
1055 -static char const *rerr_name(int code)
1056 +char const *rerr_name(int code)
1057  {
1058         int i;
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)
1063  
1064         if (am_daemon) {
1065                 log_exit(0, __FILE__, __LINE__);
1066 +#ifdef HAVE_LIBODBC
1067 +               db_log_exit(0,__FILE__,__LINE__);
1068 +#endif
1069                 if (f == -1 || !am_sender)
1070                         return;
1071         }
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
1075  
1076         if (maxname < 1) {
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",
1080 +                       fname);
1081 +#endif
1082                 fnametmp[0] = '\0';
1083                 return 0;
1084         }
1085 @@ -290,6 +294,11 @@ static int receive_data(int f_in, char *
1086                                         rsyserr(FERROR, errno,
1087                                                 "lseek failed on %s",
1088                                                 full_fname(fname));
1089 +#ifdef HAVE_LIBODBC
1090 +                                       db_log_error(FERROR, 14,
1091 +                                               "lseek failed on %s",
1092 +                                               full_fname(fname));
1093 +#endif
1094                                         exit_cleanup(RERR_FILEIO);
1095                                 }
1096                                 continue;
1097 @@ -315,6 +324,9 @@ static int receive_data(int f_in, char *
1098             report_write_error:
1099                 rsyserr(FERROR, errno, "write failed on %s",
1100                         full_fname(fname));
1101 +#ifdef HAVE_LIBODBC
1102 +               db_log_error(FERROR, 15, "write failed on %s",full_fname(fname));
1103 +#endif
1104                 exit_cleanup(RERR_FILEIO);
1105         }
1106  
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),
1115 +                                       partialptr);
1116 +#endif
1117                         } else {
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.");
1126 +#endif
1127                         exit_cleanup(RERR_PROTOCOL);
1128                 }
1129  
1130 @@ -535,6 +556,11 @@ int recv_files(int f_in, struct file_lis
1131                                         rprintf(FERROR,
1132                                                 "invalid basis_dir index: %d.\n",
1133                                                 fnamecmp_type);
1134 +#ifdef HAVE_LIBODBC
1135 +                                       db_log_error(FERROR, 18,
1136 +                                               "invalid basis_dir index: %d.\n",
1137 +                                               fnamecmp_type);
1138 +#endif
1139                                         exit_cleanup(RERR_PROTOCOL);
1140                                 }
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));
1148 +#endif
1149                         discard_receive_data(f_in, file->length);
1150                         close(fd1);
1151                         continue;
1152 @@ -593,6 +622,9 @@ int recv_files(int f_in, struct file_lis
1153                          */
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));
1158 +#endif
1159                         discard_receive_data(f_in, file->length);
1160                         close(fd1);
1161                         continue;
1162 @@ -616,6 +648,9 @@ int recv_files(int f_in, struct file_lis
1163                         if (fd2 == -1) {
1164                                 rsyserr(FERROR, errno, "open %s failed",
1165                                         full_fname(fname));
1166 +#ifdef HAVE_LIBODBC
1167 +                                       db_log_error(FERROR,22, "open %s failed", full_fname(fname));
1168 +#endif
1169                                 discard_receive_data(f_in, file->length);
1170                                 if (fd1 != -1)
1171                                         close(fd1);
1172 @@ -649,6 +684,10 @@ int recv_files(int f_in, struct file_lis
1173                         if (fd2 == -1) {
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));
1179 +#endif
1180                                 discard_receive_data(f_in, file->length);
1181                                 if (fd1 != -1)
1182                                         close(fd1);
1183 @@ -671,12 +710,19 @@ int recv_files(int f_in, struct file_lis
1184  
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");
1189 +#endif
1190  
1191                 if (fd1 != -1)
1192                         close(fd1);
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));
1199 +#endif
1200                         exit_cleanup(RERR_FILEIO);
1201                 }
1202  
1203 @@ -727,6 +773,12 @@ int recv_files(int f_in, struct file_lis
1204                                 rprintf(msgtype,
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",
1210 +                                       errstr, fname,
1211 +                                       keptstr, redostr);
1212 +#endif
1213                         }
1214                         if (!phase) {
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,
1219  
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");
1224 +#endif
1225  
1226                 if (mbuf) {
1227                         j = unmap_file(mbuf);