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