- The iconv-supporting code can now ask filtered_fwrite() to use
[rsync/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 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
30 #include <iconv.h>
31 #endif
32
33 extern int verbose;
34 extern int dry_run;
35 extern int am_daemon;
36 extern int am_server;
37 extern int am_sender;
38 extern int local_server;
39 extern int quiet;
40 extern int module_id;
41 extern int msg_fd_out;
42 extern int protocol_version;
43 extern int preserve_times;
44 extern int log_format_has_i;
45 extern int log_format_has_o_or_i;
46 extern int daemon_log_format_has_o_or_i;
47 extern char *auth_user;
48 extern char *log_format;
49 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
50 extern iconv_t ic_chck;
51 #endif
52
53 static int log_initialised;
54 static int logfile_was_closed;
55 static char *logfname;
56 static FILE *logfile;
57 struct stats stats;
58
59 int log_got_error = 0;
60
61 struct {
62         int code;
63         char const *name;
64 } const rerr_names[] = {
65         { RERR_SYNTAX     , "syntax or usage error" },
66         { RERR_PROTOCOL   , "protocol incompatibility" },
67         { RERR_FILESELECT , "errors selecting input/output files, dirs" },
68         { RERR_UNSUPPORTED, "requested action not supported" },
69         { RERR_STARTCLIENT, "error starting client-server protocol" },
70         { RERR_SOCKETIO   , "error in socket IO" },
71         { RERR_FILEIO     , "error in file IO" },
72         { RERR_STREAMIO   , "error in rsync protocol data stream" },
73         { RERR_MESSAGEIO  , "errors with program diagnostics" },
74         { RERR_IPC        , "error in IPC code" },
75         { RERR_CRASHED    , "sibling process crashed" },
76         { RERR_TERMINATED , "sibling process terminated abnormally" },
77         { RERR_SIGNAL1    , "received SIGUSR1" },
78         { RERR_SIGNAL     , "received SIGINT, SIGTERM, or SIGHUP" },
79         { RERR_WAITCHILD  , "waitpid() failed" },
80         { RERR_MALLOC     , "error allocating core memory buffers" },
81         { RERR_PARTIAL    , "some files could not be transferred" },
82         { RERR_VANISHED   , "some files vanished before they could be transferred" },
83         { RERR_TIMEOUT    , "timeout in data send/receive" },
84         { RERR_CMD_FAILED , "remote shell failed" },
85         { RERR_CMD_KILLED , "remote shell killed" },
86         { RERR_CMD_RUN    , "remote command could not be run" },
87         { RERR_CMD_NOTFOUND,"remote command not found" },
88         { RERR_DEL_LIMIT  , "the --max-delete limit stopped deletions" },
89         { 0, NULL }
90 };
91
92
93 /*
94  * Map from rsync error code to name, or return NULL.
95  */
96 static char const *rerr_name(int code)
97 {
98         int i;
99         for (i = 0; rerr_names[i].name; i++) {
100                 if (rerr_names[i].code == code)
101                         return rerr_names[i].name;
102         }
103         return NULL;
104 }
105
106 static void logit(int priority, char *buf)
107 {
108         if (logfile_was_closed)
109                 logfile_reopen();
110         if (logfile) {
111                 fprintf(logfile,"%s [%d] %s",
112                         timestring(time(NULL)), (int)getpid(), buf);
113                 fflush(logfile);
114         } else {
115                 syslog(priority, "%s", buf);
116         }
117 }
118
119 static void syslog_init()
120 {
121         static int been_here = 0;
122         int options = LOG_PID;
123
124         if (been_here)
125                 return;
126         been_here = 1;
127
128 #ifdef LOG_NDELAY
129         options |= LOG_NDELAY;
130 #endif
131
132 #ifdef LOG_DAEMON
133         openlog("rsyncd", options, lp_syslog_facility());
134 #else
135         openlog("rsyncd", options);
136 #endif
137
138 #ifndef LOG_NDELAY
139         logit(LOG_INFO, "rsyncd started\n");
140 #endif
141 }
142
143 static void logfile_open(void)
144 {
145         extern int orig_umask;
146         int old_umask = umask(022 | orig_umask);
147         logfile = fopen(logfname, "a");
148         umask(old_umask);
149         if (!logfile) {
150                 int fopen_errno = errno;
151                 /* Rsync falls back to using syslog on failure. */
152                 syslog_init();
153                 rsyserr(FERROR, fopen_errno,
154                         "failed to open log-file %s", logfname);
155                 rprintf(FINFO, "Ignoring \"log file\" setting.\n");
156         }
157 }
158
159 void log_init(void)
160 {
161         time_t t;
162
163         if (log_initialised)
164                 return;
165         log_initialised = 1;
166
167         /* this looks pointless, but it is needed in order for the
168          * C library on some systems to fetch the timezone info
169          * before the chroot */
170         t = time(NULL);
171         localtime(&t);
172
173         /* optionally use a log file instead of syslog */
174         logfname = lp_log_file();
175         if (logfname && *logfname)
176                 logfile_open();
177         else
178                 syslog_init();
179 }
180
181 void logfile_close(void)
182 {
183         if (logfile) {
184                 logfile_was_closed = 1;
185                 fclose(logfile);
186                 logfile = NULL;
187         }
188 }
189
190 void logfile_reopen(void)
191 {
192         if (logfile_was_closed) {
193                 logfile_was_closed = 0;
194                 logfile_open();
195         }
196 }
197
198 static void filtered_fwrite(FILE *f, const char *buf, int len, int use_isprint)
199 {
200         const char *s, *end = buf + len;
201         for (s = buf; s < end; s++) {
202                 if ((s < end - 4
203                   && *s == '\\' && s[1] == '#'
204                   && isdigit(*(uchar*)(s+2))
205                   && isdigit(*(uchar*)(s+3))
206                   && isdigit(*(uchar*)(s+4)))
207                  || (*s != '\t'
208                   && ((use_isprint && !isprint(*(uchar*)s))
209                    || *(uchar*)s < ' '))) {
210                         if (s != buf && fwrite(buf, s - buf, 1, f) != 1)
211                                 exit_cleanup(RERR_MESSAGEIO);
212                         fprintf(f, "\\#%03o", *(uchar*)s);
213                         buf = s + 1;
214                 }
215         }
216         if (buf != end && fwrite(buf, end - buf, 1, f) != 1)
217                 exit_cleanup(RERR_MESSAGEIO);
218 }
219
220 /* this is the underlying (unformatted) rsync debugging function. Call
221  * it with FINFO, FERROR or FLOG.  Note: recursion can happen with
222  * certain fatal conditions. */
223 void rwrite(enum logcode code, char *buf, int len)
224 {
225         int trailing_CR_or_NL;
226         FILE *f = NULL;
227
228         if (len < 0)
229                 exit_cleanup(RERR_MESSAGEIO);
230
231         if (quiet && code == FINFO)
232                 return;
233
234         if (am_server && msg_fd_out >= 0) {
235                 /* Pass the message to our sibling. */
236                 send_msg((enum msgcode)code, buf, len);
237                 return;
238         }
239
240         if (code == FSOCKERR) /* This gets simplified for a non-sibling. */
241                 code = FERROR;
242
243         if (code == FCLIENT)
244                 code = FINFO;
245         else if (am_daemon) {
246                 static int in_block;
247                 char msg[2048];
248                 int priority = code == FERROR ? LOG_WARNING : LOG_INFO;
249
250                 if (in_block)
251                         return;
252                 in_block = 1;
253                 if (!log_initialised)
254                         log_init();
255                 strlcpy(msg, buf, MIN((int)sizeof msg, len + 1));
256                 logit(priority, msg);
257                 in_block = 0;
258
259                 if (code == FLOG || !am_server)
260                         return;
261         } else if (code == FLOG)
262                 return;
263
264         if (am_server) {
265                 /* Pass the message to the non-server side. */
266                 if (io_multiplex_write((enum msgcode)code, buf, len))
267                         return;
268                 if (am_daemon) {
269                         /* TODO: can we send the error to the user somehow? */
270                         return;
271                 }
272         }
273
274         switch (code) {
275         case FERROR:
276                 log_got_error = 1;
277                 f = stderr;
278                 goto pre_scan;
279         case FINFO:
280                 f = am_server ? stderr : stdout;
281         pre_scan:
282                 while (len > 1 && *buf == '\n') {
283                         fputc(*buf, f);
284                         buf++;
285                         len--;
286                 }
287                 break;
288         case FNAME:
289                 f = am_server ? stderr : stdout;
290                 break;
291         default:
292                 exit_cleanup(RERR_MESSAGEIO);
293         }
294
295         trailing_CR_or_NL = len && (buf[len-1] == '\n' || buf[len-1] == '\r')
296                           ? buf[--len] : 0;
297
298 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
299         if (ic_chck != (iconv_t)-1) {
300                 char convbuf[1024];
301                 char *in_buf = buf, *out_buf = convbuf;
302                 size_t in_cnt = len, out_cnt = sizeof convbuf - 1;
303
304                 iconv(ic_chck, NULL, 0, NULL, 0);
305                 while (iconv(ic_chck, &in_buf,&in_cnt,
306                                  &out_buf,&out_cnt) == (size_t)-1) {
307                         if (out_buf != convbuf) {
308                                 filtered_fwrite(f, convbuf, out_buf - convbuf, 0);
309                                 out_buf = convbuf;
310                                 out_cnt = sizeof convbuf - 1;
311                         }
312                         if (errno == E2BIG)
313                                 continue;
314                         fprintf(f, "\\#%03o", *(uchar*)in_buf++);
315                         in_cnt--;
316                 }
317                 if (out_buf != convbuf)
318                         filtered_fwrite(f, convbuf, out_buf - convbuf, 0);
319         } else
320 #endif
321                 filtered_fwrite(f, buf, len, 1);
322
323         if (trailing_CR_or_NL) {
324                 fputc(trailing_CR_or_NL, f);
325                 fflush(f);
326         }
327 }
328
329 /* This is the rsync debugging function. Call it with FINFO, FERROR or
330  * FLOG. */
331 void rprintf(enum logcode code, const char *format, ...)
332 {
333         va_list ap;
334         char buf[BIGPATHBUFLEN];
335         size_t len;
336
337         va_start(ap, format);
338         len = vsnprintf(buf, sizeof buf, format, ap);
339         va_end(ap);
340
341         /* Deal with buffer overruns.  Instead of panicking, just
342          * truncate the resulting string.  (Note that configure ensures
343          * that we have a vsnprintf() that doesn't ever return -1.) */
344         if (len > sizeof buf - 1) {
345                 static const char ellipsis[] = "[...]";
346
347                 /* Reset length, and zero-terminate the end of our buffer */
348                 len = sizeof buf - 1;
349                 buf[len] = '\0';
350
351                 /* Copy the ellipsis to the end of the string, but give
352                  * us one extra character:
353                  *
354                  *                  v--- null byte at buf[sizeof buf - 1]
355                  *        abcdefghij0
356                  *     -> abcd[...]00  <-- now two null bytes at end
357                  *
358                  * If the input format string has a trailing newline,
359                  * we copy it into that extra null; if it doesn't, well,
360                  * all we lose is one byte.  */
361                 memcpy(buf+len-sizeof ellipsis, ellipsis, sizeof ellipsis);
362                 if (format[strlen(format)-1] == '\n') {
363                         buf[len-1] = '\n';
364                 }
365         }
366
367         rwrite(code, buf, len);
368 }
369
370 /* This is like rprintf, but it also tries to print some
371  * representation of the error code.  Normally errcode = errno.
372  *
373  * Unlike rprintf, this always adds a newline and there should not be
374  * one in the format string.
375  *
376  * Note that since strerror might involve dynamically loading a
377  * message catalog we need to call it once before chroot-ing. */
378 void rsyserr(enum logcode code, int errcode, const char *format, ...)
379 {
380         va_list ap;
381         char buf[BIGPATHBUFLEN];
382         size_t len;
383
384         strcpy(buf, RSYNC_NAME ": ");
385         len = (sizeof RSYNC_NAME ": ") - 1;
386
387         va_start(ap, format);
388         len += vsnprintf(buf + len, sizeof buf - len, format, ap);
389         va_end(ap);
390
391         if (len < sizeof buf) {
392                 len += snprintf(buf + len, sizeof buf - len,
393                                 ": %s (%d)\n", strerror(errcode), errcode);
394         }
395         if (len >= sizeof buf)
396                 exit_cleanup(RERR_MESSAGEIO);
397
398         rwrite(code, buf, len);
399 }
400
401 void rflush(enum logcode code)
402 {
403         FILE *f = NULL;
404
405         if (am_daemon) {
406                 return;
407         }
408
409         if (code == FLOG) {
410                 return;
411         }
412
413         if (code == FERROR) {
414                 f = stderr;
415         }
416
417         if (code == FINFO) {
418                 if (am_server)
419                         f = stderr;
420                 else
421                         f = stdout;
422         }
423
424         if (!f) exit_cleanup(RERR_MESSAGEIO);
425         fflush(f);
426 }
427
428 /* a generic logging routine for send/recv, with parameter
429  * substitiution */
430 static void log_formatted(enum logcode code, char *format, char *op,
431                           struct file_struct *file, struct stats *initial_stats,
432                           int iflags, char *hlink)
433 {
434         char buf[MAXPATHLEN+1024], buf2[MAXPATHLEN], fmt[32];
435         char *p, *s, *n;
436         size_t len, total;
437         int64 b;
438
439         *fmt = '%';
440
441         /* We expand % codes one by one in place in buf.  We don't
442          * copy in the terminating null of the inserted strings, but
443          * rather keep going until we reach the null of the format. */
444         total = strlcpy(buf, format, sizeof buf);
445         if (total > MAXPATHLEN) {
446                 rprintf(FERROR, "log-format string is WAY too long!\n");
447                 exit_cleanup(RERR_MESSAGEIO);
448         }
449         buf[total++] = '\n';
450         buf[total] = '\0';
451
452         for (p = buf; (p = strchr(p, '%')) != NULL; ) {
453                 s = p++;
454                 n = fmt + 1;
455                 if (*p == '-')
456                         *n++ = *p++;
457                 while (isdigit(*(uchar*)p) && n - fmt < (int)(sizeof fmt) - 8)
458                         *n++ = *p++;
459                 if (!*p)
460                         break;
461                 *n = '\0';
462                 n = NULL;
463
464                 switch (*p) {
465                 case 'h':
466                         if (am_daemon)
467                                 n = client_name(0);
468                         break;
469                 case 'a':
470                         if (am_daemon)
471                                 n = client_addr(0);
472                         break;
473                 case 'l':
474                         strlcat(fmt, ".0f", sizeof fmt);
475                         snprintf(buf2, sizeof buf2, fmt,
476                                  (double)file->length);
477                         n = buf2;
478                         break;
479                 case 'U':
480                         strlcat(fmt, "ld", sizeof fmt);
481                         snprintf(buf2, sizeof buf2, fmt,
482                                  (long)file->uid);
483                         n = buf2;
484                         break;
485                 case 'G':
486                         if (file->gid == GID_NONE)
487                                 n = "DEFAULT";
488                         else {
489                                 strlcat(fmt, "ld", sizeof fmt);
490                                 snprintf(buf2, sizeof buf2, fmt,
491                                          (long)file->gid);
492                                 n = buf2;
493                         }
494                         break;
495                 case 'p':
496                         strlcat(fmt, "ld", sizeof fmt);
497                         snprintf(buf2, sizeof buf2, fmt,
498                                  (long)getpid());
499                         n = buf2;
500                         break;
501                 case 'M':
502                         n = timestring(file->modtime);
503                         {
504                                 char *cp = n;
505                                 while ((cp = strchr(cp, ' ')) != NULL)
506                                         *cp = '-';
507                         }
508                         break;
509                 case 'B':
510                         n = buf2 + MAXPATHLEN - PERMSTRING_SIZE;
511                         permstring(n - 1, file->mode); /* skip the type char */
512                         break;
513                 case 'o':
514                         n = op;
515                         break;
516                 case 'f':
517                         n = f_name(file, NULL);
518                         if (am_sender && file->dir.root) {
519                                 pathjoin(buf2, sizeof buf2,
520                                          file->dir.root, n);
521                                 clean_fname(buf2, 0);
522                                 if (fmt[1])
523                                         strlcpy(n, buf2, MAXPATHLEN);
524                                 else
525                                         n = buf2;
526                         } else
527                                 clean_fname(n, 0);
528                         if (*n == '/')
529                                 n++;
530                         break;
531                 case 'n':
532                         n = f_name(file, NULL);
533                         if (S_ISDIR(file->mode))
534                                 strlcat(n, "/", MAXPATHLEN);
535                         break;
536                 case 'L':
537                         if (hlink && *hlink) {
538                                 n = hlink;
539                                 strcpy(buf2, " => ");
540                         } else if (S_ISLNK(file->mode) && file->u.link) {
541                                 n = file->u.link;
542                                 strcpy(buf2, " -> ");
543                         } else {
544                                 n = "";
545                                 if (!fmt[1])
546                                         break;
547                                 strcpy(buf2, "    ");
548                         }
549                         strlcat(fmt, "s", sizeof fmt);
550                         snprintf(buf2 + 4, sizeof buf2 - 4, fmt, n);
551                         n = buf2;
552                         break;
553                 case 'm':
554                         n = lp_name(module_id);
555                         break;
556                 case 't':
557                         n = timestring(time(NULL));
558                         break;
559                 case 'P':
560                         n = lp_path(module_id);
561                         break;
562                 case 'u':
563                         n = auth_user;
564                         break;
565                 case 'b':
566                         if (am_sender) {
567                                 b = stats.total_written -
568                                         initial_stats->total_written;
569                         } else {
570                                 b = stats.total_read -
571                                         initial_stats->total_read;
572                         }
573                         strlcat(fmt, ".0f", sizeof fmt);
574                         snprintf(buf2, sizeof buf2, fmt, (double)b);
575                         n = buf2;
576                         break;
577                 case 'c':
578                         if (!am_sender) {
579                                 b = stats.total_written -
580                                         initial_stats->total_written;
581                         } else {
582                                 b = stats.total_read -
583                                         initial_stats->total_read;
584                         }
585                         strlcat(fmt, ".0f", sizeof fmt);
586                         snprintf(buf2, sizeof buf2, fmt, (double)b);
587                         n = buf2;
588                         break;
589                 case 'i':
590                         if (iflags & ITEM_DELETED) {
591                                 n = "*deleting";
592                                 break;
593                         }
594                         n = buf2 + MAXPATHLEN - 32;
595                         n[0] = iflags & ITEM_LOCAL_CHANGE
596                               ? iflags & ITEM_XNAME_FOLLOWS ? 'h' : 'c'
597                              : !(iflags & ITEM_TRANSFER) ? '.'
598                              : !local_server && *op == 's' ? '<' : '>';
599                         n[1] = S_ISDIR(file->mode) ? 'd'
600                              : IS_SPECIAL(file->mode) ? 'S'
601                              : IS_DEVICE(file->mode) ? 'D'
602                              : S_ISLNK(file->mode) ? 'L' : 'f';
603                         n[2] = !(iflags & ITEM_REPORT_CHECKSUM) ? '.' : 'c';
604                         n[3] = !(iflags & ITEM_REPORT_SIZE) ? '.' : 's';
605                         n[4] = !(iflags & ITEM_REPORT_TIME) ? '.'
606                              : !preserve_times || S_ISLNK(file->mode) ? 'T' : 't';
607                         n[5] = !(iflags & ITEM_REPORT_PERMS) ? '.' : 'p';
608                         n[6] = !(iflags & ITEM_REPORT_OWNER) ? '.' : 'o';
609                         n[7] = !(iflags & ITEM_REPORT_GROUP) ? '.' : 'g';
610                         n[8] = '\0';
611
612                         if (iflags & (ITEM_IS_NEW|ITEM_MISSING_DATA)) {
613                                 char ch = iflags & ITEM_IS_NEW ? '+' : '?';
614                                 int i;
615                                 for (i = 2; n[i]; i++)
616                                         n[i] = ch;
617                         } else if (n[0] == '.' || n[0] == 'h'
618                                 || (n[0] == 'c' && n[1] == 'f')) {
619                                 int i;
620                                 for (i = 2; n[i]; i++) {
621                                         if (n[i] != '.')
622                                                 break;
623                                 }
624                                 if (!n[i]) {
625                                         for (i = 2; n[i]; i++)
626                                                 n[i] = ' ';
627                                 }
628                         }
629                         break;
630                 }
631
632                 /* "n" is the string to be inserted in place of this % code. */
633                 if (!n)
634                         continue;
635                 if (n != buf2 && fmt[1]) {
636                         strlcat(fmt, "s", sizeof fmt);
637                         snprintf(buf2, sizeof buf2, fmt, n);
638                         n = buf2;
639                 }
640                 len = strlen(n);
641
642                 /* Subtract the length of the escape from the string's size. */
643                 total -= p - s + 1;
644
645                 if (len + total >= (size_t)sizeof buf) {
646                         rprintf(FERROR,
647                                 "buffer overflow expanding %%%c -- exiting\n",
648                                 p[0]);
649                         exit_cleanup(RERR_MESSAGEIO);
650                 }
651
652                 /* Shuffle the rest of the string along to make space for n */
653                 if (len != (size_t)(p - s + 1))
654                         memmove(s + len, p + 1, total - (s - buf) + 1);
655                 total += len;
656
657                 /* Insert the contents of string "n", but NOT its null. */
658                 if (len)
659                         memcpy(s, n, len);
660
661                 /* Skip over inserted string; continue looking */
662                 p = s + len;
663         }
664
665         rwrite(code, buf, total);
666 }
667
668 /* Return 1 if the format escape is in the log-format string (e.g. look for
669  * the 'b' in the "%9b" format escape). */
670 int log_format_has(const char *format, char esc)
671 {
672         const char *p;
673
674         if (!format)
675                 return 0;
676
677         for (p = format; (p = strchr(p, '%')) != NULL; ) {
678                 if (*++p == '-')
679                         p++;
680                 while (isdigit(*(uchar*)p))
681                         p++;
682                 if (!*p)
683                         break;
684                 if (*p == esc)
685                         return 1;
686         }
687         return 0;
688 }
689
690 /* log the transfer of a file */
691 void log_item(struct file_struct *file, struct stats *initial_stats,
692               int iflags, char *hlink)
693 {
694         char *s_or_r = am_sender ? "send" : "recv";
695
696         if (lp_transfer_logging(module_id)) {
697                 log_formatted(FLOG, lp_log_format(module_id), s_or_r,
698                               file, initial_stats, iflags, hlink);
699         } else if (log_format && !am_server) {
700                 log_formatted(FNAME, log_format, s_or_r,
701                               file, initial_stats, iflags, hlink);
702         }
703 }
704
705 void maybe_log_item(struct file_struct *file, int iflags, int itemizing,
706                     char *buf)
707 {
708         int significant_flags = iflags & SIGNIFICANT_ITEM_FLAGS;
709         int see_item = itemizing && (significant_flags || *buf
710                 || log_format_has_i > 1 || (verbose > 1 && log_format_has_i));
711         int local_change = iflags & ITEM_LOCAL_CHANGE && significant_flags;
712         if (am_server) {
713                 if (am_daemon && !dry_run && see_item)
714                         log_item(file, &stats, iflags, buf);
715         } else if (see_item || local_change || *buf
716             || (S_ISDIR(file->mode) && significant_flags))
717                 log_item(file, &stats, iflags, buf);
718 }
719
720 void log_delete(char *fname, int mode)
721 {
722         static struct file_struct file;
723         int len = strlen(fname);
724         char *fmt;
725
726         file.mode = mode;
727         file.basename = fname;
728
729         if (!verbose && !log_format)
730                 ;
731         else if (am_server && protocol_version >= 29 && len < MAXPATHLEN) {
732                 if (S_ISDIR(mode))
733                         len++; /* directories include trailing null */
734                 send_msg(MSG_DELETED, fname, len);
735         } else {
736                 fmt = log_format_has_o_or_i ? log_format : "deleting %n";
737                 log_formatted(FCLIENT, fmt, "del.", &file, &stats,
738                               ITEM_DELETED, NULL);
739         }
740
741         if (!am_daemon || dry_run || !lp_transfer_logging(module_id))
742                 return;
743
744         fmt = daemon_log_format_has_o_or_i ? lp_log_format(module_id) : "deleting %n";
745         log_formatted(FLOG, fmt, "del.", &file, &stats, ITEM_DELETED, NULL);
746 }
747
748 /*
749  * Called when the transfer is interrupted for some reason.
750  *
751  * Code is one of the RERR_* codes from errcode.h, or terminating
752  * successfully.
753  */
754 void log_exit(int code, const char *file, int line)
755 {
756         if (code == 0) {
757                 rprintf(FLOG,"sent %.0f bytes  received %.0f bytes  total size %.0f\n",
758                         (double)stats.total_written,
759                         (double)stats.total_read,
760                         (double)stats.total_size);
761         } else {
762                 const char *name;
763
764                 name = rerr_name(code);
765                 if (!name)
766                         name = "unexplained error";
767
768                 /* VANISHED is not an error, only a warning */
769                 if (code == RERR_VANISHED) {
770                         rprintf(FINFO, "rsync warning: %s (code %d) at %s(%d) [%s]\n", 
771                                 name, code, file, line, who_am_i());
772                 } else {
773                         rprintf(FERROR, "rsync error: %s (code %d) at %s(%d) [%s]\n",
774                                 name, code, file, line, who_am_i());
775                 }
776         }
777 }