- Added some new refused_* variables that note if certain options
[rsync.git] / log.c
1 /* -*- c-file-style: "linux"; -*-
2
3    Copyright (C) 1998-2001 by Andrew Tridgell <tridge@samba.org>
4    Copyright (C) 2000-2001 by Martin Pool <mbp@samba.org>
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22   Logging and utility functions.
23   tridge, May 1998
24
25   Mapping to human-readable messages added by Martin Pool
26   <mbp@samba.org>, Oct 2000.
27   */
28 #include "rsync.h"
29
30 extern int am_daemon;
31 extern int am_server;
32 extern int am_sender;
33 extern int quiet;
34 extern int module_id;
35 extern int msg_fd_out;
36 extern char *auth_user;
37 extern char *log_format;
38
39 static int log_initialised;
40 static char *logfname;
41 static FILE *logfile;
42 struct stats stats;
43
44 int log_got_error = 0;
45
46 struct {
47         int code;
48         char const *name;
49 } const rerr_names[] = {
50         { RERR_SYNTAX     , "syntax or usage error" },
51         { RERR_PROTOCOL   , "protocol incompatibility" },
52         { RERR_FILESELECT , "errors selecting input/output files, dirs" },
53         { RERR_UNSUPPORTED, "requested action not supported" },
54         { RERR_STARTCLIENT, "error starting client-server protocol" },
55         { RERR_SOCKETIO   , "error in socket IO" },
56         { RERR_FILEIO     , "error in file IO" },
57         { RERR_STREAMIO   , "error in rsync protocol data stream" },
58         { RERR_MESSAGEIO  , "errors with program diagnostics" },
59         { RERR_IPC        , "error in IPC code" },
60         { RERR_SIGNAL     , "received SIGUSR1 or SIGINT" },
61         { RERR_WAITCHILD  , "some error returned by waitpid()" },
62         { RERR_MALLOC     , "error allocating core memory buffers" },
63         { RERR_PARTIAL    , "some files could not be transferred" },
64         { RERR_VANISHED   , "some files vanished before they could be transferred" },
65         { RERR_TIMEOUT    , "timeout in data send/receive" },
66         { RERR_CMD_FAILED , "remote shell failed" },
67         { RERR_CMD_KILLED , "remote shell killed" },
68         { RERR_CMD_RUN,     "remote command could not be run" },
69         { RERR_CMD_NOTFOUND, "remote command not found" },
70         { 0, NULL }
71 };
72
73
74
75 /*
76  * Map from rsync error code to name, or return NULL.
77  */
78 static char const *rerr_name(int code)
79 {
80         int i;
81         for (i = 0; rerr_names[i].name; i++) {
82                 if (rerr_names[i].code == code)
83                         return rerr_names[i].name;
84         }
85         return NULL;
86 }
87
88 static void log_open(void)
89 {
90         if (logfname && !logfile) {
91                 extern int orig_umask;
92                 int old_umask = umask(022 | orig_umask);
93                 logfile = fopen(logfname, "a");
94                 umask(old_umask);
95                 if (!logfile) {
96                         am_daemon = 0; /* avoid trying to log again */
97                         rsyserr(FERROR, errno, "fopen() of log-file failed");
98                         exit_cleanup(RERR_FILESELECT);
99                 }
100         }
101 }
102
103 static void logit(int priority, char *buf)
104 {
105         if (logfname) {
106                 if (!logfile)
107                         log_open();
108                 fprintf(logfile,"%s [%d] %s",
109                         timestring(time(NULL)), (int)getpid(), buf);
110                 fflush(logfile);
111         } else {
112                 syslog(priority, "%s", buf);
113         }
114 }
115
116 void log_init(void)
117 {
118         int options = LOG_PID;
119         time_t t;
120
121         if (log_initialised)
122                 return;
123         log_initialised = 1;
124
125         /* this looks pointless, but it is needed in order for the
126          * C library on some systems to fetch the timezone info
127          * before the chroot */
128         t = time(NULL);
129         localtime(&t);
130
131         /* optionally use a log file instead of syslog */
132         logfname = lp_log_file();
133         if (logfname) {
134                 if (*logfname) {
135                         log_open();
136                         return;
137                 }
138                 logfname = NULL;
139         }
140
141 #ifdef LOG_NDELAY
142         options |= LOG_NDELAY;
143 #endif
144
145 #ifdef LOG_DAEMON
146         openlog("rsyncd", options, lp_syslog_facility());
147 #else
148         openlog("rsyncd", options);
149 #endif
150
151 #ifndef LOG_NDELAY
152         logit(LOG_INFO,"rsyncd started\n");
153 #endif
154 }
155
156 void log_close(void)
157 {
158         if (logfile) {
159                 fclose(logfile);
160                 logfile = NULL;
161         }
162 }
163
164 /* this is the underlying (unformatted) rsync debugging function. Call
165  * it with FINFO, FERROR or FLOG */
166 void rwrite(enum logcode code, char *buf, int len)
167 {
168         FILE *f = NULL;
169         /* recursion can happen with certain fatal conditions */
170
171         if (quiet && code == FINFO)
172                 return;
173
174         if (len < 0)
175                 exit_cleanup(RERR_MESSAGEIO);
176
177         buf[len] = 0;
178
179         if (am_server && msg_fd_out >= 0) {
180                 /* Pass the message to our sibling. */
181                 send_msg((enum msgcode)code, buf, len);
182                 return;
183         }
184
185         if (am_daemon) {
186                 static int in_block;
187                 char msg[2048];
188                 int priority = code == FERROR ? LOG_WARNING : LOG_INFO;
189
190                 if (in_block)
191                         return;
192                 in_block = 1;
193                 if (!log_initialised)
194                         log_init();
195                 strlcpy(msg, buf, MIN((int)sizeof msg, len + 1));
196                 logit(priority, msg);
197                 in_block = 0;
198
199                 if (code == FLOG || !am_server)
200                         return;
201         } else if (code == FLOG)
202                 return;
203
204         if (am_server) {
205                 /* Pass the message to the non-server side. */
206                 if (io_multiplex_write((enum msgcode)code, buf, len))
207                         return;
208                 if (am_daemon) {
209                         /* TODO: can we send the error to the user somehow? */
210                         return;
211                 }
212         }
213
214         if (code == FERROR) {
215                 log_got_error = 1;
216                 f = stderr;
217         }
218
219         if (code == FINFO)
220                 f = am_server ? stderr : stdout;
221
222         if (!f)
223                 exit_cleanup(RERR_MESSAGEIO);
224
225         if (fwrite(buf, len, 1, f) != 1)
226                 exit_cleanup(RERR_MESSAGEIO);
227
228         if (buf[len-1] == '\r' || buf[len-1] == '\n')
229                 fflush(f);
230 }
231                 
232
233 /* This is the rsync debugging function. Call it with FINFO, FERROR or
234  * FLOG. */
235 void rprintf(enum logcode code, const char *format, ...)
236 {
237         va_list ap;
238         char buf[MAXPATHLEN+512];
239         size_t len;
240
241         va_start(ap, format);
242         len = vsnprintf(buf, sizeof(buf), format, ap);
243         va_end(ap);
244
245         /* Deal with buffer overruns.  Instead of panicking, just
246          * truncate the resulting string.  (Note that configure ensures
247          * that we have a vsnprintf() that doesn't ever return -1.) */
248         if (len > sizeof buf - 1) {
249                 const char ellipsis[] = "[...]";
250
251                 /* Reset length, and zero-terminate the end of our buffer */
252                 len = sizeof(buf)-1;
253                 buf[len] = '\0';
254
255                 /* Copy the ellipsis to the end of the string, but give
256                  * us one extra character:
257                  *
258                  *                  v--- null byte at buf[sizeof(buf)-1]
259                  *        abcdefghij0
260                  *     -> abcd[...]00  <-- now two null bytes at end
261                  *
262                  * If the input format string has a trailing newline,
263                  * we copy it into that extra null; if it doesn't, well,
264                  * all we lose is one byte.  */
265                 strncpy(buf+len-sizeof(ellipsis), ellipsis, sizeof(ellipsis));
266                 if (format[strlen(format)-1] == '\n') {
267                         buf[len-1] = '\n';
268                 }
269         }
270
271         rwrite(code, buf, len);
272 }
273
274
275 /* This is like rprintf, but it also tries to print some
276  * representation of the error code.  Normally errcode = errno.
277  *
278  * Unlike rprintf, this always adds a newline and there should not be
279  * one in the format string.
280  *
281  * Note that since strerror might involve dynamically loading a
282  * message catalog we need to call it once before chroot-ing. */
283 void rsyserr(enum logcode code, int errcode, const char *format, ...)
284 {
285         va_list ap;
286         char buf[MAXPATHLEN+512];
287         size_t len;
288
289         strcpy(buf, RSYNC_NAME ": ");
290         len = (sizeof RSYNC_NAME ": ") - 1;
291
292         va_start(ap, format);
293         len += vsnprintf(buf + len, sizeof buf - len, format, ap);
294         va_end(ap);
295
296         if (len < sizeof buf) {
297                 len += snprintf(buf + len, sizeof buf - len,
298                                 ": %s (%d)\n", strerror(errcode), errcode);
299         }
300         if (len >= sizeof buf)
301                 exit_cleanup(RERR_MESSAGEIO);
302
303         rwrite(code, buf, len);
304 }
305
306
307
308 void rflush(enum logcode code)
309 {
310         FILE *f = NULL;
311         
312         if (am_daemon) {
313                 return;
314         }
315
316         if (code == FLOG) {
317                 return;
318         }
319
320         if (code == FERROR) {
321                 f = stderr;
322         }
323
324         if (code == FINFO) {
325                 if (am_server)
326                         f = stderr;
327                 else
328                         f = stdout;
329         }
330
331         if (!f) exit_cleanup(RERR_MESSAGEIO);
332         fflush(f);
333 }
334
335
336
337 /* a generic logging routine for send/recv, with parameter
338  * substitiution */
339 static void log_formatted(enum logcode code,
340                           char *format, char *op, struct file_struct *file,
341                           struct stats *initial_stats)
342 {
343         char buf[1024];
344         char buf2[1024];
345         char *p, *s, *n;
346         size_t l;
347         int64 b;
348
349         /* We expand % codes one by one in place in buf.  We don't
350          * copy in the terminating nul of the inserted strings, but
351          * rather keep going until we reach the nul of the format.
352          * Just to make sure we don't clobber that nul and therefore
353          * accidentally keep going, we zero the buffer now. */
354         l = strlcpy(buf, format, sizeof buf);
355         if (l < sizeof buf)
356                 memset(buf + l, 0, sizeof buf - l);
357         
358         for (s = &buf[0]; s && (p = strchr(s,'%')); ) {
359                 n = NULL;
360                 s = p + 1;
361
362                 switch (p[1]) {
363                 case 'h': if (am_daemon) n = client_name(0); break;
364                 case 'a': if (am_daemon) n = client_addr(0); break;
365                 case 'l':
366                         snprintf(buf2,sizeof(buf2),"%.0f",
367                                  (double)file->length);
368                         n = buf2;
369                         break;
370                 case 'p':
371                         snprintf(buf2,sizeof(buf2),"%d",
372                                  (int)getpid());
373                         n = buf2;
374                         break;
375                 case 'o': n = op; break;
376                 case 'f':
377                         pathjoin(buf2, sizeof buf2,
378                             am_sender && file->dir.root ? file->dir.root : "",
379                             safe_fname(f_name(file)));
380                         clean_fname(buf2, 0);
381                         n = buf2;
382                         if (*n == '/') n++;
383                         break;
384                 case 'm': n = lp_name(module_id); break;
385                 case 't': n = timestring(time(NULL)); break;
386                 case 'P': n = lp_path(module_id); break;
387                 case 'u': n = auth_user; break;
388                 case 'b':
389                         if (am_sender) {
390                                 b = stats.total_written -
391                                         initial_stats->total_written;
392                         } else {
393                                 b = stats.total_read -
394                                         initial_stats->total_read;
395                         }
396                         snprintf(buf2,sizeof(buf2),"%.0f", (double)b);
397                         n = buf2;
398                         break;
399                 case 'c':
400                         if (!am_sender) {
401                                 b = stats.total_written -
402                                         initial_stats->total_written;
403                         } else {
404                                 b = stats.total_read -
405                                         initial_stats->total_read;
406                         }
407                         snprintf(buf2,sizeof(buf2),"%.0f", (double)b);
408                         n = buf2;
409                         break;
410                 }
411
412                 /* n is the string to be inserted in place of this %
413                  * code; l is its length not including the trailing
414                  * NUL */
415                 if (!n)
416                         continue;
417
418                 l = strlen(n);
419
420                 if (l + ((int)(s - &buf[0])) >= sizeof(buf)) {
421                         rprintf(FERROR,"buffer overflow expanding %%%c -- exiting\n",
422                                 p[0]);
423                         exit_cleanup(RERR_MESSAGEIO);
424                 }
425
426                 /* Shuffle the rest of the string along to make space for n */
427                 if (l != 2) {
428                         memmove(s+(l-1), s+1, strlen(s+1)+1);
429                 }
430
431                 /* Copy in n but NOT its nul, because the format sting
432                  * probably continues after this. */
433                 memcpy(p, n, l);
434
435                 /* Skip over inserted string; continue looking */
436                 s = p+l;
437         }
438
439         rprintf(code,"%s\n", buf);
440 }
441
442 /* log the outgoing transfer of a file */
443 void log_send(struct file_struct *file, struct stats *initial_stats)
444 {
445         if (lp_transfer_logging(module_id)) {
446                 log_formatted(FLOG, lp_log_format(module_id), "send", file, initial_stats);
447         } else if (log_format && !am_server) {
448                 log_formatted(FINFO, log_format, "send", file, initial_stats);
449         }
450 }
451
452 /* log the incoming transfer of a file */
453 void log_recv(struct file_struct *file, struct stats *initial_stats)
454 {
455         if (lp_transfer_logging(module_id)) {
456                 log_formatted(FLOG, lp_log_format(module_id), "recv", file, initial_stats);
457         } else if (log_format && !am_server) {
458                 log_formatted(FINFO, log_format, "recv", file, initial_stats);
459         }
460 }
461
462
463
464
465 /*
466  * Called when the transfer is interrupted for some reason.
467  *
468  * Code is one of the RERR_* codes from errcode.h, or terminating
469  * successfully.
470  */
471 void log_exit(int code, const char *file, int line)
472 {
473         if (code == 0) {
474                 rprintf(FLOG,"wrote %.0f bytes  read %.0f bytes  total size %.0f\n",
475                         (double)stats.total_written,
476                         (double)stats.total_read,
477                         (double)stats.total_size);
478         } else {
479                 const char *name;
480
481                 name = rerr_name(code);
482                 if (!name)
483                         name = "unexplained error";
484
485                 /* VANISHED is not an error, only a warning */
486                 if (code == RERR_VANISHED) {
487                         rprintf(FINFO, "rsync warning: %s (code %d) at %s(%d)\n", 
488                                 name, code, file, line);
489                 } else {
490                         rprintf(FERROR, "rsync error: %s (code %d) at %s(%d)\n",
491                                 name, code, file, line);
492                 }
493         }
494 }