Imported Upstream version 2.5.0p2
[debian/amanda] / server-src / reporter.c
index a28d5b49a80830f1009d589ca57991c89cc4f45b..43cf3fe6f474f598e1f84a1151398817e48afc07 100644 (file)
@@ -25,7 +25,7 @@
  *                        University of Maryland at College Park
  */
 /*
- * $Id: reporter.c,v 1.44.2.17.4.6.2.16.2.6 2005/03/29 16:35:11 martinea Exp $
+ * $Id: reporter.c,v 1.105 2006/03/09 16:51:42 martinea Exp $
  *
  * nightly Amanda Report generator
  */
@@ -70,59 +70,70 @@ typedef struct repdata_s {
     char *datestamp;
     timedata_t taper;
     timedata_t dumper;
+    timedata_t chunker;
     int level;
     struct repdata_s *next;
 } repdata_t;
 
 #define data(dp) ((repdata_t *)(dp)->up)
 
-struct cumulative_stats {
-    int dumpdisks, tapedisks;
+static struct cumulative_stats {
+    int dumpdisks, tapedisks, tapechunks;
     double taper_time, dumper_time;
     double outsize, origsize, tapesize;
     double coutsize, corigsize;                        /* compressed dump only */
 } stats[3];
 
-int dumpdisks[10], tapedisks[10];      /* by-level breakdown of disk count */
+static int dumpdisks[10], tapedisks[10], tapechunks[10];       /* by-level breakdown of disk count */
 
 typedef struct taper_s {
     char *label;
     double taper_time;
     double coutsize, corigsize;
-    int tapedisks;
+  int tapedisks, tapechunks;
     struct taper_s *next;
 } taper_t;
 
-taper_t *stats_by_tape = NULL;
-taper_t *current_tape = NULL;
+static taper_t *stats_by_tape = NULL;
+static taper_t *current_tape = NULL;
 
-float total_time, startup_time, planner_time;
+typedef struct strange_s {
+    char *hostname;
+    char *diskname;
+    int  level;
+    char *str;
+    struct strange_s *next;
+} strange_t;
+
+static strange_t *first_strange=NULL, *last_strange=NULL;
+
+static float total_time, startup_time, planner_time;
 
 /* count files to tape */
-int tapefcount = 0;
+static int tapefcount = 0;
 
-char *run_datestamp;
-char *today_datestamp;
-char *tape_labels = NULL;
-int last_run_tapes = 0;
+static char *run_datestamp;
+static char *today_datestamp;
+static char *tape_labels = NULL;
+static int last_run_tapes = 0;
 static int degraded_mode = 0; /* defined in driverio too */
-int normal_run = 0;
-int amflush_run = 0;
-int got_finish = 0;
+static int normal_run = 0;
+static int amflush_run = 0;
+static int got_finish = 0;
 
-char *tapestart_error = NULL;
+static char *tapestart_error = NULL;
 
-FILE *logfile, *mailf;
+static FILE *logfile, *mailf;
 
-FILE *postscript;
-char *printer;
+static FILE *postscript;
+static char *printer;
 
-disklist_t *diskq;
-disklist_t sortq;
+static disklist_t diskq;
+static disklist_t sortq;
 
-line_t *errsum = NULL;
-line_t *errdet = NULL;
-line_t *notes = NULL;
+static line_t *errsum = NULL;
+static line_t *errdet = NULL;
+static line_t *notes = NULL;
 
 static char MaxWidthsRequested = 0;    /* determined via config data */
 
@@ -130,38 +141,45 @@ char *displayunit;
 long int unitdivisor;
 
 /* local functions */
-int contline_next P((void));
-void addline P((line_t **lp, char *str));
-void usage P((void));
+static int contline_next P((void));
+static void addline P((line_t **lp, char *str));
+static void usage P((void));
 int main P((int argc, char **argv));
 
-void copy_template_file P((char *lbl_templ));
-void do_postscript_output P((void));
-void handle_start P((void));
-void handle_finish P((void));
-void handle_note P((void));
-void handle_summary P((void));
-void handle_stats P((void));
-void handle_error P((void));
-void handle_disk P((void));
-repdata_t *handle_success P((void));
-void handle_strange P((void));
-void handle_failed P((void));
-void generate_missing P((void));
-void output_tapeinfo P((void));
-void output_lines P((line_t *lp, FILE *f));
-void output_stats P((void));
-void output_summary P((void));
-void sort_disks P((void));
-int sort_by_time P((disk_t *a, disk_t *b));
-int sort_by_name P((disk_t *a, disk_t *b));
-void bogus_line P((void));
-char *nicedate P((int datestamp));
+static void copy_template_file P((char *lbl_templ));
+static void do_postscript_output P((void));
+static void handle_start P((void));
+static void handle_finish P((void));
+static void handle_note P((void));
+static void handle_summary P((void));
+static void handle_stats P((void));
+static void handle_error P((void));
+static void handle_disk P((void));
+static repdata_t *handle_success P((logtype_t logtype));
+static repdata_t *handle_chunk P((void));
+static void handle_partial P((void));
+static void handle_strange P((void));
+static void handle_failed P((void));
+static void generate_missing P((void));
+static void output_tapeinfo P((void));
+static void output_lines P((line_t *lp, FILE *f));
+static void output_stats P((void));
+static void output_summary P((void));
+static void output_strange P((void));
+static void sort_disks P((void));
+static int sort_by_name P((disk_t *a, disk_t *b));
+static void bogus_line P((void));
+static char *nicedate P((int datestamp));
 static char *prefix P((char *host, char *disk, int level));
-repdata_t *find_repdata P((disk_t *dp, char *datestamp, int level));
+static char *prefixstrange P((char *host, char *disk, int level, int len_host, int len_disk));
+static void addtostrange P((char *host, char *disk, int level, char *str));
+static repdata_t *find_repdata P((disk_t *dp, char *datestamp, int level));
 
 
-static int ColWidth(int From, int To) {
+static int
+ColWidth(From, To)
+    int From, To;
+{
     int i, Width= 0;
     for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
        Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
@@ -169,7 +187,10 @@ static int ColWidth(int From, int To) {
     return Width;
 }
 
-static char *Rule(int From, int To) {
+static char *
+Rule(From, To)
+    int From, To;
+{
     int i, ThisLeng;
     int Leng= ColWidth(0, ColumnDataCount());
     char *RuleSpace= alloc(Leng+1);
@@ -182,7 +203,11 @@ static char *Rule(int From, int To) {
     return RuleSpace;
 }
 
-static char *TextRule(int From, int To, char *s) {
+static char *
+TextRule(From, To, s)
+    int From, To;
+    char *s;
+{
     ColumnInfo *cd= &ColumnData[From];
     int leng, nbrules, i, txtlength;
     int RuleSpaceSize= ColWidth(0, ColumnDataCount());
@@ -191,8 +216,8 @@ static char *TextRule(int From, int To, char *s) {
     leng= strlen(s);
     if(leng >= (RuleSpaceSize - cd->PrefixSpace))
        leng = RuleSpaceSize - cd->PrefixSpace - 1;
-    ap_snprintf(RuleSpace, RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "", 
-               leng, leng, s);
+    snprintf(RuleSpace, RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "", 
+            leng, leng, s);
     txtlength = cd->PrefixSpace + leng + 1;
     nbrules = ColWidth(From,To) - txtlength;
     for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
@@ -201,21 +226,24 @@ static char *TextRule(int From, int To, char *s) {
     return RuleSpace;
 }
 
-char *sDivZero(float a, float b, int cn) {
+static char *
+sDivZero(a, b, cn)
+    double a, b;
+    int cn;
+{
     ColumnInfo *cd= &ColumnData[cn];
     static char PrtBuf[256];
     if (b == 0.0)
-       ap_snprintf(PrtBuf, sizeof(PrtBuf),
+       snprintf(PrtBuf, sizeof(PrtBuf),
          "%*s", cd->Width, "-- ");
     else
-       ap_snprintf(PrtBuf, sizeof(PrtBuf),
+       snprintf(PrtBuf, sizeof(PrtBuf),
          cd->Format, cd->Width, cd->Precision, a/b);
     return PrtBuf;
 }
 
-
-
-int contline_next()
+static int
+contline_next()
 {
     int ch;
 
@@ -225,9 +253,10 @@ int contline_next()
     return ch == ' ';
 }
 
-void addline(lp, str)
-line_t **lp;
-char *str;
+static void
+addline(lp, str)
+    line_t **lp;
+    char *str;
 {
     line_t *new, *p, *q;
 
@@ -242,14 +271,16 @@ char *str;
     else q->next = new;
 }
 
-void usage()
+static void
+usage()
 {
     error("Usage: amreport conf [-f output-file] [-l logfile] [-p postscript-file]");
 }
 
-int main(argc, argv)
-int argc;
-char **argv;
+int
+main(argc, argv)
+    int argc;
+    char **argv;
 {
     char *conffile;
     char *conf_diskfile;
@@ -257,7 +288,7 @@ char **argv;
     char *conf_infofile;
     char *logfname, *psfname, *outfname, *subj_str = NULL;
     tapetype_t *tp;
-    int fd, opt;
+    int opt;
     unsigned long malloc_hist_1, malloc_size_1;
     unsigned long malloc_hist_2, malloc_size_2;
     char *mail_cmd = NULL, *printer_cmd = NULL;
@@ -267,18 +298,13 @@ char **argv;
     char *errstr = NULL;
     int cn;
 
-    for(fd = 3; fd < FD_SETSIZE; fd++) {
-       /*
-        * Make sure nobody spoofs us with a lot of extra open files
-        * that would cause an open we do to get a very high file
-        * descriptor, which in turn might be used as an index into
-        * an array (e.g. an fd_set).
-        */
-       close(fd);
-    }
+    safe_fd(-1, 0);
 
     set_pname("amreport");
 
+    /* Don't die when child closes pipe */
+    signal(SIGPIPE, SIG_IGN);
+
     malloc_size_1 = malloc_inuse(&malloc_hist_1);
 
     /* Process options */
@@ -376,7 +402,7 @@ char **argv;
     } else {
        conf_diskfile = stralloc2(config_dir, conf_diskfile);
     }
-    if((diskq = read_diskfile(conf_diskfile)) == NULL) {
+    if(read_diskfile(conf_diskfile, &diskq) < 0) {
        error("could not load disklist \"%s\"", conf_diskfile);
     }
     amfree(conf_diskfile);
@@ -472,7 +498,10 @@ char **argv;
 
        case L_DISK:    handle_disk(); break;
 
-       case L_SUCCESS: handle_success(); break;
+       case L_SUCCESS: handle_success(curlog); break;
+       case L_CHUNKSUCCESS: handle_success(curlog); break;
+       case L_CHUNK:   handle_chunk(); break;
+       case L_PARTIAL: handle_partial(); break;
        case L_STRANGE: handle_strange(); break;
        case L_FAIL:    handle_failed(); break;
 
@@ -587,9 +616,10 @@ char **argv;
 
     output_tapeinfo();
 
-    if(errsum) {
+    if(first_strange || errsum) {
        fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
-       output_lines(errsum, mailf);
+       if(first_strange) output_strange();
+       if(errsum) output_lines(errsum, mailf);
     }
     fputs("\n\n", mailf);
 
@@ -683,8 +713,10 @@ char **argv;
            fprintf((fp), "%7.1f",q);       \
     } while(0)
 
-void output_stats()
+static void
+output_stats()
 {
+    double idle_time;
     tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
     int tapesize, marksize, lv, first;
 
@@ -693,6 +725,7 @@ void output_stats()
 
     stats[2].dumpdisks   = stats[0].dumpdisks   + stats[1].dumpdisks;
     stats[2].tapedisks   = stats[0].tapedisks   + stats[1].tapedisks;
+    stats[2].tapechunks  = stats[0].tapechunks  + stats[1].tapechunks;
     stats[2].outsize     = stats[0].outsize     + stats[1].outsize;
     stats[2].origsize    = stats[0].origsize    + stats[1].origsize;
     stats[2].tapesize    = stats[0].tapesize    + stats[1].tapesize;
@@ -704,6 +737,9 @@ void output_stats()
     if(!got_finish)    /* no driver finish line, estimate total run time */
        total_time = stats[2].taper_time + planner_time;
 
+    idle_time = (total_time - startup_time) - stats[2].taper_time;
+    if(idle_time < 0) idle_time = 0.0;
+
     fprintf(mailf,"STATISTICS:\n");
     fprintf(mailf,
            "                          Total       Full      Incr.\n");
@@ -775,11 +811,11 @@ void output_stats()
            mb(stats[1].tapesize));
 
     fprintf(mailf, "Tape Used (%%)             ");
-    divzero(mailf, pct(stats[2].tapesize+marksize*stats[2].tapedisks),tapesize);
+    divzero(mailf, pct(stats[2].tapesize+marksize*(stats[2].tapedisks+stats[2].tapechunks)),tapesize);
     fputs("      ", mailf);
-    divzero(mailf, pct(stats[0].tapesize+marksize*stats[0].tapedisks),tapesize);
+    divzero(mailf, pct(stats[0].tapesize+marksize*(stats[0].tapedisks+stats[0].tapechunks)),tapesize);
     fputs("      ", mailf);
-    divzero(mailf, pct(stats[1].tapesize+marksize*stats[1].tapedisks),tapesize);
+    divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),tapesize);
 
     if(stats[1].tapedisks > 0) fputs("   (level:#disks ...)", mailf);
     putc('\n', mailf);
@@ -799,6 +835,24 @@ void output_stats()
     }
     putc('\n', mailf);
 
+    if(stats[1].tapechunks > 0) fputs("   (level:#chunks ...)", mailf);
+    putc('\n', mailf);
+
+    fprintf(mailf,
+           "Chunks Taped               %4d       %4d       %4d",
+           stats[2].tapechunks, stats[0].tapechunks, stats[1].tapechunks);
+
+    if(stats[1].tapechunks > 0) {
+       first = 1;
+       for(lv = 1; lv < 10; lv++) if(tapechunks[lv]) {
+           fputs(first?"   (":" ", mailf);
+           first = 0;
+           fprintf(mailf, "%d:%d", lv, tapechunks[lv]);
+       }
+       putc(')', mailf);
+    }
+    putc('\n', mailf);
+
     fprintf(mailf, "Avg Tp Write Rate (k/s) ");
     divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
     fputs("    ", mailf);
@@ -810,7 +864,7 @@ void output_stats()
     if(stats_by_tape) {
        int label_length = strlen(stats_by_tape->label) + 5;
        fprintf(mailf,"\nUSAGE BY TAPE:\n");
-       fprintf(mailf,"  %-*s  Time      Size      %%    Nb\n",
+       fprintf(mailf,"  %-*s  Time      Size      %%    Nb    Nc\n",
                label_length, "Label");
        for(current_tape = stats_by_tape; current_tape != NULL;
            current_tape = current_tape->next) {
@@ -818,9 +872,10 @@ void output_stats()
            fprintf(mailf, " %2d:%02d", hrmn(current_tape->taper_time));
            fprintf(mailf, " %8.0f%s  ", du(current_tape->coutsize), displayunit);
            divzero(mailf, pct(current_tape->coutsize + 
-                              marksize * current_tape->tapedisks),
+                              marksize * (current_tape->tapedisks+current_tape->tapechunks)),
                           tapesize);
-           fprintf(mailf, "  %4d\n", current_tape->tapedisks);
+           fprintf(mailf, "  %4d", current_tape->tapedisks);
+           fprintf(mailf, "  %4d\n", current_tape->tapechunks);
        }
     }
 
@@ -828,7 +883,8 @@ void output_stats()
 
 /* ----- */
 
-void output_tapeinfo()
+static void
+output_tapeinfo()
 {
     tape_t *tp, *lasttp;
     int run_tapes;
@@ -858,10 +914,10 @@ void output_tapeinfo()
 
     run_tapes = getconf_int(CNF_RUNTAPES);
 
-    if (run_tapes <= 1)
+    if (run_tapes == 1)
        fputs("The next tape Amanda expects to use is: ", mailf);
-    else
-       fprintf(mailf, "The next %d tapes Amanda expects to used are: ",
+    else if(run_tapes > 1)
+       fprintf(mailf, "The next %d tapes Amanda expects to use are: ",
                run_tapes);
     
     while(run_tapes > 0) {
@@ -908,10 +964,29 @@ void output_tapeinfo()
 }
 
 /* ----- */
+static void output_strange()
+{
+    int len_host=0, len_disk=0;
+    strange_t *strange;
+    char *str = NULL;
+
+    for(strange=first_strange; strange != NULL; strange = strange->next) {
+       if(strlen(strange->hostname) > len_host)
+           len_host = strlen(strange->hostname);
+       if(strlen(strange->diskname) > len_disk)
+           len_disk = strlen(strange->diskname);
+    }
+    for(strange=first_strange; strange != NULL; strange = strange->next) {
+       str = vstralloc("  ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
+                       "  ", strange->str, NULL);
+       fprintf(mailf, "%s\n", str);
+    }
+}
 
-void output_lines(lp, f)
-line_t *lp;
-FILE *f;
+static void
+output_lines(lp, f)
+    line_t *lp;
+    FILE *f;
 {
     line_t *next;
 
@@ -927,14 +1002,9 @@ FILE *f;
 
 /* ----- */
 
-int sort_by_time(a, b)
-disk_t *a, *b;
-{
-    return data(b)->dumper.sec - data(a)->dumper.sec;
-}
-
-int sort_by_name(a, b)
-disk_t *a, *b;
+static int
+sort_by_name(a, b)
+    disk_t *a, *b;
 {
     int rc;
 
@@ -943,13 +1013,14 @@ disk_t *a, *b;
     return rc;
 }
 
-void sort_disks()
+static void
+sort_disks()
 {
     disk_t *dp;
 
     sortq.head = sortq.tail = NULL;
-    while(!empty(*diskq)) {
-       dp = dequeue_disk(diskq);
+    while(!empty(diskq)) {
+       dp = dequeue_disk(&diskq);
        if(data(dp) == NULL) { /* create one */
            find_repdata(dp, run_datestamp, 0);
        }
@@ -957,7 +1028,11 @@ void sort_disks()
     }
 }
 
-void CheckStringMax(ColumnInfo *cd, char *s) {
+static void
+CheckStringMax(cd, s)
+    ColumnInfo *cd;
+    char *s;
+{
     if (cd->MaxWidth) {
        int l= strlen(s);
        if (cd->Width < l)
@@ -965,11 +1040,15 @@ void CheckStringMax(ColumnInfo *cd, char *s) {
     }
 }
 
-void CheckIntMax(ColumnInfo *cd, int n) {
+static void
+CheckIntMax(cd, n)
+    ColumnInfo *cd;
+    int n;
+{
     if (cd->MaxWidth) {
        char testBuf[200];
        int l;
-       ap_snprintf(testBuf, sizeof(testBuf),
+       snprintf(testBuf, sizeof(testBuf),
          cd->Format, cd->Width, cd->Precision, n);
        l= strlen(testBuf);
        if (cd->Width < l)
@@ -977,11 +1056,15 @@ void CheckIntMax(ColumnInfo *cd, int n) {
     }
 }
 
-void CheckFloatMax(ColumnInfo *cd, double d) {
+static void
+CheckFloatMax(cd, d)
+    ColumnInfo *cd;
+    double d;
+{
     if (cd->MaxWidth) {
        char testBuf[200];
        int l;
-       ap_snprintf(testBuf, sizeof(testBuf),
+       snprintf(testBuf, sizeof(testBuf),
          cd->Format, cd->Width, cd->Precision, d);
        l= strlen(testBuf);
        if (cd->Width < l)
@@ -1000,7 +1083,9 @@ static int DumpRate;
 static int TapeTime;
 static int TapeRate;
 
-void CalcMaxWidth() {
+static void
+CalcMaxWidth()
+{
     /* we have to look for columspec's, that require the recalculation.
      * we do here the same loops over the sortq as is done in
      * output_summary. So, if anything is changed there, we have to
@@ -1022,7 +1107,8 @@ void CalcMaxWidth() {
                repdata->taper.result == L_BOGUS)
                continue;
            CheckIntMax(&ColumnData[Level], repdata->level);
-           if(repdata->dumper.result == L_SUCCESS) {
+            if(repdata->dumper.result == L_SUCCESS || 
+                   repdata->dumper.result == L_CHUNKSUCCESS) {
                CheckFloatMax(&ColumnData[OrigKB], du(repdata->dumper.origsize));
                CheckFloatMax(&ColumnData[OutKB], du(repdata->dumper.outsize));
                if(dp->compress == COMP_NONE)
@@ -1033,10 +1119,10 @@ void CalcMaxWidth() {
                        sDivZero(pct(repdata->dumper.outsize), f, Compress));
 
                if(!amflush_run)
-                   ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+                   snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                                "%3d:%02d", mnsc(repdata->dumper.sec));
                else
-                   ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+                   snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                                "N/A ");
                CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
 
@@ -1048,16 +1134,18 @@ void CalcMaxWidth() {
                CheckStringMax(cd, "FAILED");
                continue;
            }
-           if(repdata->taper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer), 
+           if(repdata->taper.result == L_SUCCESS ||
+              repdata->taper.result == L_CHUNKSUCCESS)
+               snprintf(TimeRateBuffer, sizeof(TimeRateBuffer), 
                  "%3d:%02d", mnsc(repdata->taper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                  "N/A ");
            CheckStringMax(cd, TimeRateBuffer);
 
            cd= &ColumnData[TapeRate];
-           if(repdata->taper.result == L_SUCCESS)
+           if(repdata->taper.result == L_SUCCESS ||
+                   repdata->taper.result == L_CHUNKSUCCESS)
                CheckFloatMax(cd, repdata->taper.kps);
            else
                CheckStringMax(cd, "N/A ");
@@ -1066,7 +1154,8 @@ void CalcMaxWidth() {
     }
 }
 
-void output_summary()
+static void
+output_summary()
 {
     disk_t *dp;
     repdata_t *repdata;
@@ -1191,20 +1280,30 @@ void output_summary()
                amfree(tmp);
                continue;
            }
-           if (repdata->dumper.result == L_FAIL) {
+           if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result  != L_PARTIAL)) {
                fprintf(mailf, "%s\n",
                        tmp=TextRule(OrigKB, TapeRate, "FAILED"));
                amfree(tmp);
                continue;
            }
 
-           if(repdata->dumper.result == L_SUCCESS)
+           if(repdata->dumper.result == L_SUCCESS ||
+              repdata->dumper.result == L_CHUNKSUCCESS)
                origsize = repdata->dumper.origsize;
-           else
+           else if(repdata->taper.result == L_SUCCESS ||
+                   repdata->taper.result == L_PARTIAL)
                origsize = repdata->taper.origsize;
+           else
+               origsize = repdata->chunker.origsize;
 
-           if(repdata->taper.result == L_SUCCESS) 
+           if(repdata->taper.result == L_SUCCESS ||
+              repdata->taper.result == L_PARTIAL ||
+              repdata->taper.result == L_CHUNKSUCCESS)
                outsize  = repdata->taper.outsize;
+           else if(repdata->chunker.result == L_SUCCESS ||
+                   repdata->chunker.result == L_PARTIAL ||
+                   repdata->chunker.result == L_CHUNKSUCCESS)
+               outsize  = repdata->chunker.outsize;
            else
                outsize  = repdata->dumper.outsize;
 
@@ -1234,17 +1333,19 @@ void output_summary()
 
            cd= &ColumnData[DumpTime];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->dumper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+           if(repdata->dumper.result == L_SUCCESS ||
+              repdata->dumper.result == L_CHUNKSUCCESS)
+               snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                  "%3d:%02d", mnsc(repdata->dumper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                  "N/A ");
            fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
 
            cd= &ColumnData[DumpRate];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->dumper.result == L_SUCCESS)
+           if(repdata->dumper.result == L_SUCCESS ||
+                   repdata->dumper.result == L_CHUNKSUCCESS)
                fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
            else
                fprintf(mailf, "%*s", cd->Width, "N/A ");
@@ -1258,34 +1359,45 @@ void output_summary()
                continue;
            }
 
-           if(repdata->taper.result == L_SUCCESS)
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+           if(repdata->taper.result == L_SUCCESS || 
+              repdata->taper.result == L_PARTIAL ||
+              repdata->taper.result == L_CHUNKSUCCESS)
+               snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                  "%3d:%02d", mnsc(repdata->taper.sec));
            else
-               ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
+               snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
                  "N/A ");
            fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
 
            cd= &ColumnData[TapeRate];
            fprintf(mailf, "%*s", cd->PrefixSpace, "");
-           if(repdata->taper.result == L_SUCCESS)
+           if(repdata->taper.result == L_SUCCESS || 
+              repdata->taper.result == L_PARTIAL ||
+              repdata->taper.result == L_CHUNKSUCCESS)
                fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
            else
                fprintf(mailf, "%*s", cd->Width, "N/A ");
+
+           if(repdata->chunker.result == L_PARTIAL ||
+              repdata->taper.result == L_PARTIAL) {
+               fprintf(mailf, " PARTIAL");
+           }
            fputc('\n', mailf);
        }
       }
     }
 }
 
-void bogus_line()
+static void
+bogus_line()
 {
     printf("line %d of log is bogus\n", curlinenum);
 }
 
 
-char *nicedate(datestamp)
-int datestamp;
+static char *
+nicedate(datestamp)
+    int datestamp;
 /*
  * Formats an integer of the form YYYYMMDD into the string
  * "Monthname DD, YYYY".  A pointer to the statically allocated string
@@ -1304,12 +1416,13 @@ int datestamp;
     day   = datestamp % 100;
     month = (datestamp / 100) % 100;
 
-    ap_snprintf(nice, sizeof(nice), "%s %d, %d", months[month], day, year);
+    snprintf(nice, sizeof(nice), "%s %d, %d", months[month], day, year);
 
     return nice;
 }
 
-void handle_start()
+static void
+handle_start()
 {
     static int started = 0;
     char *label;
@@ -1384,6 +1497,7 @@ void handle_start()
        current_tape->coutsize = 0.0;
        current_tape->corigsize = 0.0;
        current_tape->tapedisks = 0;
+       current_tape->tapechunks = 0;
        current_tape->next = NULL;
        tapefcount = 0;
 
@@ -1433,7 +1547,8 @@ void handle_start()
 }
 
 
-void handle_finish()
+static void
+handle_finish()
 {
     char *s;
     int ch;
@@ -1491,7 +1606,8 @@ void handle_finish()
     }
 }
 
-void handle_stats()
+static void
+handle_stats()
 {
     char *s;
     int ch;
@@ -1524,7 +1640,8 @@ void handle_stats()
 }
 
 
-void handle_note()
+static void
+handle_note()
 {
     char *str = NULL;
 
@@ -1536,7 +1653,8 @@ void handle_note()
 
 /* ----- */
 
-void handle_error()
+static void
+handle_error()
 {
     char *s = NULL, *nl;
     int ch;
@@ -1547,23 +1665,22 @@ void handle_error()
 
        skip_whitespace(s, ch);
 #define sc "no-tape"
-       if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
-           bogus_line();
-           return;
-       }
-       s += sizeof(sc)-1;
-       ch = s[-1];
+       if(ch != '\0' && strncmp(s - 1, sc, sizeof(sc)-1) == 0) {
+           s += sizeof(sc)-1;
+           ch = s[-1];
 #undef sc
 
-       skip_whitespace(s, ch);
-       if(ch != '\0') {
-           if((nl = strchr(s - 1, '\n')) != NULL) {
-               *nl = '\0';
+           skip_whitespace(s, ch);
+           if(ch != '\0') {
+               if((nl = strchr(s - 1, '\n')) != NULL) {
+                   *nl = '\0';
+               }
+               tapestart_error = newstralloc(tapestart_error, s - 1);
+               if(nl) *nl = '\n';
+               degraded_mode = 1;
+               return;
            }
-           tapestart_error = newstralloc(tapestart_error, s - 1);
-           if(nl) *nl = '\n';
-           degraded_mode = 1;
-           return;
+           /* else some other tape error, handle like other errors */
        }
        /* else some other tape error, handle like other errors */
     }
@@ -1575,15 +1692,17 @@ void handle_error()
 
 /* ----- */
 
-void handle_summary()
+static void
+handle_summary()
 {
     bogus_line();
 }
 
 /* ----- */
 
-int nb_disk=0;
-void handle_disk()
+static int nb_disk=0;
+static void
+handle_disk()
 {
     disk_t *dp;
     char *s, *fp;
@@ -1596,7 +1715,7 @@ void handle_disk()
     }
 
     if(nb_disk==0) {
-       for(dp = diskq->head; dp != NULL; dp = dp->next)
+       for(dp = diskq.head; dp != NULL; dp = dp->next)
            dp->todo = 0;
     }
     nb_disk++;
@@ -1618,6 +1737,7 @@ void handle_disk()
     skip_whitespace(s, ch);
     if(ch == '\0') {
        bogus_line();
+       amfree(hostname);
        return;
     }
     fp = s - 1;
@@ -1628,7 +1748,7 @@ void handle_disk()
 
     dp = lookup_disk(hostname, diskname);
     if(dp == NULL) {
-       dp = add_disk(hostname, diskname);
+       dp = add_disk(&diskq, hostname, diskname);
     }
 
     amfree(hostname);
@@ -1636,7 +1756,154 @@ void handle_disk()
     dp->todo = 1;
 }
 
-repdata_t *handle_success()
+/* XXX Just a placeholder, in case we decide to do something with L_CHUNK
+ * log entries.  Right now they're just the equivalent of L_SUCCESS, but only
+ * for a split chunk of the overall dumpfile.
+ */
+static repdata_t *
+handle_chunk()
+{
+    disk_t *dp;
+    float sec, kps, kbytes;
+    timedata_t *sp;
+    int i;
+    char *s, *fp;
+    int ch;
+    char *hostname = NULL;
+    char *diskname = NULL;
+    repdata_t *repdata;
+    int level, chunk;
+    char *datestamp;
+    
+    if(curprog != P_TAPER) {
+       bogus_line();
+       return NULL;
+    }
+    
+    s = curstr;
+    ch = *s++;
+    
+    skip_whitespace(s, ch);
+    if(ch == '\0') {
+       bogus_line();
+       return NULL;
+    }
+    fp = s - 1;
+    skip_non_whitespace(s, ch);
+    s[-1] = '\0';
+    hostname = stralloc(fp);
+    s[-1] = ch;
+    
+    skip_whitespace(s, ch);
+    if(ch == '\0') {
+       bogus_line();
+       amfree(hostname);
+       return NULL;
+    }
+    fp = s - 1;
+    skip_non_whitespace(s, ch);
+    s[-1] = '\0';
+    diskname = stralloc(fp);
+    s[-1] = ch;
+    
+    skip_whitespace(s, ch);
+    if(ch == '\0') {
+       bogus_line();
+       amfree(hostname);
+       amfree(diskname);
+       return NULL;
+    }
+    fp = s - 1;
+    skip_non_whitespace(s, ch);
+    s[-1] = '\0';
+    datestamp = stralloc(fp);
+    s[-1] = ch;
+    
+    level = atoi(datestamp);
+    if(level < 100)  {
+       datestamp = newstralloc(datestamp, run_datestamp);
+    }
+    else {
+       skip_whitespace(s, ch);
+       if(ch == '\0' || sscanf(s - 1, "%d", &chunk) != 1) {
+           bogus_line();
+           amfree(hostname);
+           amfree(diskname);
+           amfree(datestamp);
+           return NULL;
+       }
+       skip_integer(s, ch);
+       
+       skip_whitespace(s, ch);
+       if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
+           bogus_line();
+           amfree(hostname);
+           amfree(diskname);
+           amfree(datestamp);
+           return NULL;
+       }
+       skip_integer(s, ch);
+    }
+    skip_whitespace(s, ch);
+    
+    if(level < 0 || level > 9) {
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    
+    if(sscanf(s - 1,"[sec %f kb %f kps %f", &sec, &kbytes, &kps) != 3)  {
+       bogus_line();
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    
+    
+    dp = lookup_disk(hostname, diskname);
+    if(dp == NULL) {
+       char *str = NULL;
+       
+       str = vstralloc("  ", prefix(hostname, diskname, level),
+                       " ", "ERROR [not in disklist]",
+                       NULL);
+       addline(&errsum, str);
+       amfree(str);
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
+    
+    repdata = find_repdata(dp, datestamp, level);
+    
+    sp = &(repdata->taper);
+    
+    i = level > 0;
+    
+    amfree(hostname);
+    amfree(diskname);
+    amfree(datestamp);
+    
+    if(current_tape == NULL) {
+       error("current_tape == NULL");
+    }
+    if (sp->filenum == 0) {
+       sp->filenum = ++tapefcount;
+       sp->tapelabel = current_tape->label;
+    }
+    tapechunks[level] +=1;
+    stats[i].tapechunks +=1;
+    current_tape->taper_time += sec;
+    current_tape->coutsize += kbytes;
+    current_tape->tapechunks += 1;
+    return repdata;
+}
+
+static repdata_t *
+handle_success(logtype_t logtype)
 {
     disk_t *dp;
     float sec, kps, kbytes, origkb;
@@ -1650,7 +1917,8 @@ repdata_t *handle_success()
     int level;
     char *datestamp;
 
-    if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER) {
+    if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
+       curprog != P_CHUNKER) {
        bogus_line();
        return NULL;
     }
@@ -1709,6 +1977,12 @@ repdata_t *handle_success()
        }
        skip_integer(s, ch);
     }
+    if(level < 0 || level > 9) {
+       amfree(hostname);
+       amfree(diskname);
+       amfree(datestamp);
+       return NULL;
+    }
 
     skip_whitespace(s, ch);
                                /* Planner success messages (for skipped
@@ -1735,13 +2009,7 @@ repdata_t *handle_success()
 
     dp = lookup_disk(hostname, diskname);
     if(dp == NULL) {
-       char *str = NULL;
-
-       str = vstralloc("  ", prefix(hostname, diskname, level),
-                       " ", "ERROR [not in disklist]",
-                       NULL);
-       addline(&errsum, str);
-       amfree(str);
+       addtostrange(hostname, diskname, level, "ERROR [not in disklist]");
        amfree(hostname);
        amfree(diskname);
        amfree(datestamp);
@@ -1760,7 +2028,9 @@ repdata_t *handle_success()
 
     if(curprog == P_TAPER)
        sp = &(repdata->taper);
-    else sp = &(repdata->dumper);
+    else if(curprog == P_DUMPER)
+       sp = &(repdata->dumper);
+    else sp = &(repdata->chunker);
 
     i = level > 0;
 
@@ -1802,8 +2072,17 @@ repdata_t *handle_success()
        tapedisks[level] +=1;
        stats[i].tapedisks +=1;
        stats[i].tapesize += kbytes;
-       current_tape->taper_time += sec;
-       current_tape->coutsize += kbytes;
+       sp->outsize = kbytes;
+       if(repdata->chunker.outsize == 0.0 && repdata->dumper.outsize != 0.0) { /* dump to tape */
+           stats[i].outsize += kbytes;
+           if(dp->compress != COMP_NONE) {
+               stats[i].coutsize += kbytes;
+           }
+       }
+       if (logtype == L_SUCCESS || logtype== L_PARTIAL) {
+           current_tape->taper_time += sec;
+           current_tape->coutsize += kbytes;
+       }
        current_tape->corigsize += origkb;
        current_tape->tapedisks += 1;
     }
@@ -1814,25 +2093,48 @@ repdata_t *handle_success()
            sp->origsize = kbytes;
        }
        else {
-           stats[i].coutsize += kbytes;
            stats[i].corigsize += sp->origsize;
        }
        dumpdisks[level] +=1;
        stats[i].dumpdisks +=1;
        stats[i].origsize += sp->origsize;
-       stats[i].outsize += kbytes;
     }
 
+    if(curprog == P_CHUNKER) {
+       sp->outsize = kbytes;
+       stats[i].outsize += kbytes;
+       if(dp->compress != COMP_NONE) {
+           stats[i].coutsize += kbytes;
+       }
+    }
     return repdata;
 }
 
-void handle_strange()
+static void
+handle_partial()
+{
+    repdata_t *repdata;
+    timedata_t *sp;
+
+    repdata = handle_success(L_PARTIAL);
+
+    if(curprog == P_TAPER)
+       sp = &(repdata->taper);
+    else if(curprog == P_DUMPER)
+       sp = &(repdata->dumper);
+    else sp = &(repdata->chunker);
+
+    sp->result = L_PARTIAL;
+}
+
+static void
+handle_strange()
 {
     char *str = NULL;
     char *strangestr = NULL;
     repdata_t *repdata;
 
-    repdata = handle_success();
+    repdata = handle_success(L_SUCCESS);
 
     addline(&errdet,"");
     str = vstralloc("/-- ", prefix(repdata->disk->host->hostname, 
@@ -1852,16 +2154,15 @@ void handle_strange()
     }
     addline(&errdet,"\\--------");
 
-    str = vstralloc("  ", prefix(repdata->disk->host->hostname, 
-                                repdata->disk->name, repdata->level),
-                   " ", "STRANGE", " ", strangestr,
-                   NULL);
-    addline(&errsum, str);
+    str = vstralloc("STRANGE", " ", strangestr, NULL);
+    addtostrange(repdata->disk->host->hostname, repdata->disk->name, repdata->level,
+                str);
     amfree(str);
     amfree(strangestr);
 }
 
-void handle_failed()
+static void
+handle_failed()
 {
     disk_t *dp;
     char *hostname;
@@ -1936,11 +2237,7 @@ void handle_failed()
 
     dp = lookup_disk(hostname, diskname);
     if(dp == NULL) {
-       str = vstralloc("  ", prefix(hostname, diskname, level),
-                       " ", "ERROR [not in disklist]",
-                       NULL);
-       addline(&errsum, str);
-       amfree(str);
+       addtostrange(hostname, diskname, level, "ERROR [not in disklist]");
     } else {
        repdata = find_repdata(dp, datestamp, level);
 
@@ -1953,11 +2250,8 @@ void handle_failed()
     }
     amfree(datestamp);
 
-    str = vstralloc("  ", prefix(hostname, diskname, level),
-                   " ", "FAILED",
-                   " ", errstr,
-                   NULL);
-    addline(&errsum, str);
+    str = vstralloc("FAILED", " ", errstr, NULL);
+    addtostrange(hostname, diskname, level, str);
     amfree(str);
 
     if(curprog == P_DUMPER) {
@@ -1977,54 +2271,113 @@ void handle_failed()
     return;
 }
 
-void generate_missing()
+
+static void
+generate_missing()
 {
     disk_t *dp;
-    char *str = NULL;
 
-    for(dp = diskq->head; dp != NULL; dp = dp->next) {
+    for(dp = diskq.head; dp != NULL; dp = dp->next) {
        if(dp->todo && data(dp) == NULL) {
-           str = vstralloc("  ", prefix(dp->host->hostname, dp->name, -987),
-                           " ", "RESULTS MISSING",
-                           NULL);
-           addline(&errsum, str);
-           amfree(str);
+           addtostrange(dp->host->hostname, dp->name, -987, "RESULTS MISSING");
        }
     }
 }
 
+
 static char *
 prefix (host, disk, level)
     char *host;
     char *disk;
     int level;
 {
-    char h[10+1];
+    char number[NUM_STR_SIZE];
+    static char *str = NULL;
+
+    snprintf(number, sizeof(number), "%d", level);
+    str = newvstralloc(str,
+                      " ", host ? host : "(host?)",
+                      " ", disk ? disk : "(disk?)",
+                      level != -987 ? " lev " : "",
+                      level != -987 ? number : "",
+                      NULL);
+    return str;
+}
+
+
+static char *
+prefixstrange (host, disk, level, len_host, len_disk)
+    char *host;
+    char *disk;
+    int level;
+    int len_host, len_disk;
+{
+    char *h, *d;
     int l;
     char number[NUM_STR_SIZE];
     static char *str = NULL;
 
-    ap_snprintf(number, sizeof(number), "%d", level);
+    snprintf(number, sizeof(number), "%d", level);
+    h=malloc(len_host+1);
     if(host) {
-       strncpy(h, host, sizeof(h)-1);
+       strncpy(h, host, len_host);
     } else {
-       strncpy(h, "(host?)", sizeof(h)-1);
+       strncpy(h, "(host?)", len_host);
     }
-    h[sizeof(h)-1] = '\0';
-    for(l = strlen(h); l < sizeof(h)-1; l++) {
+    h[len_host] = '\0';
+    for(l = strlen(h); l < len_host; l++) {
        h[l] = ' ';
     }
+    d=malloc(len_disk+1);
+    if(disk) {
+       strncpy(d, disk, len_disk);
+    } else {
+       strncpy(d, "(disk?)", len_disk);
+    }
+    d[len_disk] = '\0';
+    for(l = strlen(d); l < len_disk; l++) {
+       d[l] = ' ';
+    }
     str = newvstralloc(str,
                       h,
-                      " ", disk ? disk : "(disk?)",
-                      level != -987 ? " lev " : "",
+                      "  ", d,
+                      level != -987 ? "  lev " : "",
                       level != -987 ? number : "",
                       NULL);
+    amfree(h);
+    amfree(d);
     return str;
 }
 
-void copy_template_file(lbl_templ)
-char *lbl_templ;
+
+static void
+addtostrange (host, disk, level, str)
+    char *host;
+    char *disk;
+    int  level;
+    char *str;
+{
+    strange_t *strange;
+
+    strange = malloc(sizeof(strange_t));
+    strange->hostname = stralloc(host);
+    strange->diskname = stralloc(disk);
+    strange->level    = level;
+    strange->str      = stralloc(str);
+    strange->next = NULL;
+    if(first_strange == NULL) {
+       first_strange = strange;
+    }
+    else {
+        last_strange->next = strange;
+    }
+    last_strange = strange;
+}
+
+
+static void
+copy_template_file(lbl_templ)
+    char *lbl_templ;
 {
   char buf[BUFSIZ];
   int fd;
@@ -2045,6 +2398,7 @@ char *lbl_templ;
                       NULL);
     handle_error();
     amfree(curstr);
+    amfree(lbl_templ);
     afclose(postscript);
     return;
   }
@@ -2059,6 +2413,7 @@ char *lbl_templ;
                         NULL);
       handle_error();
       amfree(curstr);
+      amfree(lbl_templ);
       afclose(postscript);
       return;
     }
@@ -2073,6 +2428,7 @@ char *lbl_templ;
                       NULL);
     handle_error();
     amfree(curstr);
+    amfree(lbl_templ);
     afclose(postscript);
     return;
   }
@@ -2080,10 +2436,11 @@ char *lbl_templ;
   amfree(lbl_templ);
 }
 
-repdata_t *find_repdata(dp, datestamp, level)
-disk_t *dp;
-char *datestamp;
-int level;
+static repdata_t *
+find_repdata(dp, datestamp, level)
+    disk_t *dp;
+    char *datestamp;
+    int level;
 {
     repdata_t *repdata, *prev;
 
@@ -2111,7 +2468,7 @@ int level;
 }
 
 
-void do_postscript_output()
+static void do_postscript_output()
 {
     tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
     disk_t *dp;
@@ -2142,7 +2499,7 @@ void do_postscript_output()
              mb(current_tape->coutsize));
        fprintf(postscript, "(Tape Used (%%)       ");
        divzero(postscript, pct(current_tape->coutsize + 
-                               marksize * current_tape->tapedisks),
+                               marksize * (current_tape->tapedisks + current_tape->tapechunks)),
                                tapesize);
        fprintf(postscript," %%) DrawStat\n");
        fprintf(postscript, "(Compression Ratio:  ");
@@ -2167,17 +2524,20 @@ void do_postscript_output()
                    continue;
                }
 
-               if(repdata->dumper.result == L_SUCCESS)
+               if(repdata->dumper.result == L_SUCCESS ||
+                  repdata->dumper.result == L_PARTIAL)
                    origsize = repdata->dumper.origsize;
                else
                    origsize = repdata->taper.origsize;
 
-               if(repdata->taper.result == L_SUCCESS) 
+               if(repdata->taper.result == L_SUCCESS ||
+                  repdata->taper.result == L_PARTIAL)
                    outsize  = repdata->taper.outsize;
                else
                    outsize  = repdata->dumper.outsize;
 
-               if (repdata->taper.result == L_SUCCESS) {
+               if (repdata->taper.result == L_SUCCESS ||
+                   repdata->taper.result == L_PARTIAL) {
                    if(origsize != 0.0) {
                        fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0f) (%8.0f) DrawHost\n",
                            dp->host->hostname, dp->name, repdata->level,