Commit | Line | Data |
---|---|---|
2c40f06f WD |
1 | Add support for logging daemon messages to an SQL database. |
2 | ||
03019e41 | 3 | To use this patch, run these commands for a successful build: |
2c40f06f | 4 | |
03019e41 | 5 | patch -p1 <patches/ODBC-dblog.diff |
27e96866 | 6 | ./prepare-source |
a9eff38d | 7 | ./configure --enable-ODBC |
8a529471 | 8 | make |
2c40f06f | 9 | |
03019e41 | 10 | See the newly-created file "instructions" for more info. |
8a529471 | 11 | |
9a7eef96 WD |
12 | --- old/Makefile.in |
13 | +++ new/Makefile.in | |
60a8bf36 | 14 | @@ -32,7 +32,7 @@ LIBOBJ=lib/wildmatch.o lib/compat.o lib/ |
1680e814 WD |
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 | |
590329e5 WD |
17 | OBJS1=flist.o rsync.o generator.o receiver.o cleanup.o sender.o exclude.o \ |
18 | - util.o main.o checksum.o match.o syscall.o log.o backup.o | |
19 | + util.o main.o checksum.o match.o syscall.o log.o backup.o @EXTRA_OBJECT@ | |
20 | OBJS2=options.o io.o compat.o hlink.o token.o uidlist.o socket.o \ | |
6849cd84 | 21 | fileio.o batch.o clientname.o chmod.o |
103bcb1d | 22 | OBJS3=progress.o pipe.o |
9a7eef96 WD |
23 | --- old/cleanup.c |
24 | +++ new/cleanup.c | |
55c1a3b7 WD |
25 | @@ -27,6 +27,7 @@ extern int am_server; |
26 | extern int am_daemon; | |
a9eff38d WD |
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; | |
55c1a3b7 | 32 | extern char *logfile_name; |
03019e41 | 33 | @@ -174,8 +175,13 @@ NORETURN void _exit_cleanup(int code, co |
b3ea4757 WD |
34 | code = exit_code = RERR_PARTIAL; |
35 | } | |
2c40f06f | 36 | |
b3ea4757 WD |
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); | |
637ba3ff | 40 | +#ifdef HAVE_LIBODBC |
b3ea4757 WD |
41 | + db_log_exit(code, file, line); |
42 | + db_log_close(); | |
2c40f06f | 43 | +#endif |
b3ea4757 | 44 | + } |
2c40f06f | 45 | |
b3ea4757 | 46 | /* FALLTHROUGH */ |
03019e41 | 47 | #include "case_N.h" |
9a7eef96 WD |
48 | --- old/clientserver.c |
49 | +++ new/clientserver.c | |
f2376a08 | 50 | @@ -397,6 +397,9 @@ static int rsync_module(int f_in, int f_ |
6849cd84 | 51 | XFLG_ABS_IF_SLASH | XFLG_OLD_PREFIXES); |
2c40f06f | 52 | |
0ffcefad | 53 | log_init(1); |
637ba3ff | 54 | +#ifdef HAVE_LIBODBC |
2c40f06f WD |
55 | + db_log_open(); |
56 | +#endif | |
57 | ||
ff55cce0 | 58 | #ifdef HAVE_PUTENV |
6849cd84 | 59 | if (*lp_prexfer_exec(i) || *lp_postxfer_exec(i)) { |
f2376a08 | 60 | @@ -638,6 +641,9 @@ static int rsync_module(int f_in, int f_ |
545864f1 WD |
61 | rprintf(FLOG, "rsync %s %s from %s@%s (%s)\n", |
62 | am_sender ? "on" : "to", | |
2c40f06f | 63 | request, auth_user, host, addr); |
637ba3ff | 64 | +#ifdef HAVE_LIBODBC |
2c40f06f WD |
65 | + db_log_session(); |
66 | +#endif | |
67 | } else { | |
545864f1 WD |
68 | rprintf(FLOG, "rsync %s %s from %s (%s)\n", |
69 | am_sender ? "on" : "to", | |
9a7eef96 WD |
70 | --- old/configure.in |
71 | +++ new/configure.in | |
f2376a08 | 72 | @@ -637,6 +637,12 @@ if test x"$with_included_popt" != x"yes" |
2c40f06f | 73 | AC_CHECK_LIB(popt, poptGetContext, , [with_included_popt=yes]) |
545864f1 WD |
74 | fi |
75 | ||
a9eff38d WD |
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) | |
2c40f06f WD |
78 | + AC_CHECK_LIB(odbc,SQLExecDirect) |
79 | + EXTRA_OBJECT="$EXTRA_OBJECT dblog.o" | |
a9eff38d | 80 | + AC_SUBST(EXTRA_OBJECT) ]) |
545864f1 | 81 | + |
2c40f06f | 82 | AC_MSG_CHECKING([whether to use included libpopt]) |
93ca4d27 WD |
83 | if test x"$with_included_popt" = x"yes"; then |
84 | AC_MSG_RESULT($srcdir/popt) | |
9a7eef96 WD |
85 | --- old/db_log_error-list.txt |
86 | +++ new/db_log_error-list.txt | |
a9eff38d WD |
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? | |
9a7eef96 WD |
123 | --- old/dblog-tables-mysql.sql |
124 | +++ new/dblog-tables-mysql.sql | |
a9eff38d | 125 | @@ -0,0 +1,64 @@ |
103bcb1d WD |
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, | |
103bcb1d WD |
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, | |
637ba3ff | 165 | + process_id int NOT NULL, |
103bcb1d WD |
166 | + Primary Key (id), |
167 | + foreign key (session_id) references session (id) | |
168 | +); | |
a9eff38d WD |
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 | +); | |
9a7eef96 WD |
190 | --- old/dblog-tables-postgresql.sql |
191 | +++ new/dblog-tables-postgresql.sql | |
a9eff38d | 192 | @@ -0,0 +1,67 @@ |
103bcb1d WD |
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(), | |
103bcb1d WD |
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, | |
637ba3ff | 234 | + "process_id" int NOT NULL, |
103bcb1d WD |
235 | + Primary Key (id), |
236 | + foreign key (session_id) references session (id) | |
237 | +); | |
a9eff38d WD |
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 | +); | |
9a7eef96 WD |
260 | --- old/dblog.c |
261 | +++ new/dblog.c | |
a9eff38d | 262 | @@ -0,0 +1,549 @@ |
2c40f06f WD |
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 | + | |
637ba3ff | 272 | +#ifdef HAVE_SQL_H |
2c40f06f WD |
273 | +#include <sql.h> |
274 | +#else | |
637ba3ff | 275 | +#ifdef HAVE_ODBC_SQL_H |
2c40f06f WD |
276 | +#include <odbc/sql.h> |
277 | +#endif | |
278 | +#endif | |
279 | + | |
637ba3ff | 280 | +#ifdef HAVE_SQLEXT_H |
2c40f06f WD |
281 | +#include <sqlext.h> |
282 | +#else | |
637ba3ff | 283 | +#ifdef HAVE_ODBC_SQLEXT_H |
2c40f06f WD |
284 | +#include <odbc/sqlext.h> |
285 | +#endif | |
286 | +#endif | |
287 | + | |
637ba3ff | 288 | +#ifdef HAVE_SQLTYPES_H |
2c40f06f WD |
289 | +#include <sqltypes.h> |
290 | +#else | |
637ba3ff | 291 | +#ifdef HAVE_ODBC_SQLTYPES_H |
2c40f06f WD |
292 | +#include <odbc/sqltypes.h> |
293 | +#endif | |
294 | +#endif | |
295 | + | |
296 | +SQLHENV db_environ_handle; /* Handle ODBC environment */ | |
297 | +long result; /* result of functions */ | |
a9eff38d WD |
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; | |
2c40f06f | 303 | +extern int am_sender; |
a9eff38d | 304 | +extern int am_generator; |
2c40f06f WD |
305 | +extern char *auth_user; |
306 | +extern int module_id; | |
a9eff38d WD |
307 | +extern int dry_run; |
308 | + | |
2c40f06f WD |
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 | +{ | |
a9eff38d WD |
362 | + if (!lp_database_logging(module_id)) |
363 | + return; | |
2c40f06f | 364 | + |
a9eff38d WD |
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 | + } | |
2c40f06f | 371 | + |
a9eff38d WD |
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 | + } | |
2c40f06f | 389 | + |
a9eff38d WD |
390 | + /* Set connection attributes for the generator db connection */ |
391 | + SQLSetConnectAttr(db_handle_g, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0); | |
2c40f06f | 392 | + |
a9eff38d WD |
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); | |
2c40f06f | 397 | + |
a9eff38d WD |
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 | + } | |
2c40f06f | 421 | + |
a9eff38d WD |
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; | |
2c40f06f | 439 | + } |
a9eff38d WD |
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; | |
2c40f06f WD |
469 | + } |
470 | +} | |
471 | + | |
472 | +void db_log_close() | |
473 | +{ | |
a9eff38d WD |
474 | + if (!lp_database_logging(module_id)) |
475 | + return; | |
476 | + | |
477 | + if (am_generator) { | |
478 | + if (sql_statement_handle_g != NULL) { | |
2c40f06f | 479 | + /* free the statement handle first */ |
a9eff38d WD |
480 | + SQLFreeHandle(SQL_HANDLE_STMT,sql_statement_handle_g); |
481 | + sql_statement_handle_g = NULL; | |
2c40f06f | 482 | + } else { |
a9eff38d | 483 | + rprintf(FERROR,"No generator sql statement handle to close\n"); |
2c40f06f | 484 | + } |
a9eff38d WD |
485 | + |
486 | + if (db_handle_g != NULL) { | |
2c40f06f | 487 | + /* disconnect, and free the database handle. */ |
a9eff38d WD |
488 | + SQLDisconnect(db_handle_g); |
489 | + SQLFreeHandle(SQL_HANDLE_DBC,db_handle_g); | |
490 | + db_handle_g = NULL; | |
2c40f06f | 491 | + } else { |
a9eff38d | 492 | + rprintf(FERROR,"Generator database connection already closed\n"); |
2c40f06f | 493 | + } |
a9eff38d WD |
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; | |
2c40f06f | 499 | + } else { |
a9eff38d | 500 | + rprintf(FERROR,"No receiver sql statement handle to close\n"); |
2c40f06f | 501 | + } |
a9eff38d WD |
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"); | |
2c40f06f WD |
519 | + } |
520 | +} | |
521 | + | |
522 | +static long get_unique_session_id() | |
523 | +{ | |
524 | + long unique; | |
525 | + char strSqlStatement[1024]; | |
a9eff38d WD |
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; | |
2c40f06f WD |
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) { | |
a9eff38d WD |
535 | + snprintf(strSqlStatement, sizeof strSqlStatement, |
536 | + "SELECT NEXTVAL('%s');", lp_sequence_name(module_id)); | |
2c40f06f | 537 | + } else if (strcmp(lp_unique_id_method(module_id),"nextval-oracle") == 0) { |
a9eff38d WD |
538 | + snprintf(strSqlStatement, sizeof strSqlStatement, |
539 | + "SELECT %s.NEXTVAL FROM dual;", lp_sequence_name(module_id)); | |
2c40f06f | 540 | + } else if (strcmp(lp_unique_id_method(module_id),"nextval-db2") == 0) { |
a9eff38d WD |
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)); | |
2c40f06f WD |
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 | + | |
2c40f06f WD |
582 | +void db_log_session() |
583 | +{ | |
584 | + char strSqlStatement[1024]; | |
585 | + int gotSessionID = 0; | |
a9eff38d WD |
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. */ | |
2c40f06f | 597 | + if (strcmp(lp_unique_id_method(module_id),"nextval-postgresql") == 0 |
a9eff38d WD |
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))) { | |
2c40f06f WD |
602 | + session_id = get_unique_session_id(); |
603 | + gotSessionID = 1; | |
a9eff38d WD |
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()); | |
2c40f06f WD |
608 | + } else { |
609 | + /* Otherwise the ID gets created automatically, and we get the ID it used after the insert. */ | |
a9eff38d WD |
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()); | |
2c40f06f WD |
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 | + } | |
a9eff38d | 633 | + } else { |
2c40f06f WD |
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; | |
637ba3ff WD |
641 | + char strSqlStatement[2048]; |
642 | + char strFileName[MAXPATHLEN]; | |
643 | + char *strFileNamePtr; | |
2c40f06f WD |
644 | + char strFileSize[255]; |
645 | + int64 intBytesTransferred; | |
646 | + int64 intCheckSumBytes; | |
a9eff38d WD |
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; | |
2c40f06f | 649 | + |
a9eff38d WD |
650 | + if (!lp_database_logging(module_id)) |
651 | + return; | |
637ba3ff | 652 | + |
a9eff38d | 653 | + if (db_handle != NULL) { |
bd68c3c2 | 654 | + strFileNamePtr = f_name(file, NULL); |
a9eff38d WD |
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++; | |
2c40f06f | 663 | + |
1aa236e1 | 664 | + snprintf(strFileSize, sizeof strFileSize, "%.0f", (double)F_LENGTH(file)); |
a9eff38d WD |
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 | + } | |
2c40f06f | 670 | + |
a9eff38d WD |
671 | + if (!am_sender) { |
672 | + intCheckSumBytes = stats.total_written - initial_stats->total_written; | |
2c40f06f | 673 | + } else { |
a9eff38d | 674 | + intCheckSumBytes = stats.total_read - initial_stats->total_read; |
2c40f06f | 675 | + } |
a9eff38d WD |
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"); | |
2c40f06f WD |
691 | + } |
692 | +} | |
693 | + | |
2c40f06f WD |
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; | |
a9eff38d WD |
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 | + | |
2c40f06f | 705 | + if (db_handle != NULL) { |
a9eff38d WD |
706 | + if (code != 0) { |
707 | + error_text = rerr_name(code); | |
708 | + if (!error_text) { | |
709 | + error_text = "unexplained error"; | |
2c40f06f | 710 | + } |
a9eff38d WD |
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()); | |
2c40f06f | 718 | + |
a9eff38d | 719 | + result = SQLExecDirect(sql_statement_handle,strSqlStatement,SQL_NTS); |
2c40f06f | 720 | + |
a9eff38d WD |
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); | |
2c40f06f WD |
724 | + } |
725 | + } else { | |
726 | + rprintf(FERROR,"Error at db_log_exit: Not connected to database!\n"); | |
727 | + } | |
728 | +} | |
a9eff38d WD |
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 | +} | |
9a7eef96 WD |
812 | --- old/instructions |
813 | +++ new/instructions | |
a9eff38d WD |
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. | |
9a7eef96 WD |
899 | --- old/loadparm.c |
900 | +++ new/loadparm.c | |
0ffcefad | 901 | @@ -122,9 +122,16 @@ typedef struct |
51b0132f WD |
902 | { |
903 | char *auth_users; | |
904 | char *comment; | |
905 | + char *custom_unique_id_select; | |
2c40f06f | 906 | + char *database_datasource; |
2c40f06f | 907 | + char *database_password; |
51b0132f | 908 | + char *database_username; |
a9eff38d | 909 | + char *delete_table_name; |
51b0132f | 910 | char *dont_compress; |
a9eff38d | 911 | + char *error_table_name; |
51b0132f WD |
912 | char *exclude; |
913 | char *exclude_from; | |
2c40f06f | 914 | + char *exit_table_name; |
51b0132f WD |
915 | char *filter; |
916 | char *gid; | |
917 | char *hosts_allow; | |
0ffcefad | 918 | @@ -142,14 +149,20 @@ typedef struct |
51b0132f WD |
919 | char *prexfer_exec; |
920 | char *refuse_options; | |
921 | char *secrets_file; | |
2c40f06f | 922 | + char *sequence_name; |
51b0132f WD |
923 | + char *session_table_name; |
924 | char *temp_dir; | |
925 | + char *transfer_table_name; | |
926 | char *uid; | |
2c40f06f | 927 | + char *unique_id_method; |
51b0132f WD |
928 | |
929 | int max_connections; | |
930 | int max_verbosity; | |
0ffcefad | 931 | int syslog_facility; |
51b0132f WD |
932 | int timeout; |
933 | ||
934 | + BOOL database_logging; | |
2c40f06f WD |
935 | + BOOL get_custom_id_before_insert; |
936 | BOOL ignore_errors; | |
51b0132f WD |
937 | BOOL ignore_nonreadable; |
938 | BOOL list; | |
0ffcefad | 939 | @@ -169,9 +182,16 @@ static service sDefault = |
51b0132f | 940 | { |
32f6df4b WD |
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, | |
a9eff38d | 947 | + /* delete_table_name; */ NULL, |
32f6df4b | 948 | /* dont_compress; */ "*.gz *.tgz *.zip *.z *.rpm *.deb *.iso *.bz2 *.tbz", |
a9eff38d | 949 | + /* error_table_name; */ NULL, |
32f6df4b WD |
950 | /* exclude; */ NULL, |
951 | /* exclude_from; */ NULL, | |
952 | + /* exit_table_name; */ NULL, | |
953 | /* filter; */ NULL, | |
954 | /* gid; */ NOBODY_GROUP, | |
955 | /* hosts_allow; */ NULL, | |
0ffcefad | 956 | @@ -189,14 +209,20 @@ static service sDefault = |
32f6df4b WD |
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, | |
51b0132f | 966 | |
32f6df4b WD |
967 | /* max_connections; */ 0, |
968 | /* max_verbosity; */ 1, | |
0ffcefad | 969 | /* syslog_facility; */ LOG_DAEMON, |
32f6df4b | 970 | /* timeout; */ 0, |
51b0132f | 971 | |
32f6df4b WD |
972 | + /* database_logging; */ False, |
973 | + /* get_custom_id_before_insert; */ True, | |
974 | /* ignore_errors; */ False, | |
975 | /* ignore_nonreadable; */ False, | |
976 | /* list; */ True, | |
0ffcefad | 977 | @@ -295,10 +321,19 @@ static struct parm_struct parm_table[] = |
51b0132f WD |
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}, | |
a9eff38d | 986 | + {"delete table name", P_STRING, P_LOCAL, &sDefault.delete_table_name, NULL,0}, |
51b0132f | 987 | {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, |
a9eff38d | 988 | + {"error table name", P_STRING, P_LOCAL, &sDefault.error_table_name, NULL,0}, |
51b0132f WD |
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}, | |
0ffcefad | 997 | @@ -323,12 +358,16 @@ static struct parm_struct parm_table[] = |
a9eff38d WD |
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}, | |
51b0132f WD |
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}, | |
a9eff38d | 1003 | {"strict modes", P_BOOL, P_LOCAL, &sDefault.strict_modes, NULL,0}, |
0ffcefad | 1004 | {"syslog facility", P_ENUM, P_LOCAL, &sDefault.syslog_facility,enum_facilities,0}, |
a9eff38d WD |
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}, | |
51b0132f | 1008 | + {"transfer table name",P_STRING,P_LOCAL, &sDefault.transfer_table_name,NULL,0}, |
a9eff38d | 1009 | {"uid", P_STRING, P_LOCAL, &sDefault.uid, NULL,0}, |
51b0132f | 1010 | + {"unique id method", P_STRING, P_LOCAL, &sDefault.unique_id_method, NULL,0}, |
a9eff38d WD |
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} | |
0ffcefad | 1014 | @@ -384,9 +423,16 @@ FN_GLOBAL_INTEGER(lp_rsync_port, &Global |
51b0132f WD |
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) | |
2c40f06f | 1019 | +FN_LOCAL_STRING(lp_database_datasource, database_datasource) |
2c40f06f | 1020 | +FN_LOCAL_STRING(lp_database_password, database_password) |
51b0132f | 1021 | +FN_LOCAL_STRING(lp_database_username, database_username) |
a9eff38d | 1022 | +FN_LOCAL_STRING(lp_delete_table_name,delete_table_name) |
51b0132f | 1023 | FN_LOCAL_STRING(lp_dont_compress, dont_compress) |
a9eff38d | 1024 | +FN_LOCAL_STRING(lp_error_table_name,error_table_name) |
51b0132f WD |
1025 | FN_LOCAL_STRING(lp_exclude, exclude) |
1026 | FN_LOCAL_STRING(lp_exclude_from, exclude_from) | |
2c40f06f | 1027 | +FN_LOCAL_STRING(lp_exit_table_name, exit_table_name) |
51b0132f WD |
1028 | FN_LOCAL_STRING(lp_filter, filter) |
1029 | FN_LOCAL_STRING(lp_gid, gid) | |
1030 | FN_LOCAL_STRING(lp_hosts_allow, hosts_allow) | |
0ffcefad | 1031 | @@ -404,14 +450,20 @@ FN_LOCAL_STRING(lp_postxfer_exec, postxf |
51b0132f WD |
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) | |
2c40f06f | 1035 | +FN_LOCAL_STRING(lp_sequence_name,sequence_name) |
51b0132f | 1036 | +FN_LOCAL_STRING(lp_session_table_name,session_table_name) |
0ffcefad | 1037 | FN_LOCAL_INTEGER(lp_syslog_facility, syslog_facility) |
51b0132f WD |
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) | |
2c40f06f | 1041 | +FN_LOCAL_STRING(lp_unique_id_method,unique_id_method) |
51b0132f WD |
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) | |
2c40f06f WD |
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) | |
51b0132f | 1051 | FN_LOCAL_BOOL(lp_list, list) |
9a7eef96 WD |
1052 | --- old/log.c |
1053 | +++ new/log.c | |
1aa236e1 | 1054 | @@ -95,7 +95,7 @@ struct { |
2c40f06f WD |
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++) { | |
9a7eef96 WD |
1063 | --- old/receiver.c |
1064 | +++ new/receiver.c | |
1aa236e1 | 1065 | @@ -110,6 +110,10 @@ int get_tmpname(char *fnametmp, char *fn |
93ca4d27 | 1066 | |
a9eff38d | 1067 | if (maxname < 1) { |
93ca4d27 | 1068 | rprintf(FERROR, "temporary filename too long: %s\n", fname); |
a9eff38d WD |
1069 | +#ifdef HAVE_LIBODBC |
1070 | + db_log_error(FERROR,13, "temporary filename too long: %s\n", | |
93ca4d27 | 1071 | + fname); |
a9eff38d WD |
1072 | +#endif |
1073 | fnametmp[0] = '\0'; | |
1074 | return 0; | |
1075 | } | |
b3ea4757 | 1076 | @@ -173,6 +177,10 @@ static int receive_data(int f_in, char * |
88f7513d WD |
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 | } | |
b3ea4757 | 1087 | @@ -230,6 +238,11 @@ static int receive_data(int f_in, char * |
88f7513d WD |
1088 | "lseek of %s returned %.0f, not %.0f", |
1089 | full_fname(fname), | |
1090 | (double)pos, (double)offset); | |
a9eff38d WD |
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; | |
b3ea4757 | 1099 | @@ -255,6 +268,9 @@ static int receive_data(int f_in, char * |
a9eff38d WD |
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 | ||
f2376a08 | 1109 | @@ -298,6 +314,12 @@ static void handle_delayed_updates(char |
93ca4d27 | 1110 | rsyserr(FERROR, errno, |
a9eff38d | 1111 | "rename failed for %s (from %s)", |
93ca4d27 | 1112 | full_fname(fname), partialptr); |
a9eff38d WD |
1113 | +#ifdef HAVE_LIBODBC |
1114 | + db_log_error(FERROR, 16, | |
1115 | + "rename failed for %s (from %s)", | |
1116 | + full_fname(fname), | |
93ca4d27 | 1117 | + partialptr); |
a9eff38d WD |
1118 | +#endif |
1119 | } else { | |
60a8bf36 | 1120 | if (remove_source_files |
1aa236e1 | 1121 | || (preserve_hard_links && F_IS_HLINKED(file))) |
f2376a08 | 1122 | @@ -431,6 +453,9 @@ int recv_files(int f_in, char *local_nam |
a9eff38d WD |
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 | |
88f7513d | 1127 | + db_log_error(FERROR,17,"attempt to hack rsync failed."); |
a9eff38d WD |
1128 | +#endif |
1129 | exit_cleanup(RERR_PROTOCOL); | |
1130 | } | |
1131 | ||
f2376a08 | 1132 | @@ -487,6 +512,11 @@ int recv_files(int f_in, char *local_nam |
a9eff38d WD |
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, | |
f2376a08 | 1144 | @@ -535,6 +565,9 @@ int recv_files(int f_in, char *local_nam |
b3ea4757 | 1145 | } else if (do_fstat(fd1,&st) != 0) { |
a9eff38d WD |
1146 | rsyserr(FERROR, errno, "fstat %s failed", |
1147 | full_fname(fnamecmp)); | |
1148 | +#ifdef HAVE_LIBODBC | |
88f7513d | 1149 | + db_log_error(FERROR, 19,"fstat %s failed",full_fname(fnamecmp)); |
a9eff38d | 1150 | +#endif |
1aa236e1 | 1151 | discard_receive_data(f_in, F_LENGTH(file)); |
a9eff38d WD |
1152 | close(fd1); |
1153 | continue; | |
f2376a08 | 1154 | @@ -548,6 +581,9 @@ int recv_files(int f_in, char *local_nam |
a9eff38d WD |
1155 | */ |
1156 | rprintf(FERROR,"recv_files: %s is a directory\n", | |
1157 | full_fname(fnamecmp)); | |
1158 | +#ifdef HAVE_LIBODBC | |
88f7513d | 1159 | + db_log_error(FERROR,20,"recv_files: %s is a directory",full_fname(fnamecmp)); |
a9eff38d | 1160 | +#endif |
1aa236e1 | 1161 | discard_receive_data(f_in, F_LENGTH(file)); |
a9eff38d WD |
1162 | close(fd1); |
1163 | continue; | |
f2376a08 | 1164 | @@ -571,6 +607,9 @@ int recv_files(int f_in, char *local_nam |
a9eff38d WD |
1165 | if (fd2 == -1) { |
1166 | rsyserr(FERROR, errno, "open %s failed", | |
1167 | full_fname(fname)); | |
1168 | +#ifdef HAVE_LIBODBC | |
88f7513d | 1169 | + db_log_error(FERROR,22, "open %s failed", full_fname(fname)); |
a9eff38d | 1170 | +#endif |
1aa236e1 | 1171 | discard_receive_data(f_in, F_LENGTH(file)); |
a9eff38d WD |
1172 | if (fd1 != -1) |
1173 | close(fd1); | |
f2376a08 | 1174 | @@ -604,6 +643,10 @@ int recv_files(int f_in, char *local_nam |
a9eff38d WD |
1175 | if (fd2 == -1) { |
1176 | rsyserr(FERROR, errno, "mkstemp %s failed", | |
1177 | full_fname(fnametmp)); | |
1178 | +#ifdef HAVE_LIBODBC | |
88f7513d | 1179 | + db_log_error(FERROR, 22, "mkstemp %s failed", |
a9eff38d WD |
1180 | + full_fname(fnametmp)); |
1181 | +#endif | |
1aa236e1 | 1182 | discard_receive_data(f_in, F_LENGTH(file)); |
a9eff38d WD |
1183 | if (fd1 != -1) |
1184 | close(fd1); | |
f2376a08 | 1185 | @@ -624,12 +667,19 @@ int recv_files(int f_in, char *local_nam |
1aa236e1 | 1186 | fname, fd2, F_LENGTH(file)); |
2c40f06f | 1187 | |
a859733e | 1188 | log_item(log_code, file, &initial_stats, iflags, NULL); |
637ba3ff | 1189 | +#ifdef HAVE_LIBODBC |
13bed3dd | 1190 | + db_log_transfer(file, &initial_stats, "receive"); |
2c40f06f | 1191 | +#endif |
dc3ae351 | 1192 | |
13bed3dd | 1193 | if (fd1 != -1) |
dc3ae351 | 1194 | close(fd1); |
a9eff38d WD |
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 | ||
f2376a08 | 1205 | @@ -686,6 +736,12 @@ int recv_files(int f_in, char *local_nam |
93ca4d27 | 1206 | rprintf(msgtype, |
a9eff38d | 1207 | "%s: %s failed verification -- update %s%s.\n", |
93ca4d27 | 1208 | errstr, fname, keptstr, redostr); |
a9eff38d | 1209 | +#ifdef HAVE_LIBODBC |
88f7513d | 1210 | + db_log_error(msgtype,24, |
a9eff38d | 1211 | + "%s: %s failed verification -- update %s%s.\n", |
93ca4d27 | 1212 | + errstr, fname, |
a9eff38d WD |
1213 | + keptstr, redostr); |
1214 | +#endif | |
1215 | } | |
f2376a08 | 1216 | if (!phase || incremental) { |
590329e5 | 1217 | send_msg_int(MSG_REDO, ndx); |
9a7eef96 WD |
1218 | --- old/sender.c |
1219 | +++ new/sender.c | |
f2376a08 | 1220 | @@ -326,6 +326,9 @@ void send_files(int f_in, int f_out) |
a859733e | 1221 | end_progress(st.st_size); |
36bbf3d1 | 1222 | |
a859733e | 1223 | log_item(log_code, file, &initial_stats, iflags, NULL); |
637ba3ff | 1224 | +#ifdef HAVE_LIBODBC |
9be39c35 | 1225 | + db_log_transfer(file, &initial_stats,"send"); |
2c40f06f | 1226 | +#endif |
2c40f06f | 1227 | |
9be39c35 WD |
1228 | if (mbuf) { |
1229 | j = unmap_file(mbuf); |