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