X-Git-Url: https://mattmccutchen.net/rsync/rsync-patches.git/blobdiff_plain/1a2bc9cd5090ca968629f4c4a4915a8e49231386..6af518aa4bffb3514a0b430f5cd80baa0e2c2f56:/ODBC-dblog.diff diff --git a/ODBC-dblog.diff b/ODBC-dblog.diff index 4aeede1..98c6f79 100644 --- a/ODBC-dblog.diff +++ b/ODBC-dblog.diff @@ -1,171 +1,128 @@ Add support for logging daemon messages to an SQL database. -After applying this patch, run these commands for a successful build: +To use this patch, run these commands for a successful build: - autoconf - autoheader - ./configure --with-ODBC - make proto + patch -p1 2) { - rprintf(FINFO,"_exit_cleanup(code=%d, file=%s, line=%d): about to call exit(%d)\n", ---- orig/clientserver.c 2005-05-19 08:52:42 -+++ clientserver.c 2004-07-03 20:22:18 -@@ -346,6 +346,9 @@ static int rsync_module(int f_in, int f_ - XFLG_ANCHORED2ABS | XFLG_OLD_PREFIXES); + /* FALLTHROUGH */ + #include "case_N.h" +--- old/clientserver.c ++++ new/clientserver.c +@@ -410,6 +410,9 @@ static int rsync_module(int f_in, int f_ + XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES); - log_init(); -+#if HAVE_LIBODBC + log_init(1); ++#ifdef HAVE_LIBODBC + db_log_open(); +#endif - if (use_chroot) { - /* -@@ -468,6 +471,9 @@ static int rsync_module(int f_in, int f_ + #ifdef HAVE_PUTENV + if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { +@@ -649,6 +652,9 @@ static int rsync_module(int f_in, int f_ rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n", am_sender ? "on" : "to", request, auth_user, host, addr); -+#if HAVE_LIBODBC ++#ifdef HAVE_LIBODBC + db_log_session(); +#endif } else { rprintf(FLOG, "rsync %s %s from %s (%s)\n", am_sender ? "on" : "to", ---- orig/configure.in 2005-05-13 22:24:18 -+++ configure.in 2004-07-03 20:22:18 -@@ -94,6 +94,8 @@ AC_ARG_WITH(rsync-path, - [ --with-rsync-path=PATH set default --rsync-path to PATH (default: rsync)], - [ RSYNC_PATH="$with_rsync_path" ], - [ RSYNC_PATH="rsync" ]) -+AC_ARG_WITH(ODBC, -+ [ --with-ODBC compile in support for ODBC database logging]) - - AC_DEFINE_UNQUOTED(RSYNC_PATH, "$RSYNC_PATH", [location of rsync on remote machine]) - -@@ -535,6 +537,14 @@ then +--- old/configure.in ++++ new/configure.in +@@ -659,6 +659,12 @@ if test x"$with_included_popt" != x"yes" AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes]) fi -+if test x"$with_ODBC" = x"yes" -+then -+ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h) ++AC_ARG_ENABLE(ODBC, AC_HELP_STRING([--enable-ODBC], [compile in support for ODBC database logging]), ++ [ AC_CHECK_HEADERS(sql.h sqlext.h sqltypes.h) + AC_CHECK_LIB(odbc,SQLExecDirect) + EXTRA_OBJECT="$EXTRA_OBJECT dblog.o" -+ AC_SUBST(EXTRA_OBJECT) -+fi ++ AC_SUBST(EXTRA_OBJECT) ]) + AC_MSG_CHECKING([whether to use included libpopt]) - if test x"$with_included_popt" = x"yes" - then ---- orig/dblog-tables-mysql.sql 2004-07-02 21:35:58 -+++ dblog-tables-mysql.sql 2004-07-02 21:35:58 -@@ -0,0 +1,43 @@ + if test x"$with_included_popt" = x"yes"; then + AC_MSG_RESULT($srcdir/popt) +--- old/db_log_error-list.txt ++++ new/db_log_error-list.txt +@@ -0,0 +1,35 @@ ++error type description ++0 not an error. ++1 authentication ++2 file/dir deletion failed ++3 connection closed ++4 read error ++5 multiplexing overflow ++6 unexpected tag ++7 over long v-string received ++8 invalid block length ++9 invalid checksum length ++10 invalid remainder length ++11 failed to write error ++12 attempting to send over-long vstring ++13 temporary filename too long ++14 lseek failed ++15 write failed ++16 rename failed ++17 rsync hack failed ++18 "invalid basis_dir index ++19 fstat failed ++20 is a directory ++21 open file failed ++22 mkstemp failed ++23 close failed ++24 failed verification ++25 IO error, skipping deletion. ++26 directory creation failed ++27 ignoring unsafe symbolic link ++28 symbolic link failed ++29 mknod failed ++30 failed to stat ++31 unlink ++32 failed to open file/directory ++33 open? +--- old/dblog-tables-mysql.sql ++++ new/dblog-tables-mysql.sql +@@ -0,0 +1,64 @@ +drop table transfer; +drop table exit; +drop table session; @@ -185,7 +142,6 @@ This patch adds the following options: + id int auto_increment NOT NULL, + session_id int NOT NULL, + date timestamp NOT NULL, -+ file_path varchar(255) NOT NULL, + file_name varchar(255) NOT NULL, + file_size bigint NOT NULL, + bytes_transferred bigint NOT NULL, @@ -206,12 +162,34 @@ This patch adds the following options: + error_code int NOT NULL, + error_file varchar(64) NOT NULL, + error_line int NOT NULL, ++ process_id int NOT NULL, + Primary Key (id), + foreign key (session_id) references session (id) +); ---- orig/dblog-tables-postgresql.sql 2004-07-02 21:35:58 -+++ dblog-tables-postgresql.sql 2004-07-02 21:35:58 -@@ -0,0 +1,45 @@ ++ ++CREATE TABLE error ( ++ id int auto_increment NOT NULL, ++ session_id int NOT NULL, ++ date timestamp NOT NULL, ++ logcode bigint NOT NULL, ++ error_number bigint NOT NULL, ++ error_text varchar(512), ++ PrimaryKey (id), ++ foreign key (session_id) references session (id) ++); ++ ++CREATE TABLE delete ( ++ id serial NOT NULL, ++ session_id int NOT NULL, ++ date timestamp NOT NULL, ++ path varchar(512) NOT NULL, ++ mode int NOT NULL, ++ PrimaryKey (id), ++ foreign key (session_id) references session (id) ++); +--- old/dblog-tables-postgresql.sql ++++ new/dblog-tables-postgresql.sql +@@ -0,0 +1,67 @@ +drop table transfer; +drop table exit; +drop table session; @@ -233,7 +211,6 @@ This patch adds the following options: + "id" serial NOT NULL, + "session_id" int NOT NULL, + "date" timestamp NOT NULL default now(), -+ "file_path" varchar(512) NOT NULL, + "file_name" varchar(512) NOT NULL, + "file_size" bigint NOT NULL, + "bytes_transferred" bigint NOT NULL, @@ -254,12 +231,35 @@ This patch adds the following options: + "error_code" int NOT NULL, + "error_file" varchar(64) NOT NULL, + "error_line" int NOT NULL, ++ "process_id" int NOT NULL, ++ Primary Key (id), ++ foreign key (session_id) references session (id) ++); ++ ++CREATE TABLE "error" ( ++ "id" serial NOT NULL, ++ "session_id" int NOT NULL, ++ "date" timestamp NOT NULL default now(), ++ "logcode" int NOT NULL, ++ "error_number" int NOT NULL, ++ "error_text" varchar(512), ++ Primary Key (id), ++ foreign key (session_id) references session (id) ++ ++); ++ ++CREATE TABLE "delete" ( ++ "id" serial NOT NULL, ++ "session_id" int NOT NULL, ++ "date" timestamp NOT NULL default now(), ++ "path" varchar(512) NOT NULL, ++ "mode" int NOT NULL, + Primary Key (id), + foreign key (session_id) references session (id) +); ---- orig/dblog.c 2004-07-02 21:35:58 -+++ dblog.c 2004-07-02 21:35:58 -@@ -0,0 +1,352 @@ +--- old/dblog.c ++++ new/dblog.c +@@ -0,0 +1,549 @@ +/* + * ODBC Database logging functions + * @@ -269,37 +269,43 @@ This patch adds the following options: + +#include "rsync.h" + -+#if HAVE_SQL_H ++#ifdef HAVE_SQL_H +#include +#else -+#if HAVE_ODBC_SQL_H ++#ifdef HAVE_ODBC_SQL_H +#include +#endif +#endif + -+#if HAVE_SQLEXT_H ++#ifdef HAVE_SQLEXT_H +#include +#else -+#if HAVE_ODBC_SQLEXT_H ++#ifdef HAVE_ODBC_SQLEXT_H +#include +#endif +#endif + -+#if HAVE_SQLTYPES_H ++#ifdef HAVE_SQLTYPES_H +#include +#else -+#if HAVE_ODBC_SQLTYPES_H ++#ifdef HAVE_ODBC_SQLTYPES_H +#include +#endif +#endif + +SQLHENV db_environ_handle; /* Handle ODBC environment */ +long result; /* result of functions */ -+SQLHDBC db_handle= NULL; /* database connection handle */ -+SQLHSTMT sql_statement_handle; /* SQL statement handle */ ++SQLHDBC db_handle_g = NULL; /* database connection handle for generator*/ ++SQLHDBC db_handle_r = NULL; /* database connection handle for sender */ ++SQLHSTMT sql_statement_handle_g; /* SQL statement handle for generator*/ ++SQLHSTMT sql_statement_handle_r; /* SQL statement handle for receiver*/ ++extern int am_daemon; +extern int am_sender; ++extern int am_generator; +extern char *auth_user; +extern int module_id; ++extern int dry_run; ++ + +char sql_status[10]; /* Status SQL */ +SQLINTEGER V_OD_err, V_OD_rowanz, V_OD_id; @@ -353,103 +359,172 @@ This patch adds the following options: + +void db_log_open(void) +{ -+ if (lp_database_logging(module_id)) { -+ if (db_handle == NULL) { -+ /* get ODBC environment handle */ -+ result = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&db_environ_handle); -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ rprintf(FERROR, "Error: couldn't get database environment handle\n"); -+ return; -+ } ++ if (!lp_database_logging(module_id)) ++ return; + -+ /* Setting database enviroment */ -+ result = SQLSetEnvAttr(db_environ_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ rprintf(FERROR, "Error: couldn't set database environment.\n"); -+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); -+ db_environ_handle = NULL; -+ return; -+ } ++ /* get ODBC environment handle */ ++ result = SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&db_environ_handle); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ rprintf(FERROR, "Error: couldn't get database environment handle\n"); ++ return; ++ } + -+ /* Get a database handle */ -+ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle); -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ rprintf(FERROR, "Error: couldn't allocate database handle\n"); -+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); -+ db_environ_handle = NULL; -+ return; -+ } ++ /* Setting database enviroment */ ++ result = SQLSetEnvAttr(db_environ_handle, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ rprintf(FERROR, "Error: couldn't set database environment.\n"); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; ++ } ++ if (db_handle_g == NULL) { ++ /* Get a database handle for the generator*/ ++ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_g); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ rprintf(FERROR, "Error: couldn't allocate database handle for generator\n"); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; ++ } + -+ /* Set connection attributes */ -+ SQLSetConnectAttr(db_handle, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); ++ /* Set connection attributes for the generator db connection */ ++ SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); + -+ /* Connect to the database. */ -+ result = SQLConnect(db_handle, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS, -+ (SQLCHAR*) lp_database_username(module_id), SQL_NTS, -+ (SQLCHAR*) lp_database_password(module_id), SQL_NTS); ++ /* get the database connection for the generator. */ ++ result = SQLConnect(db_handle_g, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS, ++ (SQLCHAR*) lp_database_username(module_id), SQL_NTS, ++ (SQLCHAR*) lp_database_password(module_id), SQL_NTS); + -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, -+ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen); -+ rprintf(FERROR,"Error Connecting to Database %s\n",V_OD_msg); -+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle); -+ db_handle = NULL; -+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); -+ db_environ_handle = NULL; -+ return; -+ } -+ rprintf(FLOG,"Connected to database!\n"); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g, 1, ++ sql_status, &V_OD_err, V_OD_msg, 100, &V_OD_mlen); ++ rprintf(FERROR,"Error Connecting to Database (generator) %s\n",V_OD_msg); ++ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); ++ db_handle_g = NULL; ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; ++ } ++ rprintf(FLOG,"Connected to database for generator!\n"); ++ } else { ++ rprintf(FERROR,"Already connected to database for generator\n"); ++ } ++ if (db_handle_r == NULL) { ++ /* Get a database handle for the receiver */ ++ result = SQLAllocHandle(SQL_HANDLE_DBC, db_environ_handle, &db_handle_r); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ rprintf(FERROR, "Error: couldn't allocate database handle for receiver\n"); ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; ++ } + -+ /* get SQL statement handle */ -+ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle, &sql_statement_handle); -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); -+ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg); -+ SQLDisconnect(db_handle); -+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle); -+ db_handle = NULL; -+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); -+ db_environ_handle = NULL; -+ return; -+ } -+ } else { -+ rprintf(FERROR,"Already connected to database\n"); ++ /* Set connection attributes for the receiver db connection */ ++ SQLSetConnectAttr(db_handle_r, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); ++ ++ /* get the generator connection for the receiver. */ ++ result = SQLConnect(db_handle_r, (SQLCHAR*) lp_database_datasource(module_id), SQL_NTS, ++ (SQLCHAR*) lp_database_username(module_id), SQL_NTS, ++ (SQLCHAR*) lp_database_password(module_id), SQL_NTS); ++ ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, ++ sql_status, &V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error Connecting to Database (receiver) %s\n",V_OD_msg); ++ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r); ++ db_handle_r = NULL; ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; + } ++ rprintf(FLOG,"Connected to database for receiver!\n"); ++ } else { ++ rprintf(FERROR,"Already connected to database for receiver\n"); ++ } ++ ++ /* get SQL statement handle for generator */ ++ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_g, &sql_statement_handle_g); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_g,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg); ++ SQLDisconnect(db_handle_g); ++ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); ++ db_handle_g = NULL; ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; ++ } ++ ++ /* get SQL statement handle for receiver */ ++ result = SQLAllocHandle(SQL_HANDLE_STMT, db_handle_r, &sql_statement_handle_r); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle_r,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error in allocating SQL statement handle %s\n",V_OD_msg); ++ SQLDisconnect(db_handle_r); ++ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r); ++ db_handle_r = NULL; ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ return; + } +} + +void db_log_close() +{ -+ if (lp_database_logging(module_id)) { -+ if (sql_statement_handle != NULL) { ++ if (!lp_database_logging(module_id)) ++ return; ++ ++ if (am_generator) { ++ if (sql_statement_handle_g != NULL) { + /* free the statement handle first */ -+ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle); -+ sql_statement_handle = NULL; ++ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g); ++ sql_statement_handle_g = NULL; + } else { -+ rprintf(FERROR,"No sql statement handle to close\n"); ++ rprintf(FERROR,"No generator sql statement handle to close\n"); + } -+ if (db_handle != NULL) { ++ ++ if (db_handle_g != NULL) { + /* disconnect, and free the database handle. */ -+ SQLDisconnect(db_handle); -+ SQLFreeHandle(SQL_HANDLE_DBC,db_handle); -+ db_handle = NULL; ++ SQLDisconnect(db_handle_g); ++ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); ++ db_handle_g = NULL; + } else { -+ rprintf(FERROR,"Database already closed"); ++ rprintf(FERROR,"Generator database connection already closed\n"); + } -+ if (db_environ_handle != NULL) { -+ /* free the environment handle */ -+ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); -+ db_environ_handle = NULL; ++ } else { /* must be receiver */ ++ if (sql_statement_handle_r != NULL) { ++ /* free the statement handle first */ ++ SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_r); ++ sql_statement_handle_r = NULL; + } else { -+ rprintf(FERROR,"No environment handle to close"); ++ rprintf(FERROR,"No receiver sql statement handle to close\n"); ++ } ++ ++ if (db_handle_r != NULL) { ++ /* disconnect, and free the database handle. */ ++ SQLDisconnect(db_handle_r); ++ SQLFreeHandle(SQL_HANDLE_DBC,db_handle_r); ++ db_handle_r = NULL; ++ } else { ++ rprintf(FERROR,"Receiver database connection already closed\n"); + } + } ++ ++ if (db_environ_handle != NULL) { ++ /* free the environment handle */ ++ SQLFreeHandle(SQL_HANDLE_ENV, db_environ_handle); ++ db_environ_handle = NULL; ++ } else { ++ rprintf(FERROR,"No environment handle to close\n"); ++ } +} + +static long get_unique_session_id() +{ + long unique; + char strSqlStatement[1024]; ++ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; ++ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; + + if (db_handle != NULL) { + /* choose the appropriate select statement based upon which DBMS we're using. @@ -457,17 +532,23 @@ This patch adds the following options: + * object (sequence), others use an auto increment datatype and have a method + * to get the last ID inserted using this connection. */ + if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0) { -+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT NEXTVAL('%s');",lp_sequence_name(module_id)); ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "SELECT NEXTVAL('%s');", lp_sequence_name(module_id)); + } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) { -+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT %s.NEXTVAL FROM dual;",lp_sequence_name(module_id)); ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id)); + } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) { -+ snprintf(strSqlStatement,sizeof(strSqlStatement),"VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id)); -+ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */ -+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT LAST_INSERT_ID()"); -+ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */ -+ snprintf(strSqlStatement,sizeof(strSqlStatement),"SELECT @@IDENTITY"); -+ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */ -+ snprintf(strSqlStatement,sizeof(strSqlStatement),lp_custom_unique_id_select(module_id)); ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "VALUES NEXTVAL FOR %s;",lp_sequence_name(module_id)); ++ } else if (strcmp(lp_unique_id_method(module_id),"last_insert_id") == 0) { /* MySql */ ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "SELECT LAST_INSERT_ID()"); ++ } else if (strcmp(lp_unique_id_method(module_id),"@@IDENTITY") == 0) { /* Sybase */ ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "SELECT @@IDENTITY"); ++ } else if (strcmp(lp_unique_id_method(module_id),"custom") == 0){ /* Users custom statement */ ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ lp_custom_unique_id_select(module_id)); + } + + /* bind the 1st column to unique */ @@ -498,25 +579,38 @@ This patch adds the following options: + return -1; +} + -+ +void db_log_session() +{ + char strSqlStatement[1024]; + int gotSessionID = 0; -+ if (lp_database_logging(module_id)) { -+ /* if we're using a sequence via the nextval command to get a unique ID, we need to get it before -+ * we do the insert. We also get the unique ID now if custom, and get_custom_id_before_insert is set. */ ++ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; ++ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; ++ ++ if (!lp_database_logging(module_id)) ++ return; ++ ++ if (db_handle != NULL) { ++ /* if we're using a sequence via the nextval command to ++ * get a unique ID, we need to get it before we do the ++ * insert. We also get the unique ID now if custom, ++ * and get_custom_id_before_insert is set. */ + if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0 -+ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0 -+ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0 -+ || (strcmp(lp_unique_id_method(module_id),"custom") == 0 -+ && lp_get_custom_id_before_insert(module_id))) { ++ || strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0 ++ || strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0 ++ || (strcmp(lp_unique_id_method(module_id),"custom") == 0 ++ && lp_get_custom_id_before_insert(module_id))) { + session_id = get_unique_session_id(); + gotSessionID = 1; -+ 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()); ++ 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()); + } else { + /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */ -+ 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()); ++ 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()); + } + + /* Insert the new session into the database */ @@ -536,8 +630,7 @@ This patch adds the following options: + if (gotSessionID == 0) { + session_id = get_unique_session_id(); + } -+ } -+ else { ++ } else { + rprintf(FERROR,"Error at db_log_session: Not connected to database!\n"); + } +} @@ -545,150 +638,424 @@ This patch adds the following options: +void db_log_transfer(struct file_struct *file,struct stats *initial_stats,char *operation) +{ + extern struct stats stats; -+ char strSqlStatement[1024]; -+ char strFilePath[255]; -+ char strFileName[255]; ++ char strSqlStatement[2048]; ++ char strFileName[MAXPATHLEN]; ++ char *strFileNamePtr; + char strFileSize[255]; + int64 intBytesTransferred; + int64 intCheckSumBytes; ++ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; ++ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; + -+ if (lp_database_logging(module_id)) { -+ if (db_handle != NULL) { -+ snprintf(strFileName,sizeof(strFileName), "%s",f_name(file)); -+ snprintf(strFilePath, sizeof(strFilePath), "%s", file->basedir?file->basedir:""); -+ snprintf(strFileSize,sizeof(strFileSize),"%.0f", (double)file->length); -+ if (am_sender) { -+ intBytesTransferred = stats.total_written - initial_stats->total_written; -+ } else { -+ intBytesTransferred = stats.total_read - initial_stats->total_read; -+ } ++ if (!lp_database_logging(module_id)) ++ return; + -+ if (!am_sender) { -+ intCheckSumBytes = stats.total_written - initial_stats->total_written; -+ } else { -+ intCheckSumBytes = stats.total_read - initial_stats->total_read; -+ } ++ if (db_handle != NULL) { ++ strFileNamePtr = f_name(file, NULL); ++ if (am_sender && file->dir.root) { ++ pathjoin(strFileName, sizeof strFileName, ++ file->dir.root, strFileNamePtr); ++ strFileNamePtr = strFileName; ++ } ++ clean_fname(strFileNamePtr, 0); ++ if (*strFileNamePtr == '/') ++ strFileNamePtr++; + -+ 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); -+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); -+ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); -+ if (result == SQL_INVALID_HANDLE) -+ rprintf(FERROR,"INVALID HANDLE\n"); -+ } ++ snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)F_LENGTH(file)); ++ if (am_sender) { ++ intBytesTransferred = stats.total_written - initial_stats->total_written; + } else { -+ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n"); ++ intBytesTransferred = stats.total_read - initial_stats->total_read; ++ } ++ ++ if (!am_sender) { ++ intCheckSumBytes = stats.total_written - initial_stats->total_written; ++ } else { ++ intCheckSumBytes = stats.total_read - initial_stats->total_read; ++ } ++ ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "INSERT INTO %s (session_id,date, file_name, file_size, bytes_transferred, checksum_bytes_transferred, operation) VALUES ('%ld','%s','%s','%s','%Ld','%Ld','%s');", ++ lp_transfer_table_name(module_id), session_id, timestring(time(NULL)), ++ sanitizeSql(strFileNamePtr), strFileSize, intBytesTransferred, ++ intCheckSumBytes, operation); ++ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error at db_log_transfer: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); ++ if (result == SQL_INVALID_HANDLE) ++ rprintf(FERROR,"INVALID HANDLE\n"); + } ++ } else { ++ rprintf(FERROR,"Error at db_log_transfer: Not connected to database!\n"); + } +} + -+ +void db_log_exit(int code, const char *file, int line) +{ + char strSqlStatement[2048]; + const char *error_text; + extern struct stats stats; ++ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; ++ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; ++ ++ if (!lp_database_logging(module_id)) ++ return; ++ + if (db_handle != NULL) { -+ if (lp_database_logging(module_id)) { -+ if (code != 0) { -+ error_text = rerr_name(code); -+ if (!error_text) { -+ error_text = "unexplained error"; -+ } -+ } else { -+ error_text = ""; ++ if (code != 0) { ++ error_text = rerr_name(code); ++ if (!error_text) { ++ error_text = "unexplained error"; + } -+ 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); ++ } else { ++ error_text = ""; ++ } ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "INSERT INTO %s (session_id, date, total_bytes_written,total_bytes_read,total_size,error_text,error_code,error_file,error_line,process_id) VALUES ('%ld','%s','%Ld','%Ld','%Ld','%s','%d','%s','%d','%d');", ++ lp_exit_table_name(module_id), session_id, timestring(time(NULL)), stats.total_written, ++ stats.total_read, stats.total_size, error_text, code, file, line, getpid()); + -+ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); ++ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); + -+ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { -+ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); -+ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); -+ } ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error at db_log_exit: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); + } + } else { + rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n"); + } +} ---- orig/loadparm.c 2005-02-19 17:38:51 -+++ loadparm.c 2004-07-03 20:22:18 -@@ -125,6 +125,17 @@ typedef struct - BOOL list; - BOOL use_chroot; - BOOL transfer_logging; -+ BOOL database_logging; ++ ++void db_log_delete(char *fname, int mode) ++{ ++ char strSqlStatement[2048]; ++ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; ++ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; ++ ++ if (!am_daemon || dry_run || !lp_database_logging(module_id)) ++ return; ++ ++ if (db_handle != NULL) { ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "INSERT INTO %s (session_id, date, path, mode) VALUES ('%ld','%s','%s','%d');", ++ lp_delete_table_name(module_id), session_id, timestring(time(NULL)), sanitizeSql(fname), mode); ++ ++ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); ++ ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error at db_log_delete: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); ++ } ++ } else { ++ rprintf(FERROR,"Error at db_log_delete: Not connected to database!\n"); ++ } ++} ++ ++void db_log_error(enum logcode code, int errcode, const char *format,...) ++{ ++ char strSqlStatement[MAXPATHLEN+1024]; ++ va_list ap; ++ char buf[MAXPATHLEN+512]; ++ size_t len; ++ SQLHDBC db_handle = (am_generator) ? db_handle_g : db_handle_r; ++ SQLHSTMT sql_statement_handle = (am_generator) ? sql_statement_handle_g : sql_statement_handle_r; ++ ++ if (!lp_database_logging(module_id)) ++ return; ++ ++ va_start(ap, format); ++ len = vsnprintf(buf, sizeof buf, format, ap); ++ va_end(ap); ++ ++ /* Deal with buffer overruns. Instead of panicking, just ++ * truncate the resulting string. (Note that configure ensures ++ * that we have a vsnprintf() that doesn't ever return -1.) */ ++ if (len > sizeof buf - 1) { ++ const char ellipsis[] = "[...]"; ++ ++ /* Reset length, and zero-terminate the end of our buffer */ ++ len = sizeof buf - 1; ++ buf[len] = '\0'; ++ ++ /* Copy the ellipsis to the end of the string, but give ++ * us one extra character: ++ * ++ * v--- null byte at buf[sizeof buf - 1] ++ * abcdefghij0 ++ * -> abcd[...]00 <-- now two null bytes at end ++ * ++ * If the input format string has a trailing newline, ++ * we copy it into that extra null; if it doesn't, well, ++ * all we lose is one byte. */ ++ strncpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis); ++ if (format[strlen(format)-1] == '\n') { ++ buf[len-1] = '\n'; ++ } ++ } ++ ++ if (db_handle != NULL) { ++ snprintf(strSqlStatement, sizeof strSqlStatement, ++ "INSERT INTO %s (session_id, date, logcode, error_number, error_text) VALUES ('%ld','%s','%d','%d','%s');", ++ lp_error_table_name(module_id), session_id, timestring(time(NULL)), code, errcode, sanitizeSql(buf)); ++ ++ result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); ++ ++ if (result != SQL_SUCCESS && result != SQL_SUCCESS_WITH_INFO) { ++ SQLGetDiagRec(SQL_HANDLE_DBC, db_handle,1, sql_status,&V_OD_err,V_OD_msg,100,&V_OD_mlen); ++ rprintf(FERROR,"Error at db_log_error: Error in Insert %s %s\n",strSqlStatement,V_OD_msg); ++ } ++ } else { ++ rprintf(FERROR,"Error at db_log_error: Not connected to database!\n"); ++ } ++} +--- old/instructions ++++ new/instructions +@@ -0,0 +1,84 @@ ++This patch adds the following options: ++ ++"database logging" ++ If set to True, rsync will attempt to connect to ++ the specified datasource and write to the named tables. ++ Defaults to False. ++ ++"database datasource" ++ Specifies the name of the ODBC data source to use. ++ ++"database username" ++ The username to use when connecting to the database. ++ ++"database password" ++ The password to use when connecting to the database. ++ ++"transfer table name" ++ The name of the transfer table to log to. This table contains individual ++ filenames, file sizes, bytes transferred, checksum bytes transferred, ++ operation (send or receive), and a timestamp. ++ ++"session table name" ++ The name of the session table to log to. This table contains the username, ++ module name, module path, ip address, process ID, and a timestamp. ++ ++"exit table name" ++ ++ The name of the exit table to log to. This table contains the total bytes ++ read, total bytes written, total size of all files, error code, line the ++ error occured at, file the error occured at and the text of the error. ++ (most of which will be blank if the program exited normally). ++ ++"delete table name" ++ ++ The name of the table to log deleted files/directories to. ++ ++"error table name" ++ ++ The name of the table errors will be logged to. ++ ++"unique id method" ++ Different databases use different methods to get a unique identifier. ++ Some databases support sequence objects, and use various forms of the ++ nextval command to retrieve a unique identifier from it. Other databases ++ support an autonumber field, and support different methds of retrieving ++ the ID used in the last insert. Valid values for this option are: ++ ++ nextval-postgres ++ uses the syntax of nextval for PostgreSQL databases ++ ++ nextval-oracle ++ uses the syntax of nextval for Oracle databases ++ ++ nextval-db2 ++ uses the syntax of nextval for DB2 databases ++ ++ last_insert_id ++ uses the last_insert_id() command for the MySQL databases ++ ++ @@IDENTITY ++ uses the @@IDENTITY command for Sybase databases ++ ++ custom ++ Define your own method to get a unique identifier. See the ++ "custom unique id select", and the "get custom id before select" ++ parameters. ++ ++"sequence name" ++ If your database supports sequences, list the name of the sequence to use ++ for the session unique identifier. ++ ++"custom unique id select" ++ Only used if you specify the custom method in "unique id method". This is ++ a SQL statement to be executed to get a unique ID. This SQL statement must ++ return one column with the unique ID to use for the session ID. Should be ++ used in concert with the "get custom id before select" parameter. ++ ++"get custom id before insert" ++ This parameter is ignored unless the "unique id method" is set to custom. ++ If set to true, the "custom unique id select" statement will be executed ++ BEFORE the session row is inserted into the database. (as is done when a ++ sequence is used for unique IDs). If False the statement will be executed ++ after the session row is inserted (as is done when the session ID is ++ automatically generates unique IDs). Defaults to True. +--- old/loadparm.c ++++ new/loadparm.c +@@ -124,9 +124,16 @@ typedef struct + { + char *auth_users; + char *comment; ++ char *custom_unique_id_select; + char *database_datasource; -+ char *database_username; + char *database_password; -+ char *transfer_table_name; ++ char *database_username; ++ char *delete_table_name; + char *dont_compress; ++ char *error_table_name; + char *exclude; + char *exclude_from; + char *exit_table_name; -+ char *session_table_name; + char *filter; + char *gid; + char *hosts_allow; +@@ -144,15 +151,21 @@ typedef struct + char *prexfer_exec; + char *refuse_options; + char *secrets_file; + char *sequence_name; ++ char *session_table_name; + char *temp_dir; ++ char *transfer_table_name; + char *uid; + char *unique_id_method; -+ char *custom_unique_id_select; + + int max_connections; + int max_verbosity; + int syslog_facility; + int timeout; + ++ BOOL database_logging; + BOOL fake_super; + BOOL get_custom_id_before_insert; BOOL ignore_errors; - char *uid; - char *gid; -@@ -159,6 +170,17 @@ static service sDefault = - True, /* list */ - True, /* use chroot */ - False, /* transfer logging */ -+ False, /* Database Logging */ -+ NULL, /* Database datasource */ -+ NULL, /* Database username */ -+ NULL, /* Database password */ -+ NULL, /* Transfer table name */ -+ NULL, /* Exit table name */ -+ NULL, /* Session table name */ -+ NULL, /* sequence name */ -+ NULL, /* unique method */ -+ NULL, /* custom unique id select*/ -+ True, /* get custom id before insert */ - False, /* ignore errors */ - "nobody",/* uid */ + BOOL ignore_nonreadable; + BOOL list; +@@ -172,9 +185,16 @@ static service sDefault = + { + /* auth_users; */ NULL, + /* comment; */ NULL, ++ /* custom_unique_id_select; */ NULL, ++ /* database_datasource; */ NULL, ++ /* database_password; */ NULL, ++ /* database_username; */ NULL, ++ /* delete_table_name; */ NULL, + /* dont_compress; */ DEFAULT_DONT_COMPRESS, ++ /* error_table_name; */ NULL, + /* exclude; */ NULL, + /* exclude_from; */ NULL, ++ /* exit_table_name; */ NULL, + /* filter; */ NULL, + /* gid; */ NOBODY_GROUP, + /* hosts_allow; */ NULL, +@@ -192,15 +212,21 @@ static service sDefault = + /* prexfer_exec; */ NULL, + /* refuse_options; */ NULL, + /* secrets_file; */ NULL, ++ /* sequence_name; */ NULL, ++ /* session_table_name; */ NULL, + /* temp_dir; */ NULL, ++ /* transfer_table_name; */ NULL, + /* uid; */ NOBODY_USER, ++ /* unique_id_method; */ NULL, -@@ -302,6 +324,17 @@ static struct parm_struct parm_table[] = - {"include", P_STRING, P_LOCAL, &sDefault.include, NULL, 0}, - {"include from", P_STRING, P_LOCAL, &sDefault.include_from,NULL, 0}, - {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging,NULL,0}, -+ {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging,NULL,0}, -+ {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0}, -+ {"database username",P_STRING, P_LOCAL, &sDefault.database_username,NULL,0}, -+ {"database password",P_STRING, P_LOCAL, &sDefault.database_password,NULL,0}, -+ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0}, -+ {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name,NULL,0}, -+ {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0}, -+ {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name,NULL,0}, -+ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method,NULL,0}, -+ {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0}, -+ {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0}, - {"ignore errors", P_BOOL, P_LOCAL, &sDefault.ignore_errors,NULL,0}, - {"log format", P_STRING, P_LOCAL, &sDefault.log_format, NULL, 0}, - {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options,NULL, 0}, -@@ -372,6 +405,17 @@ FN_LOCAL_BOOL(lp_write_only, write_only) - FN_LOCAL_BOOL(lp_list, list) - FN_LOCAL_BOOL(lp_use_chroot, use_chroot) - FN_LOCAL_BOOL(lp_transfer_logging, transfer_logging) -+FN_LOCAL_BOOL(lp_database_logging, database_logging) + /* max_connections; */ 0, + /* max_verbosity; */ 1, + /* syslog_facility; */ LOG_DAEMON, + /* timeout; */ 0, + ++ /* database_logging; */ False, + /* fake_super; */ False, ++ /* get_custom_id_before_insert; */ True, + /* ignore_errors; */ False, + /* ignore_nonreadable; */ False, + /* list; */ True, +@@ -299,11 +325,20 @@ static struct parm_struct parm_table[] = + + {"auth users", P_STRING, P_LOCAL, &sDefault.auth_users, NULL,0}, + {"comment", P_STRING, P_LOCAL, &sDefault.comment, NULL,0}, ++ {"custom unique id select",P_STRING,P_LOCAL,&sDefault.custom_unique_id_select,NULL,0}, ++ {"database datasource",P_STRING,P_LOCAL, &sDefault.database_datasource,NULL,0}, ++ {"database logging", P_BOOL, P_LOCAL, &sDefault.database_logging, NULL,0}, ++ {"database password", P_STRING, P_LOCAL, &sDefault.database_password, NULL,0}, ++ {"database username", P_STRING, P_LOCAL, &sDefault.database_username, NULL,0}, ++ {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0}, + {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, ++ {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0}, + {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0}, + {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0}, ++ {"exit table name", P_STRING, P_LOCAL, &sDefault.exit_table_name, NULL,0}, + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0}, + {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0}, ++ {"get custom id before insert",P_BOOL,P_LOCAL,&sDefault.get_custom_id_before_insert,NULL,0}, + {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0}, + {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0}, + {"hosts deny", P_STRING, P_LOCAL, &sDefault.hosts_deny, NULL,0}, +@@ -328,12 +363,16 @@ static struct parm_struct parm_table[] = + {"read only", P_BOOL, P_LOCAL, &sDefault.read_only, NULL,0}, + {"refuse options", P_STRING, P_LOCAL, &sDefault.refuse_options, NULL,0}, + {"secrets file", P_STRING, P_LOCAL, &sDefault.secrets_file, NULL,0}, ++ {"sequence name", P_STRING, P_LOCAL, &sDefault.sequence_name, NULL,0}, ++ {"session table name",P_STRING, P_LOCAL, &sDefault.session_table_name,NULL,0}, + {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0}, + {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0}, + {"temp dir", P_PATH, P_LOCAL, &sDefault.temp_dir, NULL,0}, + {"timeout", P_INTEGER,P_LOCAL, &sDefault.timeout, NULL,0}, + {"transfer logging", P_BOOL, P_LOCAL, &sDefault.transfer_logging, NULL,0}, ++ {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0}, + {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0}, ++ {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0}, + {"use chroot", P_BOOL, P_LOCAL, &sDefault.use_chroot, NULL,0}, + {"write only", P_BOOL, P_LOCAL, &sDefault.write_only, NULL,0}, + {NULL, P_BOOL, P_NONE, NULL, NULL,0} +@@ -389,9 +428,16 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Global + + FN_LOCAL_STRING(lp_auth_users, auth_users) + FN_LOCAL_STRING(lp_comment, comment) ++FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select) +FN_LOCAL_STRING(lp_database_datasource, database_datasource) -+FN_LOCAL_STRING(lp_database_username, database_username) +FN_LOCAL_STRING(lp_database_password, database_password) -+FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name) ++FN_LOCAL_STRING(lp_database_username, database_username) ++FN_LOCAL_STRING(lp_delete_table_name,delete_table_name) + FN_LOCAL_STRING(lp_dont_compress, dont_compress) ++FN_LOCAL_STRING(lp_error_table_name,error_table_name) + FN_LOCAL_STRING(lp_exclude, exclude) + FN_LOCAL_STRING(lp_exclude_from, exclude_from) +FN_LOCAL_STRING(lp_exit_table_name, exit_table_name) -+FN_LOCAL_STRING(lp_session_table_name,session_table_name) + FN_LOCAL_STRING(lp_filter, filter) + FN_LOCAL_STRING(lp_gid, gid) + FN_LOCAL_STRING(lp_hosts_allow, hosts_allow) +@@ -409,15 +455,21 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf + FN_LOCAL_STRING(lp_prexfer_exec, prexfer_exec) + FN_LOCAL_STRING(lp_refuse_options, refuse_options) + FN_LOCAL_STRING(lp_secrets_file, secrets_file) +FN_LOCAL_STRING(lp_sequence_name,sequence_name) ++FN_LOCAL_STRING(lp_session_table_name,session_table_name) + FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility) + FN_LOCAL_STRING(lp_temp_dir, temp_dir) ++FN_LOCAL_STRING(lp_transfer_table_name, transfer_table_name) + FN_LOCAL_STRING(lp_uid, uid) +FN_LOCAL_STRING(lp_unique_id_method,unique_id_method) -+FN_LOCAL_STRING(lp_custom_unique_id_select,custom_unique_id_select) + + FN_LOCAL_INTEGER(lp_max_connections, max_connections) + FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) + FN_LOCAL_INTEGER(lp_timeout, timeout) + ++FN_LOCAL_BOOL(lp_database_logging, database_logging) + FN_LOCAL_BOOL(lp_fake_super, fake_super) +FN_LOCAL_BOOL(lp_get_custom_id_before_insert,get_custom_id_before_insert) FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) - FN_LOCAL_STRING(lp_uid, uid) ---- orig/log.c 2005-04-15 07:08:03 -+++ log.c 2004-07-03 20:22:18 -@@ -84,7 +84,7 @@ struct { + FN_LOCAL_BOOL(lp_list, list) +--- old/log.c ++++ new/log.c +@@ -96,7 +96,7 @@ struct { /* * Map from rsync error code to name, or return NULL. */ @@ -697,37 +1064,168 @@ This patch adds the following options: { int i; for (i = 0; rerr_names[i].name; i++) { ---- orig/main.c 2005-05-12 07:51:58 -+++ main.c 2004-07-03 20:22:18 -@@ -144,6 +144,9 @@ static void handle_stats(int f) +--- old/receiver.c ++++ new/receiver.c +@@ -111,6 +111,10 @@ int get_tmpname(char *fnametmp, char *fn - if (am_daemon) { - log_exit(0, __FILE__, __LINE__); -+#if HAVE_LIBODBC -+ db_log_exit(0,__FILE__,__LINE__); + if (maxname < 1) { + rprintf(FERROR, "temporary filename too long: %s\n", fname); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR,13, "temporary filename too long: %s\n", ++ fname); ++#endif + fnametmp[0] = '\0'; + return 0; + } +@@ -176,6 +180,10 @@ static int receive_data(int f_in, char * + if (fd != -1 && (j = do_lseek(fd, offset, SEEK_SET)) != offset) { + rsyserr(FERROR, errno, "lseek of %s returned %.0f, not %.0f", + full_fname(fname), (double)j, (double)offset); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 14, "lseek failed on %s", ++ full_fname(fname)); ++#endif + exit_cleanup(RERR_FILEIO); + } + } +@@ -233,6 +241,11 @@ static int receive_data(int f_in, char * + "lseek of %s returned %.0f, not %.0f", + full_fname(fname), + (double)pos, (double)offset); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 14, ++ "lseek failed on %s", ++ full_fname(fname)); ++#endif + exit_cleanup(RERR_FILEIO); + } + continue; +@@ -258,6 +271,9 @@ static int receive_data(int f_in, char * + report_write_error: + rsyserr(FERROR, errno, "write failed on %s", + full_fname(fname)); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 15, "write failed on %s",full_fname(fname)); +#endif - if (f == -1 || !am_sender) - return; + exit_cleanup(RERR_FILEIO); } ---- orig/receiver.c 2005-04-14 01:53:12 -+++ receiver.c 2005-03-05 00:31:00 -@@ -640,6 +640,9 @@ int recv_files(int f_in, struct file_lis - if (!log_before_transfer) - log_item(file, &initial_stats, iflags, NULL); -+#if HAVE_LIBODBC +@@ -301,6 +317,12 @@ static void handle_delayed_updates(char + rsyserr(FERROR, errno, + "rename failed for %s (from %s)", + full_fname(fname), partialptr); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 16, ++ "rename failed for %s (from %s)", ++ full_fname(fname), ++ partialptr); ++#endif + } else { + if (remove_source_files + || (preserve_hard_links && F_IS_HLINKED(file))) +@@ -454,6 +476,9 @@ int recv_files(int f_in, char *local_nam + if (server_filter_list.head + && check_filter(&server_filter_list, fname, 0) < 0) { + rprintf(FERROR, "attempt to hack rsync failed.\n"); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR,17,"attempt to hack rsync failed."); ++#endif + exit_cleanup(RERR_PROTOCOL); + } + +@@ -512,6 +537,11 @@ int recv_files(int f_in, char *local_nam + rprintf(FERROR, + "invalid basis_dir index: %d.\n", + fnamecmp_type); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 18, ++ "invalid basis_dir index: %d.\n", ++ fnamecmp_type); ++#endif + exit_cleanup(RERR_PROTOCOL); + } + pathjoin(fnamecmpbuf, sizeof fnamecmpbuf, +@@ -560,6 +590,9 @@ int recv_files(int f_in, char *local_nam + } else if (do_fstat(fd1,&st) != 0) { + rsyserr(FERROR, errno, "fstat %s failed", + full_fname(fnamecmp)); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp)); ++#endif + discard_receive_data(f_in, F_LENGTH(file)); + close(fd1); + if (inc_recurse) +@@ -575,6 +608,9 @@ int recv_files(int f_in, char *local_nam + */ + rprintf(FERROR,"recv_files: %s is a directory\n", + full_fname(fnamecmp)); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp)); ++#endif + discard_receive_data(f_in, F_LENGTH(file)); + close(fd1); + if (inc_recurse) +@@ -609,6 +645,9 @@ int recv_files(int f_in, char *local_nam + if (fd2 == -1) { + rsyserr(FERROR, errno, "open %s failed", + full_fname(fname)); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR,22, "open %s failed", full_fname(fname)); ++#endif + discard_receive_data(f_in, F_LENGTH(file)); + if (fd1 != -1) + close(fd1); +@@ -646,6 +685,10 @@ int recv_files(int f_in, char *local_nam + if (fd2 == -1) { + rsyserr(FERROR, errno, "mkstemp %s failed", + full_fname(fnametmp)); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 22, "mkstemp %s failed", ++ full_fname(fnametmp)); ++#endif + discard_receive_data(f_in, F_LENGTH(file)); + if (fd1 != -1) + close(fd1); +@@ -668,12 +711,19 @@ int recv_files(int f_in, char *local_nam + fname, fd2, F_LENGTH(file)); + + log_item(log_code, file, &initial_stats, iflags, NULL); ++#ifdef HAVE_LIBODBC + db_log_transfer(file, &initial_stats, "receive"); +#endif if (fd1 != -1) close(fd1); ---- orig/sender.c 2005-05-19 08:52:42 -+++ sender.c 2005-03-05 00:31:19 -@@ -347,6 +347,9 @@ void send_files(struct file_list *flist, + if (close(fd2) < 0) { + rsyserr(FERROR, errno, "close failed on %s", + full_fname(fnametmp)); ++#ifdef HAVE_LIBODBC ++ db_log_error(FERROR, 23, "close failed on %s", ++ full_fname(fnametmp)); ++#endif + exit_cleanup(RERR_FILEIO); + } + +@@ -730,6 +780,12 @@ int recv_files(int f_in, char *local_nam + rprintf(msgtype, + "%s: %s failed verification -- update %s%s.\n", + errstr, fname, keptstr, redostr); ++#ifdef HAVE_LIBODBC ++ db_log_error(msgtype,24, ++ "%s: %s failed verification -- update %s%s.\n", ++ errstr, fname, ++ keptstr, redostr); ++#endif + } + if (!redoing) { + send_msg_int(MSG_REDO, ndx); +--- old/sender.c ++++ new/sender.c +@@ -344,6 +344,9 @@ void send_files(int f_in, int f_out) + end_progress(st.st_size); - if (!log_before_transfer) - log_item(file, &initial_stats, iflags, NULL); -+#if HAVE_LIBODBC + log_item(log_code, file, &initial_stats, iflags, NULL); ++#ifdef HAVE_LIBODBC + db_log_transfer(file, &initial_stats,"send"); +#endif