1Add support for logging daemon messages to an SQL database. 2 3To use this patch, run these commands for a successful build: 4 5 patch -p1 <patches/ODBC-dblog.diff 6 ./prepare-source 7 ./configure --enable-ODBC 8 make 9 10See the newly-created file "instructions" for more info. 11 12--- old/Makefile.in 13+++ new/Makefile.in 14@@ -32,7 +32,7 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/ 15 ZLIBOBJ=zlib/deflate.o zlib/inffast.o zlib/inflate.o zlib/inftrees.o \ 16 zlib/trees.o zlib/zutil.o zlib/adler32.o zlib/compress.o zlib/crc32.o 17 OBJS1=rsync.o generator.o receiver.o cleanup.o sender.o exclude.o util.o \ 18- main.o checksum.o match.o syscall.o log.o backup.o 19+ main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@ 20 OBJS2=options.o flist.o io.o compat.o hlink.o token.o uidlist.o socket.o \ 21 fileio.o batch.o clientname.o chmod.o 22 OBJS3=progress.o pipe.o 23--- old/cleanup.c 24+++ new/cleanup.c 25@@ -27,6 +27,7 @@ extern int am_server; 26 extern int am_daemon; 27 extern int io_error; 28 extern int keep_partial; 29+extern int am_generator; 30 extern int log_got_error; 31 extern char *partial_dir; 32 extern char *logfile_name; 33@@ -174,8 +175,13 @@ NORETURN void _exit_cleanup(int code, co 34 code = exit_code = RERR_PARTIAL; 35 } 36 37- if (code || am_daemon || (logfile_name && (am_server || !verbose))) 38+ if (code || am_daemon || (logfile_name && (am_server || !verbose))) { 39 log_exit(code, file, line); 40+#ifdef HAVE_LIBODBC 41+ db_log_exit(code, file, line); 42+ db_log_close(); 43+#endif 44+ } 45 46 /* FALLTHROUGH */ 47 #include "case_N.h" 48--- old/clientserver.c 49+++ new/clientserver.c 50@@ -394,6 +394,9 @@ static int rsync_module(int f_in, int f_ 51 XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES); 52 53 log_init(1); 54+#ifdef HAVE_LIBODBC 55+ db_log_open(); 56+#endif 57 58 #ifdef HAVE_PUTENV 59 if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { 60@@ -633,6 +636,9 @@ static int rsync_module(int f_in, int f_ 61 rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n", 62 am_sender ? "on" : "to", 63 request, auth_user, host, addr); 64+#ifdef HAVE_LIBODBC 65+ db_log_session(); 66+#endif 67 } else { 68 rprintf(FLOG, "rsync %s %s from %s (%s)\n", 69 am_sender ? "on" : "to", 70--- old/configure.in 71+++ new/configure.in 72@@ -610,6 +610,12 @@ if test x"$with_included_popt" != x"yes" 73 AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes]) 74 fi 75 76+AC_ARG_ENABLE(ODBC, AC_HELP_STRING([--enable-ODBC], [compile in support for ODBC database logging]), 77+ [ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h) 78+ AC_CHECK_LIB(odbc,SQLExecDirect) 79+ EXTRA_OBJECT="$EXTRA_OBJECT dblog.o" 80+ AC_SUBST(EXTRA_OBJECT) ]) 81+ 82 AC_MSG_CHECKING([whether to use included libpopt]) 83 if test x"$with_included_popt" = x"yes"; then 84 AC_MSG_RESULT($srcdir/popt) 85--- old/db_log_error-list.txt 86+++ new/db_log_error-list.txt 87@@ -0,0 +1,35 @@ 88+error type description 89+0 not an error. 90+1 authentication 91+2 file/dir deletion failed 92+3 connection closed 93+4 read error 94+5 multiplexing overflow 95+6 unexpected tag 96+7 over long v-string received 97+8 invalid block length 98+9 invalid checksum length 99+10 invalid remainder length 100+11 failed to write error 101+12 attempting to send over-long vstring 102+13 temporary filename too long 103+14 lseek failed 104+15 write failed 105+16 rename failed 106+17 rsync hack failed 107+18 "invalid basis_dir index 108+19 fstat failed 109+20 is a directory 110+21 open file failed 111+22 mkstemp failed 112+23 close failed 113+24 failed verification 114+25 IO error, skipping deletion. 115+26 directory creation failed 116+27 ignoring unsafe symbolic link 117+28 symbolic link failed 118+29 mknod failed 119+30 failed to stat 120+31 unlink 121+32 failed to open file/directory 122+33 open? 123--- old/dblog-tables-mysql.sql 124+++ new/dblog-tables-mysql.sql 125@@ -0,0 +1,64 @@ 126+drop table transfer; 127+drop table exit; 128+drop table session; 129+ 130+CREATE TABLE session ( 131+ id int auto_increment NOT NULL, 132+ date timestamp NOT NULL, 133+ ip_address varchar(15) NOT NULL, 134+ username varchar(20) NOT NULL, 135+ module_name varchar(20) NOT NULL, 136+ module_path varchar(255) NOT NULL, 137+ process_id int NOT NULL, 138+ Primary Key (id) 139+); 140+ 141+CREATE TABLE transfer ( 142+ id int auto_increment NOT NULL, 143+ session_id int NOT NULL, 144+ date timestamp NOT NULL, 145+ file_name varchar(255) NOT NULL, 146+ file_size bigint NOT NULL, 147+ bytes_transferred bigint NOT NULL, 148+ checksum_bytes_transferred bigint NOT NULL, 149+ operation varchar(20), 150+ Primary Key (id), 151+ foreign key (session_id) references session (id) 152+); 153+ 154+CREATE TABLE exit ( 155+ id int auto_increment NOT NULL, 156+ session_id int NOT NULL, 157+ date timestamp NOT NULL, 158+ total_bytes_written bigint NOT NULL, 159+ total_bytes_read bigint NOT NULL, 160+ total_size bigint NOT NULL, 161+ error_text varchar(128) NOT NULL, 162+ error_code int NOT NULL, 163+ error_file varchar(64) NOT NULL, 164+ error_line int NOT NULL, 165+ process_id int NOT NULL, 166+ Primary Key (id), 167+ foreign key (session_id) references session (id) 168+); 169+ 170+CREATE TABLE error ( 171+ id int auto_increment NOT NULL, 172+ session_id int NOT NULL, 173+ date timestamp NOT NULL, 174+ logcode bigint NOT NULL, 175+ error_number bigint NOT NULL, 176+ error_text varchar(512), 177+ PrimaryKey (id), 178+ foreign key (session_id) references session (id) 179+); 180+ 181+CREATE TABLE delete ( 182+ id serial NOT NULL, 183+ session_id int NOT NULL, 184+ date timestamp NOT NULL, 185+ path varchar(512) NOT NULL, 186+ mode int NOT NULL, 187+ PrimaryKey (id), 188+ foreign key (session_id) references session (id) 189+); 190--- old/dblog-tables-postgresql.sql 191+++ new/dblog-tables-postgresql.sql 192@@ -0,0 +1,67 @@ 193+drop table transfer; 194+drop table exit; 195+drop table session; 196+drop sequence session_id_seq; 197+create sequence session_id_seq; 198+ 199+CREATE TABLE "session" ( 200+ "id" int NOT NULL, 201+ "date" timestamp NOT NULL default now(), 202+ "ip_address" varchar(15) NOT NULL, 203+ "username" varchar(20) NOT NULL, 204+ "module_name" varchar(20) NOT NULL, 205+ "module_path" varchar(255) NOT NULL, 206+ "process_id" int NOT NULL, 207+ Primary Key (id) 208+); 209+ 210+CREATE TABLE "transfer" ( 211+ "id" serial NOT NULL, 212+ "session_id" int NOT NULL, 213+ "date" timestamp NOT NULL default now(), 214+ "file_name" varchar(512) NOT NULL, 215+ "file_size" bigint NOT NULL, 216+ "bytes_transferred" bigint NOT NULL, 217+ "checksum_bytes_transferred" bigint NOT NULL, 218+ "operation" varchar(20), 219+ Primary Key (id), 220+ foreign key (session_id) references session (id) 221+); 222+ 223+CREATE TABLE "exit" ( 224+ "id" serial NOT NULL, 225+ "session_id" int NOT NULL, 226+ "date" timestamp NOT NULL default now(), 227+ "total_bytes_written" bigint NOT NULL, 228+ "total_bytes_read" bigint NOT NULL, 229+ "total_size" bigint NOT NULL, 230+ "error_text" varchar(128) NOT NULL, 231+ "error_code" int NOT NULL, 232+ "error_file" varchar(64) NOT NULL, 233+ "error_line" int NOT NULL, 234+ "process_id" int NOT NULL, 235+ Primary Key (id), 236+ foreign key (session_id) references session (id) 237+); 238+ 239+CREATE TABLE "error" ( 240+ "id" serial NOT NULL, 241+ "session_id" int NOT NULL, 242+ "date" timestamp NOT NULL default now(), 243+ "logcode" int NOT NULL, 244+ "error_number" int NOT NULL, 245+ "error_text" varchar(512), 246+ Primary Key (id), 247+ foreign key (session_id) references session (id) 248+ 249+); 250+ 251+CREATE TABLE "delete" ( 252+ "id" serial NOT NULL, 253+ "session_id" int NOT NULL, 254+ "date" timestamp NOT NULL default now(), 255+ "path" varchar(512) NOT NULL, 256+ "mode" int NOT NULL, 257+ Primary Key (id), 258+ foreign key (session_id) references session (id) 259+); 260--- old/dblog.c 261+++ new/dblog.c 262@@ -0,0 +1,549 @@ 263+/* 264+ * ODBC Database logging functions 265+ * 266+ * Written by Steve Sether, April 2004 267+ * steve@vellmont.com 268+ */ 269+ 270+#include "rsync.h" 271+ 272+#ifdef HAVE_SQL_H 273+#include <sql.h> 274+#else 275+#ifdef HAVE_ODBC_SQL_H 276+#include <odbc/sql.h> 277+#endif 278+#endif 279+ 280+#ifdef HAVE_SQLEXT_H 281+#include <sqlext.h> 282+#else 283+#ifdef HAVE_ODBC_SQLEXT_H 284+#include <odbc/sqlext.h> 285+#endif 286+#endif 287+ 288+#ifdef HAVE_SQLTYPES_H 289+#include <sqltypes.h> 290+#else 291+#ifdef HAVE_ODBC_SQLTYPES_H 292+#include <odbc/sqltypes.h> 293+#endif 294+#endif 295+ 296+SQLHENV db_environ_handle; /* Handle ODBC environment */ 297+long result; /* result of functions */ 298+SQLHDBC db_handle_g = NULL; /* database connection handle for generator*/ 299+SQLHDBC db_handle_r = NULL; /* database connection handle for sender */ 300+SQLHSTMT sql_statement_handle_g; /* SQL statement handle for generator*/ 301+SQLHSTMT sql_statement_handle_r; /* SQL statement handle for receiver*/ 302+extern int am_daemon; 303+extern int am_sender; 304+extern int am_generator; 305+extern char *auth_user; 306+extern int module_id; 307+extern int dry_run; 308+ 309+ 310+char sql_status[10]; /* Status SQL */ 311+SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id; 312+SQLSMALLINT V_OD_mlen, V_OD_colanz; 313+char V_OD_msg[200], V_OD_buffer[200]; 314+SQLINTEGER session_id; 315+ 316+ 317+/* This function simply removes invalid characters from the SQL statement 318+ * to prevent SQL injection attacks. */ 319+char *sanitizeSql(const char *input) 320+{ 321+ char *out, *ptr; 322+ const char *c; 323+ 324+ if (strlen(input) > ((~(unsigned int)0)>>1)-3) 325+ return 0; 326+ if (!(out = ptr = new_array(char, strlen(input) * 2 + 1))) 327+ return 0; 328+ 329+ for (c = input; *c; c++) { 330+ switch (*c) { 331+ case '\'': 332+ *ptr++ = '\''; 333+ *ptr++ = '\''; 334+ break; 335+ case '\b': 336+ *ptr++ = '\\'; 337+ *ptr++ = 'b'; 338+ break; 339+ case '\n': 340+ *ptr++ = '\\'; 341+ *ptr++ = 'n'; 342+ break; 343+ case '\r': 344+ *ptr++ = '\\'; 345+ *ptr++ = 'r'; 346+ break; 347+ case '\t': 348+ *ptr++ = '\\'; 349+ *ptr++ = 't'; 350+ break; 351+ default: 352+ *ptr++ = *c; 353+ break; 354+ } 355+ } 356+ *ptr = '\0'; 357+ return out; 358+} 359+ 360+void db_log_open(void) 361+{ 362+ if (!lp_database_logging(module_id)) 363+ return; 364+ 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+ if (db_handle_g == NULL) { 381+ /* Get a database handle for the generator*/ 382+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_g); 383+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 384+ rprintf(FERROR, "Error: couldn't allocate database handle for generator\n"); 385+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 386+ db_environ_handle = NULL; 387+ return; 388+ } 389+ 390+ /* Set connection attributes for the generator db connection */ 391+ SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); 392+ 393+ /* get the database connection for the generator. */ 394+ result = SQLConnect(db_handle_g, (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_g, 1, 400+ sql_status, &V_OD_err, V_OD_msg, 100, &V_OD_mlen); 401+ rprintf(FERROR,"Error Connecting to Database (generator) %s\n",V_OD_msg); 402+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); 403+ db_handle_g = NULL; 404+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 405+ db_environ_handle = NULL; 406+ return; 407+ } 408+ rprintf(FLOG,"Connected to database for generator!\n"); 409+ } else { 410+ rprintf(FERROR,"Already connected to database for generator\n"); 411+ } 412+ if (db_handle_r == NULL) { 413+ /* Get a database handle for the receiver */ 414+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_r); 415+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 416+ rprintf(FERROR, "Error: couldn't allocate database handle for receiver\n"); 417+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 418+ db_environ_handle = NULL; 419+ return; 420+ } 421+ 422+ /* Set connection attributes for the receiver db connection */ 423+ SQLSetConnectAttr(db_handle_r, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); 424+ 425+ /* get the generator connection for the receiver. */ 426+ result = SQLConnect(db_handle_r, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS, 427+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS, 428+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS); 429+ 430+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 431+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, 432+ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen); 433+ rprintf(FERROR,"Error Connecting to Database (receiver) %s\n",V_OD_msg); 434+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r); 435+ db_handle_r = NULL; 436+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 437+ db_environ_handle = NULL; 438+ return; 439+ } 440+ rprintf(FLOG,"Connected to database for receiver!\n"); 441+ } else { 442+ rprintf(FERROR,"Already connected to database for receiver\n"); 443+ } 444+ 445+ /* get SQL statement handle for generator */ 446+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_g, &sql_statement_handle_g); 447+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 448+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 449+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg); 450+ SQLDisconnect(db_handle_g); 451+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); 452+ db_handle_g = NULL; 453+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 454+ db_environ_handle = NULL; 455+ return; 456+ } 457+ 458+ /* get SQL statement handle for receiver */ 459+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_r, &sql_statement_handle_r); 460+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 461+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 462+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg); 463+ SQLDisconnect(db_handle_r); 464+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r); 465+ db_handle_r = NULL; 466+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 467+ db_environ_handle = NULL; 468+ return; 469+ } 470+} 471+ 472+void db_log_close() 473+{ 474+ if (!lp_database_logging(module_id)) 475+ return; 476+ 477+ if (am_generator) { 478+ if (sql_statement_handle_g != NULL) { 479+ /* free the statement handle first */ 480+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g); 481+ sql_statement_handle_g = NULL; 482+ } else { 483+ rprintf(FERROR,"No generator sql statement handle to close\n"); 484+ } 485+ 486+ if (db_handle_g != NULL) { 487+ /* disconnect, and free the database handle. */ 488+ SQLDisconnect(db_handle_g); 489+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); 490+ db_handle_g = NULL; 491+ } else { 492+ rprintf(FERROR,"Generator database connection already closed\n"); 493+ } 494+ } else { /* must be receiver */ 495+ if (sql_statement_handle_r != NULL) { 496+ /* free the statement handle first */ 497+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_r); 498+ sql_statement_handle_r = NULL; 499+ } else { 500+ rprintf(FERROR,"No receiver sql statement handle to close\n"); 501+ } 502+ 503+ if (db_handle_r != NULL) { 504+ /* disconnect, and free the database handle. */ 505+ SQLDisconnect(db_handle_r); 506+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r); 507+ db_handle_r = NULL; 508+ } else { 509+ rprintf(FERROR,"Receiver database connection already closed\n"); 510+ } 511+ } 512+ 513+ if (db_environ_handle != NULL) { 514+ /* free the environment handle */ 515+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); 516+ db_environ_handle = NULL; 517+ } else { 518+ rprintf(FERROR,"No environment handle to close\n"); 519+ } 520+} 521+ 522+static long get_unique_session_id() 523+{ 524+ long unique; 525+ char strSqlStatement[1024]; 526+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; 527+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; 528+ 529+ if (db_handle != NULL) { 530+ /* choose the appropriate select statement based upon which DBMS we're using. 531+ * different datbases use different methods to get a unique ID. Some use a counter 532+ * object (sequence), others use an auto increment datatype and have a method 533+ * to get the last ID inserted using this connection. */ 534+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) { 535+ snprintf(strSqlStatement, sizeof strSqlStatement, 536+ "SELECT NEXTVAL('%s');", lp_sequence_name(module_id)); 537+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) { 538+ snprintf(strSqlStatement, sizeof strSqlStatement, 539+ "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id)); 540+ } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) { 541+ snprintf(strSqlStatement, sizeof strSqlStatement, 542+ "VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id)); 543+ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */ 544+ snprintf(strSqlStatement, sizeof strSqlStatement, 545+ "SELECT LAST_INSERT_ID()"); 546+ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */ 547+ snprintf(strSqlStatement, sizeof strSqlStatement, 548+ "SELECT @@IDENTITY"); 549+ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */ 550+ snprintf(strSqlStatement, sizeof strSqlStatement, 551+ lp_custom_unique_id_select(module_id)); 552+ } 553+ 554+ /* bind the 1st column to unique */ 555+ SQLBindCol(sql_statement_handle,1,SQL_C_LONG,&unique,150,&V_OD_err); 556+ /* execute the SQL statement */ 557+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); 558+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 559+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 560+ rprintf(FERROR,"Error at get_sequence: Error in Select! %s %s\n",strSqlStatement,V_OD_msg); 561+ } else { 562+ result = SQLFetch(sql_statement_handle); 563+ if (result != SQL_NO_DATA && unique != 0) { 564+ rprintf(FINFO,"Got unique sequence! %ld\n",unique); 565+ } else { 566+ rprintf(FERROR,"Error at get_sequence: Didn't get unique session ID\n"); 567+ } 568+ /* Close the cursor so the statement can be re-used */ 569+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE); 570+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 571+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 572+ rprintf(FERROR,"Error at get_sequence: Error in closing SQL statement handle %s\n",V_OD_msg); 573+ return unique; 574+ } 575+ return unique; 576+ } 577+ } 578+ rprintf(FERROR,"Error at get_sequence: Not connected to database\n"); 579+ return -1; 580+} 581+ 582+void db_log_session() 583+{ 584+ char strSqlStatement[1024]; 585+ int gotSessionID = 0; 586+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; 587+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; 588+ 589+ if (!lp_database_logging(module_id)) 590+ return; 591+ 592+ if (db_handle != NULL) { 593+ /* if we're using a sequence via the nextval command to 594+ * get a unique ID, we need to get it before we do the 595+ * insert. We also get the unique ID now if custom, 596+ * and get_custom_id_before_insert is set. */ 597+ if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0 598+ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0 599+ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0 600+ || (strcmp(lp_unique_id_method(module_id),"custom") == 0 601+ && lp_get_custom_id_before_insert(module_id))) { 602+ session_id = get_unique_session_id(); 603+ gotSessionID = 1; 604+ snprintf(strSqlStatement, sizeof strSqlStatement, 605+ "INSERT INTO %s (id, date, ip_address, username, module_name, module_path, process_id) VALUES ('%ld', '%s', '%s', '%s','%s','%s','%d');", 606+ lp_session_table_name(module_id), session_id, timestring(time(NULL)), client_addr(0), 607+ auth_user, lp_name(module_id), lp_path(module_id), getpid()); 608+ } else { 609+ /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */ 610+ snprintf(strSqlStatement, sizeof strSqlStatement, 611+ "INSERT INTO %s (date, ip_address, username, module_name, module_path, process_id) VALUES ('%s', '%s', '%s', '%s','%s','%d');", 612+ lp_session_table_name(module_id), timestring(time(NULL)), client_addr(0), auth_user, 613+ lp_name(module_id), lp_path(module_id), getpid()); 614+ } 615+ 616+ /* Insert the new session into the database */ 617+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); 618+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 619+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 620+ rprintf(FERROR,"Error at db_log_session: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); 621+ } 622+ 623+ /* close the cursor so the statement handle can be re-used. */ 624+ result = SQLFreeStmt(sql_statement_handle,SQL_CLOSE); 625+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 626+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 627+ rprintf(FERROR,"Error in resetting SQL statement handle %s\n",V_OD_msg); 628+ } 629+ /* get the session ID for databases that give the unique ID after an insert */ 630+ if (gotSessionID == 0) { 631+ session_id = get_unique_session_id(); 632+ } 633+ } else { 634+ rprintf(FERROR,"Error at db_log_session: Not connected to database!\n"); 635+ } 636+} 637+ 638+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation) 639+{ 640+ extern struct stats stats; 641+ char strSqlStatement[2048]; 642+ char strFileName[MAXPATHLEN]; 643+ char *strFileNamePtr; 644+ char strFileSize[255]; 645+ int64 intBytesTransferred; 646+ int64 intCheckSumBytes; 647+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; 648+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; 649+ 650+ if (!lp_database_logging(module_id)) 651+ return; 652+ 653+ if (db_handle != NULL) { 654+ strFileNamePtr = f_name(file, NULL); 655+ if (am_sender && file->dir.root) { 656+ pathjoin(strFileName, sizeof strFileName, 657+ file->dir.root, strFileNamePtr); 658+ strFileNamePtr = strFileName; 659+ } 660+ clean_fname(strFileNamePtr, 0); 661+ if (*strFileNamePtr == '/') 662+ strFileNamePtr++; 663+ 664+ snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)file->length); 665+ if (am_sender) { 666+ intBytesTransferred = stats.total_written - initial_stats->total_written; 667+ } else { 668+ intBytesTransferred = stats.total_read - initial_stats->total_read; 669+ } 670+ 671+ if (!am_sender) { 672+ intCheckSumBytes = stats.total_written - initial_stats->total_written; 673+ } else { 674+ intCheckSumBytes = stats.total_read - initial_stats->total_read; 675+ } 676+ 677+ snprintf(strSqlStatement, sizeof strSqlStatement, 678+ "INSERT INTO %s (session_id,date, file_name, file_size, bytes_transferred, checksum_bytes_transferred, operation) VALUES ('%ld','%s','%s','%s','%Ld','%Ld','%s');", 679+ lp_transfer_table_name(module_id), session_id, timestring(time(NULL)), 680+ sanitizeSql(strFileNamePtr), strFileSize, intBytesTransferred, 681+ intCheckSumBytes, operation); 682+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); 683+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 684+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 685+ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); 686+ if (result == SQL_INVALID_HANDLE) 687+ rprintf(FERROR,"INVALID HANDLE\n"); 688+ } 689+ } else { 690+ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n"); 691+ } 692+} 693+ 694+void db_log_exit(int code, const char *file, int line) 695+{ 696+ char strSqlStatement[2048]; 697+ const char *error_text; 698+ extern struct stats stats; 699+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; 700+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; 701+ 702+ if (!lp_database_logging(module_id)) 703+ return; 704+ 705+ if (db_handle != NULL) { 706+ if (code != 0) { 707+ error_text = rerr_name(code); 708+ if (!error_text) { 709+ error_text = "unexplained error"; 710+ } 711+ } else { 712+ error_text = ""; 713+ } 714+ snprintf(strSqlStatement, sizeof strSqlStatement, 715+ "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');", 716+ lp_exit_table_name(module_id), session_id, timestring(time(NULL)), stats.total_written, 717+ stats.total_read, stats.total_size, error_text, code, file, line, getpid()); 718+ 719+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); 720+ 721+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 722+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 723+ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); 724+ } 725+ } else { 726+ rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n"); 727+ } 728+} 729+ 730+void db_log_delete(char *fname, int mode) 731+{ 732+ char strSqlStatement[2048]; 733+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; 734+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; 735+ 736+ if (!am_daemon || dry_run || !lp_database_logging(module_id)) 737+ return; 738+ 739+ if (db_handle != NULL) { 740+ snprintf(strSqlStatement, sizeof strSqlStatement, 741+ "INSERT INTO %s (session_id, date, path, mode) VALUES ('%ld','%s','%s','%d');", 742+ lp_delete_table_name(module_id), session_id, timestring(time(NULL)), sanitizeSql(fname), mode); 743+ 744+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); 745+ 746+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 747+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 748+ rprintf(FERROR,"Error at db_log_delete: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); 749+ } 750+ } else { 751+ rprintf(FERROR,"Error at db_log_delete: Not connected to database!\n"); 752+ } 753+} 754+ 755+void db_log_error(enum logcode code, int errcode, const char *format,...) 756+{ 757+ char strSqlStatement[MAXPATHLEN+1024]; 758+ va_list ap; 759+ char buf[MAXPATHLEN+512]; 760+ size_t len; 761+ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; 762+ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; 763+ 764+ if (!lp_database_logging(module_id)) 765+ return; 766+ 767+ va_start(ap, format); 768+ len = vsnprintf(buf, sizeof buf, format, ap); 769+ va_end(ap); 770+ 771+ /* Deal with buffer overruns. Instead of panicking, just 772+ * truncate the resulting string. (Note that configure ensures 773+ * that we have a vsnprintf() that doesn't ever return -1.) */ 774+ if (len > sizeof buf - 1) { 775+ const char ellipsis[] = "[...]"; 776+ 777+ /* Reset length, and zero-terminate the end of our buffer */ 778+ len = sizeof buf - 1; 779+ buf[len] = '\0'; 780+ 781+ /* Copy the ellipsis to the end of the string, but give 782+ * us one extra character: 783+ * 784+ * v--- null byte at buf[sizeof buf - 1] 785+ * abcdefghij0 786+ * -> abcd[...]00 <-- now two null bytes at end 787+ * 788+ * If the input format string has a trailing newline, 789+ * we copy it into that extra null; if it doesn't, well, 790+ * all we lose is one byte. */ 791+ strncpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis); 792+ if (format[strlen(format)-1] == '\n') { 793+ buf[len-1] = '\n'; 794+ } 795+ } 796+ 797+ if (db_handle != NULL) { 798+ snprintf(strSqlStatement, sizeof strSqlStatement, 799+ "INSERT INTO %s (session_id, date, logcode, error_number, error_text) VALUES ('%ld','%s','%d','%d','%s');", 800+ lp_error_table_name(module_id), session_id, timestring(time(NULL)), code, errcode, sanitizeSql(buf)); 801+ 802+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); 803+ 804+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { 805+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); 806+ rprintf(FERROR,"Error at db_log_error: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); 807+ } 808+ } else { 809+ rprintf(FERROR,"Error at db_log_error: Not connected to database!\n"); 810+ } 811+} 812--- old/instructions 813+++ new/instructions 814@@ -0,0 +1,84 @@ 815+This patch adds the following options: 816+ 817+"database logging" 818+ If set to True, rsync will attempt to connect to 819+ the specified datasource and write to the named tables. 820+ Defaults to False. 821+ 822+"database datasource" 823+ Specifies the name of the ODBC data source to use. 824+ 825+"database username" 826+ The username to use when connecting to the database. 827+ 828+"database password" 829+ The password to use when connecting to the database. 830+ 831+"transfer table name" 832+ The name of the transfer table to log to. This table contains individual 833+ filenames, file sizes, bytes transferred, checksum bytes transferred, 834+ operation (send or receive), and a timestamp. 835+ 836+"session table name" 837+ The name of the session table to log to. This table contains the username, 838+ module name, module path, ip address, process ID, and a timestamp. 839+ 840+"exit table name" 841+ 842+ The name of the exit table to log to. This table contains the total bytes 843+ read, total bytes written, total size of all files, error code, line the 844+ error occured at, file the error occured at and the text of the error. 845+ (most of which will be blank if the program exited normally). 846+ 847+"delete table name" 848+ 849+ The name of the table to log deleted files/directories to. 850+ 851+"error table name" 852+ 853+ The name of the table errors will be logged to. 854+ 855+"unique id method" 856+ Different databases use different methods to get a unique identifier. 857+ Some databases support sequence objects, and use various forms of the 858+ nextval command to retrieve a unique identifier from it. Other databases 859+ support an autonumber field, and support different methds of retrieving 860+ the ID used in the last insert. Valid values for this option are: 861+ 862+ nextval-postgres 863+ uses the syntax of nextval for PostgreSQL databases 864+ 865+ nextval-oracle 866+ uses the syntax of nextval for Oracle databases 867+ 868+ nextval-db2 869+ uses the syntax of nextval for DB2 databases 870+ 871+ last_insert_id 872+ uses the last_insert_id() command for the MySQL databases 873+ 874+ @@IDENTITY 875+ uses the @@IDENTITY command for Sybase databases 876+ 877+ custom 878+ Define your own method to get a unique identifier. See the 879+ "custom unique id select", and the "get custom id before select" 880+ parameters. 881+ 882+"sequence name" 883+ If your database supports sequences, list the name of the sequence to use 884+ for the session unique identifier. 885+ 886+"custom unique id select" 887+ Only used if you specify the custom method in "unique id method". This is 888+ a SQL statement to be executed to get a unique ID. This SQL statement must 889+ return one column with the unique ID to use for the session ID. Should be 890+ used in concert with the "get custom id before select" parameter. 891+ 892+"get custom id before insert" 893+ This parameter is ignored unless the "unique id method" is set to custom. 894+ If set to true, the "custom unique id select" statement will be executed 895+ BEFORE the session row is inserted into the database. (as is done when a 896+ sequence is used for unique IDs). If False the statement will be executed 897+ after the session row is inserted (as is done when the session ID is 898+ automatically generates unique IDs). Defaults to True. 899--- old/loadparm.c 900+++ new/loadparm.c 901@@ -122,9 +122,16 @@ typedef struct 902 { 903 char *auth_users; 904 char *comment; 905+ char *custom_unique_id_select; 906+ char *database_datasource; 907+ char *database_password; 908+ char *database_username; 909+ char *delete_table_name; 910 char *dont_compress; 911+ char *error_table_name; 912 char *exclude; 913 char *exclude_from; 914+ char *exit_table_name; 915 char *filter; 916 char *gid; 917 char *hosts_allow; 918@@ -142,14 +149,20 @@ typedef struct 919 char *prexfer_exec; 920 char *refuse_options; 921 char *secrets_file; 922+ char *sequence_name; 923+ char *session_table_name; 924 char *temp_dir; 925+ char *transfer_table_name; 926 char *uid; 927+ char *unique_id_method; 928 929 int max_connections; 930 int max_verbosity; 931 int syslog_facility; 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@@ -169,9 +182,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@@ -189,14 +209,20 @@ 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 /* syslog_facility; */ LOG_DAEMON, 970 /* timeout; */ 0, 971 972+ /* database_logging; */ False, 973+ /* get_custom_id_before_insert; */ True, 974 /* ignore_errors; */ False, 975 /* ignore_nonreadable; */ False, 976 /* list; */ True, 977@@ -295,10 +321,19 @@ static struct parm_struct parm_table[] = 978 979 {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0}, 980 {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL,0}, 981+ {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0}, 982+ {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0}, 983+ {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging, NULL,0}, 984+ {"database password", P_STRING, P_LOCAL, &sDefault.database_password, NULL,0}, 985+ {"database username", P_STRING, P_LOCAL, &sDefault.database_username, NULL,0}, 986+ {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0}, 987 {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, 988+ {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0}, 989 {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0}, 990 {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0}, 991+ {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name, NULL,0}, 992 {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0}, 993+ {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0}, 994 {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0}, 995 {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0}, 996 {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL,0}, 997@@ -323,12 +358,16 @@ static struct parm_struct parm_table[] = 998 {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0}, 999 {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0}, 1000 {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0}, 1001+ {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name, NULL,0}, 1002+ {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0}, 1003 {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0}, 1004 {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0}, 1005 {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0}, 1006 {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0}, 1007 {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0}, 1008+ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0}, 1009 {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0}, 1010+ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0}, 1011 {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0}, 1012 {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL,0}, 1013 {NULL, P_BOOL, P_NONE, NULL, NULL,0} 1014@@ -384,9 +423,16 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Global 1015 1016 FN_LOCAL_STRING(lp_auth_users, auth_users) 1017 FN_LOCAL_STRING(lp_comment, comment) 1018+FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select) 1019+FN_LOCAL_STRING(lp_database_datasource, database_datasource) 1020+FN_LOCAL_STRING(lp_database_password, database_password) 1021+FN_LOCAL_STRING(lp_database_username, database_username) 1022+FN_LOCAL_STRING(lp_delete_table_name,delete_table_name) 1023 FN_LOCAL_STRING(lp_dont_compress, dont_compress) 1024+FN_LOCAL_STRING(lp_error_table_name,error_table_name) 1025 FN_LOCAL_STRING(lp_exclude, exclude) 1026 FN_LOCAL_STRING(lp_exclude_from, exclude_from) 1027+FN_LOCAL_STRING(lp_exit_table_name, exit_table_name) 1028 FN_LOCAL_STRING(lp_filter, filter) 1029 FN_LOCAL_STRING(lp_gid, gid) 1030 FN_LOCAL_STRING(lp_hosts_allow, hosts_allow) 1031@@ -404,14 +450,20 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf 1032 FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec) 1033 FN_LOCAL_STRING(lp_refuse_options, refuse_options) 1034 FN_LOCAL_STRING(lp_secrets_file, secrets_file) 1035+FN_LOCAL_STRING(lp_sequence_name,sequence_name) 1036+FN_LOCAL_STRING(lp_session_table_name,session_table_name) 1037 FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility) 1038 FN_LOCAL_STRING(lp_temp_dir, temp_dir) 1039+FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name) 1040 FN_LOCAL_STRING(lp_uid, uid) 1041+FN_LOCAL_STRING(lp_unique_id_method,unique_id_method) 1042 1043 FN_LOCAL_INTEGER(lp_max_connections, max_connections) 1044 FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) 1045 FN_LOCAL_INTEGER(lp_timeout, timeout) 1046 1047+FN_LOCAL_BOOL(lp_database_logging, database_logging) 1048+FN_LOCAL_BOOL(lp_get_custom_id_before_insert,get_custom_id_before_insert) 1049 FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) 1050 FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) 1051 FN_LOCAL_BOOL(lp_list, list) 1052--- old/log.c 1053+++ new/log.c 1054@@ -93,7 +93,7 @@ struct { 1055 /* 1056 * Map from rsync error code to name, or return NULL. 1057 */ 1058-static char const *rerr_name(int code) 1059+char const *rerr_name(int code) 1060 { 1061 int i; 1062 for (i = 0; rerr_names[i].name; i++) { 1063--- old/receiver.c 1064+++ new/receiver.c 1065@@ -110,6 +110,10 @@ static int get_tmpname(char *fnametmp, c 1066 1067 if (maxname < 1) { 1068 rprintf(FERROR, "temporary filename too long: %s\n", fname); 1069+#ifdef HAVE_LIBODBC 1070+ db_log_error(FERROR,13, "temporary filename too long: %s\n", 1071+ fname); 1072+#endif 1073 fnametmp[0] = '\0'; 1074 return 0; 1075 } 1076@@ -173,6 +177,10 @@ static int receive_data(int f_in, char * 1077 if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) { 1078 rsyserr(FERROR, errno, "lseek of %s returned %.0f, not %.0f", 1079 full_fname(fname), (double)j, (double)offset); 1080+#ifdef HAVE_LIBODBC 1081+ db_log_error(FERROR, 14, "lseek failed on %s", 1082+ full_fname(fname)); 1083+#endif 1084 exit_cleanup(RERR_FILEIO); 1085 } 1086 } 1087@@ -230,6 +238,11 @@ static int receive_data(int f_in, char * 1088 "lseek of %s returned %.0f, not %.0f", 1089 full_fname(fname), 1090 (double)pos, (double)offset); 1091+#ifdef HAVE_LIBODBC 1092+ db_log_error(FERROR, 14, 1093+ "lseek failed on %s", 1094+ full_fname(fname)); 1095+#endif 1096 exit_cleanup(RERR_FILEIO); 1097 } 1098 continue; 1099@@ -255,6 +268,9 @@ static int receive_data(int f_in, char * 1100 report_write_error: 1101 rsyserr(FERROR, errno, "write failed on %s", 1102 full_fname(fname)); 1103+#ifdef HAVE_LIBODBC 1104+ db_log_error(FERROR, 15, "write failed on %s",full_fname(fname)); 1105+#endif 1106 exit_cleanup(RERR_FILEIO); 1107 } 1108 1109@@ -298,6 +314,12 @@ static void handle_delayed_updates(struc 1110 rsyserr(FERROR, errno, 1111 "rename failed for %s (from %s)", 1112 full_fname(fname), partialptr); 1113+#ifdef HAVE_LIBODBC 1114+ db_log_error(FERROR, 16, 1115+ "rename failed for %s (from %s)", 1116+ full_fname(fname), 1117+ partialptr); 1118+#endif 1119 } else { 1120 if (remove_source_files 1121 || (preserve_hard_links 1122@@ -422,6 +444,9 @@ int recv_files(int f_in, struct file_lis 1123 if (server_filter_list.head 1124 && check_filter(&server_filter_list, fname, 0) < 0) { 1125 rprintf(FERROR, "attempt to hack rsync failed.\n"); 1126+#ifdef HAVE_LIBODBC 1127+ db_log_error(FERROR,17,"attempt to hack rsync failed."); 1128+#endif 1129 exit_cleanup(RERR_PROTOCOL); 1130 } 1131 1132@@ -478,6 +503,11 @@ int recv_files(int f_in, struct file_lis 1133 rprintf(FERROR, 1134 "invalid basis_dir index: %d.\n", 1135 fnamecmp_type); 1136+#ifdef HAVE_LIBODBC 1137+ db_log_error(FERROR, 18, 1138+ "invalid basis_dir index: %d.\n", 1139+ fnamecmp_type); 1140+#endif 1141 exit_cleanup(RERR_PROTOCOL); 1142 } 1143 pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, 1144@@ -526,6 +556,9 @@ int recv_files(int f_in, struct file_lis 1145 } else if (do_fstat(fd1,&st) != 0) { 1146 rsyserr(FERROR, errno, "fstat %s failed", 1147 full_fname(fnamecmp)); 1148+#ifdef HAVE_LIBODBC 1149+ db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp)); 1150+#endif 1151 discard_receive_data(f_in, file->length); 1152 close(fd1); 1153 continue; 1154@@ -539,6 +572,9 @@ int recv_files(int f_in, struct file_lis 1155 */ 1156 rprintf(FERROR,"recv_files: %s is a directory\n", 1157 full_fname(fnamecmp)); 1158+#ifdef HAVE_LIBODBC 1159+ db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp)); 1160+#endif 1161 discard_receive_data(f_in, file->length); 1162 close(fd1); 1163 continue; 1164@@ -562,6 +598,9 @@ int recv_files(int f_in, struct file_lis 1165 if (fd2 == -1) { 1166 rsyserr(FERROR, errno, "open %s failed", 1167 full_fname(fname)); 1168+#ifdef HAVE_LIBODBC 1169+ db_log_error(FERROR,22, "open %s failed", full_fname(fname)); 1170+#endif 1171 discard_receive_data(f_in, file->length); 1172 if (fd1 != -1) 1173 close(fd1); 1174@@ -595,6 +634,10 @@ int recv_files(int f_in, struct file_lis 1175 if (fd2 == -1) { 1176 rsyserr(FERROR, errno, "mkstemp %s failed", 1177 full_fname(fnametmp)); 1178+#ifdef HAVE_LIBODBC 1179+ db_log_error(FERROR, 22, "mkstemp %s failed", 1180+ full_fname(fnametmp)); 1181+#endif 1182 discard_receive_data(f_in, file->length); 1183 if (fd1 != -1) 1184 close(fd1); 1185@@ -615,12 +658,19 @@ int recv_files(int f_in, struct file_lis 1186 fname, fd2, file->length); 1187 1188 log_item(log_code, file, &initial_stats, iflags, NULL); 1189+#ifdef HAVE_LIBODBC 1190+ db_log_transfer(file, &initial_stats, "receive"); 1191+#endif 1192 1193 if (fd1 != -1) 1194 close(fd1); 1195 if (close(fd2) < 0) { 1196 rsyserr(FERROR, errno, "close failed on %s", 1197 full_fname(fnametmp)); 1198+#ifdef HAVE_LIBODBC 1199+ db_log_error(FERROR, 23, "close failed on %s", 1200+ full_fname(fnametmp)); 1201+#endif 1202 exit_cleanup(RERR_FILEIO); 1203 } 1204 1205@@ -679,6 +729,12 @@ int recv_files(int f_in, struct file_lis 1206 rprintf(msgtype, 1207 "%s: %s failed verification -- update %s%s.\n", 1208 errstr, fname, keptstr, redostr); 1209+#ifdef HAVE_LIBODBC 1210+ db_log_error(msgtype,24, 1211+ "%s: %s failed verification -- update %s%s.\n", 1212+ errstr, fname, 1213+ keptstr, redostr); 1214+#endif 1215 } 1216 if (!phase) { 1217 SIVAL(numbuf, 0, i); 1218--- old/sender.c 1219+++ new/sender.c 1220@@ -355,6 +355,9 @@ void send_files(struct file_list *flist, 1221 end_progress(st.st_size); 1222 1223 log_item(log_code, file, &initial_stats, iflags, NULL); 1224+#ifdef HAVE_LIBODBC 1225+ db_log_transfer(file, &initial_stats,"send"); 1226+#endif 1227 1228 if (mbuf) { 1229 j = unmap_file(mbuf); 1230--- old/proto.h 1231+++ new/proto.h 1232@@ -47,6 +47,14 @@ int start_daemon(int f_in, int f_out); 1233 int daemon_main(void); 1234 void setup_protocol(int f_out,int f_in); 1235 int claim_connection(char *fname,int max_connections); 1236+char *sanitizeSql(const char *input); 1237+void db_log_open(void); 1238+void db_log_close(); 1239+void db_log_session(); 1240+void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation); 1241+void db_log_exit(int code, const char *file, int line); 1242+void db_log_delete(char *fname, int mode); 1243+void db_log_error(enum logcode code, int errcode, const char *format,...); 1244 void set_filter_dir(const char *dir, unsigned int dirlen); 1245 void *push_local_filters(const char *dir, unsigned int dirlen); 1246 void pop_local_filters(void *mem); 1247@@ -147,9 +155,16 @@ char *lp_socket_options(void); 1248 int lp_rsync_port(void); 1249 char *lp_auth_users(int ); 1250 char *lp_comment(int ); 1251+char *lp_custom_unique_id_select(int ); 1252+char *lp_database_datasource(int ); 1253+char *lp_database_password(int ); 1254+char *lp_database_username(int ); 1255+char *lp_delete_table_name(int ); 1256 char *lp_dont_compress(int ); 1257+char *lp_error_table_name(int ); 1258 char *lp_exclude(int ); 1259 char *lp_exclude_from(int ); 1260+char *lp_exit_table_name(int ); 1261 char *lp_filter(int ); 1262 char *lp_gid(int ); 1263 char *lp_hosts_allow(int ); 1264@@ -167,12 +182,18 @@ char *lp_postxfer_exec(int ); 1265 char *lp_prexfer_exec(int ); 1266 char *lp_refuse_options(int ); 1267 char *lp_secrets_file(int ); 1268+char *lp_sequence_name(int ); 1269+char *lp_session_table_name(int ); 1270 int lp_syslog_facility(int ); 1271 char *lp_temp_dir(int ); 1272+char *lp_transfer_table_name(int ); 1273 char *lp_uid(int ); 1274+char *lp_unique_id_method(int ); 1275 int lp_max_connections(int ); 1276 int lp_max_verbosity(int ); 1277 int lp_timeout(int ); 1278+BOOL lp_database_logging(int ); 1279+BOOL lp_get_custom_id_before_insert(int ); 1280 BOOL lp_ignore_errors(int ); 1281 BOOL lp_ignore_nonreadable(int ); 1282 BOOL lp_list(int ); 1283@@ -184,6 +205,7 @@ BOOL lp_write_only(int ); 1284 BOOL lp_load(char *pszFname, int globals_only); 1285 int lp_numservices(void); 1286 int lp_number(char *name); 1287+char const *rerr_name(int code); 1288 void log_init(int restart); 1289 void logfile_close(void); 1290 void logfile_reopen(void); 1291--- old/configure 1292+++ new/configure 1293@@ -664,6 +664,7 @@ INSTALL_DATA 1294 HAVE_REMSH 1295 LIBOBJS 1296 ALLOCA 1297+EXTRA_OBJECT 1298 OBJ_SAVE 1299 OBJ_RESTORE 1300 CC_SHOBJ_FLAG 1301@@ -1258,6 +1259,7 @@ Optional Features: 1302 --disable-largefile omit support for large files 1303 --disable-ipv6 don't even try to use IPv6 1304 --disable-locale turn off locale features 1305+ --enable-ODBC compile in support for ODBC database logging 1306 1307 Optional Packages: 1308 --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] 1309@@ -14159,6 +14161,266 @@ fi 1310 1311 fi 1312 1313+# Check whether --enable-ODBC was given. 1314+if test "${enable_ODBC+set}" = set; then 1315+ enableval=$enable_ODBC; 1316+ 1317+ 1318+for ac_header in sql.h sqlext.h sqltypes.h 1319+do 1320+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` 1321+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then 1322+ { echo "$as_me:$LINENO: checking for $ac_header" >&5 1323+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } 1324+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then 1325+ echo $ECHO_N "(cached) $ECHO_C" >&6 1326+fi 1327+ac_res=`eval echo '${'$as_ac_Header'}'` 1328+ { echo "$as_me:$LINENO: result: $ac_res" >&5 1329+echo "${ECHO_T}$ac_res" >&6; } 1330+else 1331+ # Is the header compilable? 1332+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 1333+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } 1334+cat >conftest.$ac_ext <<_ACEOF 1335+/* confdefs.h. */ 1336+_ACEOF 1337+cat confdefs.h >>conftest.$ac_ext 1338+cat >>conftest.$ac_ext <<_ACEOF 1339+/* end confdefs.h. */ 1340+$ac_includes_default 1341+#include <$ac_header> 1342+_ACEOF 1343+rm -f conftest.$ac_objext 1344+if { (ac_try="$ac_compile" 1345+case "(($ac_try" in 1346+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1347+ *) ac_try_echo=$ac_try;; 1348+esac 1349+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1350+ (eval "$ac_compile") 2>conftest.er1 1351+ ac_status=$? 1352+ grep -v '^ *+' conftest.er1 >conftest.err 1353+ rm -f conftest.er1 1354+ cat conftest.err >&5 1355+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1356+ (exit $ac_status); } && 1357+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' 1358+ { (case "(($ac_try" in 1359+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1360+ *) ac_try_echo=$ac_try;; 1361+esac 1362+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1363+ (eval "$ac_try") 2>&5 1364+ ac_status=$? 1365+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1366+ (exit $ac_status); }; } && 1367+ { ac_try='test -s conftest.$ac_objext' 1368+ { (case "(($ac_try" in 1369+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1370+ *) ac_try_echo=$ac_try;; 1371+esac 1372+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1373+ (eval "$ac_try") 2>&5 1374+ ac_status=$? 1375+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1376+ (exit $ac_status); }; }; then 1377+ ac_header_compiler=yes 1378+else 1379+ echo "$as_me: failed program was:" >&5 1380+sed 's/^/| /' conftest.$ac_ext >&5 1381+ 1382+ ac_header_compiler=no 1383+fi 1384+ 1385+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext 1386+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 1387+echo "${ECHO_T}$ac_header_compiler" >&6; } 1388+ 1389+# Is the header present? 1390+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 1391+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } 1392+cat >conftest.$ac_ext <<_ACEOF 1393+/* confdefs.h. */ 1394+_ACEOF 1395+cat confdefs.h >>conftest.$ac_ext 1396+cat >>conftest.$ac_ext <<_ACEOF 1397+/* end confdefs.h. */ 1398+#include <$ac_header> 1399+_ACEOF 1400+if { (ac_try="$ac_cpp conftest.$ac_ext" 1401+case "(($ac_try" in 1402+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1403+ *) ac_try_echo=$ac_try;; 1404+esac 1405+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1406+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 1407+ ac_status=$? 1408+ grep -v '^ *+' conftest.er1 >conftest.err 1409+ rm -f conftest.er1 1410+ cat conftest.err >&5 1411+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1412+ (exit $ac_status); } >/dev/null; then 1413+ if test -s conftest.err; then 1414+ ac_cpp_err=$ac_c_preproc_warn_flag 1415+ ac_cpp_err=$ac_cpp_err$ac_c_werror_flag 1416+ else 1417+ ac_cpp_err= 1418+ fi 1419+else 1420+ ac_cpp_err=yes 1421+fi 1422+if test -z "$ac_cpp_err"; then 1423+ ac_header_preproc=yes 1424+else 1425+ echo "$as_me: failed program was:" >&5 1426+sed 's/^/| /' conftest.$ac_ext >&5 1427+ 1428+ ac_header_preproc=no 1429+fi 1430+ 1431+rm -f conftest.err conftest.$ac_ext 1432+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 1433+echo "${ECHO_T}$ac_header_preproc" >&6; } 1434+ 1435+# So? What about this header? 1436+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in 1437+ yes:no: ) 1438+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 1439+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} 1440+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 1441+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} 1442+ ac_header_preproc=yes 1443+ ;; 1444+ no:yes:* ) 1445+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 1446+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} 1447+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 1448+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} 1449+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 1450+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} 1451+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 1452+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} 1453+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 1454+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} 1455+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 1456+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} 1457+ 1458+ ;; 1459+esac 1460+{ echo "$as_me:$LINENO: checking for $ac_header" >&5 1461+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } 1462+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then 1463+ echo $ECHO_N "(cached) $ECHO_C" >&6 1464+else 1465+ eval "$as_ac_Header=\$ac_header_preproc" 1466+fi 1467+ac_res=`eval echo '${'$as_ac_Header'}'` 1468+ { echo "$as_me:$LINENO: result: $ac_res" >&5 1469+echo "${ECHO_T}$ac_res" >&6; } 1470+ 1471+fi 1472+if test `eval echo '${'$as_ac_Header'}'` = yes; then 1473+ cat >>confdefs.h <<_ACEOF 1474+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 1475+_ACEOF 1476+ 1477+fi 1478+ 1479+done 1480+ 1481+ 1482+{ echo "$as_me:$LINENO: checking for SQLExecDirect in -lodbc" >&5 1483+echo $ECHO_N "checking for SQLExecDirect in -lodbc... $ECHO_C" >&6; } 1484+if test "${ac_cv_lib_odbc_SQLExecDirect+set}" = set; then 1485+ echo $ECHO_N "(cached) $ECHO_C" >&6 1486+else 1487+ ac_check_lib_save_LIBS=$LIBS 1488+LIBS="-lodbc $LIBS" 1489+cat >conftest.$ac_ext <<_ACEOF 1490+/* confdefs.h. */ 1491+_ACEOF 1492+cat confdefs.h >>conftest.$ac_ext 1493+cat >>conftest.$ac_ext <<_ACEOF 1494+/* end confdefs.h. */ 1495+ 1496+/* Override any GCC internal prototype to avoid an error. 1497+ Use char because int might match the return type of a GCC 1498+ builtin and then its argument prototype would still apply. */ 1499+#ifdef __cplusplus 1500+extern "C" 1501+#endif 1502+char SQLExecDirect (); 1503+int 1504+main () 1505+{ 1506+return SQLExecDirect (); 1507+ ; 1508+ return 0; 1509+} 1510+_ACEOF 1511+rm -f conftest.$ac_objext conftest$ac_exeext 1512+if { (ac_try="$ac_link" 1513+case "(($ac_try" in 1514+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1515+ *) ac_try_echo=$ac_try;; 1516+esac 1517+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1518+ (eval "$ac_link") 2>conftest.er1 1519+ ac_status=$? 1520+ grep -v '^ *+' conftest.er1 >conftest.err 1521+ rm -f conftest.er1 1522+ cat conftest.err >&5 1523+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1524+ (exit $ac_status); } && 1525+ { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' 1526+ { (case "(($ac_try" in 1527+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1528+ *) ac_try_echo=$ac_try;; 1529+esac 1530+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1531+ (eval "$ac_try") 2>&5 1532+ ac_status=$? 1533+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1534+ (exit $ac_status); }; } && 1535+ { ac_try='test -s conftest$ac_exeext' 1536+ { (case "(($ac_try" in 1537+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; 1538+ *) ac_try_echo=$ac_try;; 1539+esac 1540+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 1541+ (eval "$ac_try") 2>&5 1542+ ac_status=$? 1543+ echo "$as_me:$LINENO: \$? = $ac_status" >&5 1544+ (exit $ac_status); }; }; then 1545+ ac_cv_lib_odbc_SQLExecDirect=yes 1546+else 1547+ echo "$as_me: failed program was:" >&5 1548+sed 's/^/| /' conftest.$ac_ext >&5 1549+ 1550+ ac_cv_lib_odbc_SQLExecDirect=no 1551+fi 1552+ 1553+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ 1554+ conftest$ac_exeext conftest.$ac_ext 1555+LIBS=$ac_check_lib_save_LIBS 1556+fi 1557+{ echo "$as_me:$LINENO: result: $ac_cv_lib_odbc_SQLExecDirect" >&5 1558+echo "${ECHO_T}$ac_cv_lib_odbc_SQLExecDirect" >&6; } 1559+if test $ac_cv_lib_odbc_SQLExecDirect = yes; then 1560+ cat >>confdefs.h <<_ACEOF 1561+#define HAVE_LIBODBC 1 1562+_ACEOF 1563+ 1564+ LIBS="-lodbc $LIBS" 1565+ 1566+fi 1567+ 1568+ EXTRA_OBJECT="$EXTRA_OBJECT dblog.o" 1569+ 1570+fi 1571+ 1572+ 1573 { echo "$as_me:$LINENO: checking whether to use included libpopt" >&5 1574 echo $ECHO_N "checking whether to use included libpopt... $ECHO_C" >&6; } 1575 if test x"$with_included_popt" = x"yes"; then 1576@@ -15483,6 +15745,7 @@ INSTALL_DATA!$INSTALL_DATA$ac_delim 1577 HAVE_REMSH!$HAVE_REMSH$ac_delim 1578 LIBOBJS!$LIBOBJS$ac_delim 1579 ALLOCA!$ALLOCA$ac_delim 1580+EXTRA_OBJECT!$EXTRA_OBJECT$ac_delim 1581 OBJ_SAVE!$OBJ_SAVE$ac_delim 1582 OBJ_RESTORE!$OBJ_RESTORE$ac_delim 1583 CC_SHOBJ_FLAG!$CC_SHOBJ_FLAG$ac_delim 1584@@ -15490,7 +15753,7 @@ BUILD_POPT!$BUILD_POPT$ac_delim 1585 LTLIBOBJS!$LTLIBOBJS$ac_delim 1586 _ACEOF 1587 1588- if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 71; then 1589+ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 72; then 1590 break 1591 elif $ac_last_try; then 1592 { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 1593--- old/config.h.in 1594+++ new/config.h.in 1595@@ -155,6 +155,9 @@ 1596 /* Define to 1 if you have the `nsl_s' library (-lnsl_s). */ 1597 #undef HAVE_LIBNSL_S 1598 1599+/* Define to 1 if you have the `odbc' library (-lodbc). */ 1600+#undef HAVE_LIBODBC 1601+ 1602 /* Define to 1 if you have the `popt' library (-lpopt). */ 1603 #undef HAVE_LIBPOPT 1604 1605@@ -280,6 +283,15 @@ 1606 /* Define to 1 if you have the "socketpair" function */ 1607 #undef HAVE_SOCKETPAIR 1608 1609+/* Define to 1 if you have the <sqlext.h> header file. */ 1610+#undef HAVE_SQLEXT_H 1611+ 1612+/* Define to 1 if you have the <sqltypes.h> header file. */ 1613+#undef HAVE_SQLTYPES_H 1614+ 1615+/* Define to 1 if you have the <sql.h> header file. */ 1616+#undef HAVE_SQL_H 1617+ 1618 /* Define to 1 if you have the <stdint.h> header file. */ 1619 #undef HAVE_STDINT_H 1620 1621