2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of U.M. not be used in advertising or
11 * publicity pertaining to distribution of the software without specific,
12 * written prior permission. U.M. makes no representations about the
13 * suitability of this software for any purpose. It is provided "as is"
14 * without express or implied warranty.
16 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: reporter.c,v 1.44.2.17.4.6.2.16 2003/11/26 16:10:23 martinea Exp $
30 * nightly Amanda Report generator
51 /* don't have (or need) a skipped type except internally to reporter */
52 #define L_SKIPPED L_MARKER
54 typedef struct line_s {
59 typedef struct timedata_s {
61 float origsize, outsize;
68 typedef struct repdata_s {
74 struct repdata_s *next;
77 #define data(dp) ((repdata_t *)(dp)->up)
79 struct cumulative_stats {
80 int dumpdisks, tapedisks;
81 double taper_time, dumper_time;
82 double outsize, origsize, tapesize;
83 double coutsize, corigsize; /* compressed dump only */
86 int dumpdisks[10], tapedisks[10]; /* by-level breakdown of disk count */
88 typedef struct taper_s {
91 double coutsize, corigsize;
96 taper_t *stats_by_tape = NULL;
97 taper_t *current_tape = NULL;
99 float total_time, startup_time;
101 /* count files to tape */
105 char *today_datestamp;
106 char *tape_labels = NULL;
107 int last_run_tapes = 0;
108 static int degraded_mode = 0; /* defined in driverio too */
113 char *tapestart_error = NULL;
115 FILE *logfile, *mailf;
123 line_t *errsum = NULL;
124 line_t *errdet = NULL;
125 line_t *notes = NULL;
127 static char MaxWidthsRequested = 0; /* determined via config data */
130 char *hostname = NULL, *diskname = NULL;
133 /* local functions */
134 int contline_next P((void));
135 void addline P((line_t **lp, char *str));
136 void usage P((void));
137 int main P((int argc, char **argv));
139 void copy_template_file P((char *lbl_templ));
140 void do_postscript_output P((void));
141 void handle_start P((void));
142 void handle_finish P((void));
143 void handle_note P((void));
144 void handle_summary P((void));
145 void handle_stats P((void));
146 void handle_error P((void));
147 void handle_disk P((void));
148 repdata_t *handle_success P((void));
149 void handle_strange P((void));
150 void handle_failed P((void));
151 void generate_missing P((void));
152 void output_tapeinfo P((void));
153 void output_lines P((line_t *lp, FILE *f));
154 void output_stats P((void));
155 void output_summary P((void));
156 void sort_disks P((void));
157 int sort_by_time P((disk_t *a, disk_t *b));
158 int sort_by_name P((disk_t *a, disk_t *b));
159 void bogus_line P((void));
160 char *nicedate P((int datestamp));
161 static char *prefix P((char *host, char *disk, int level));
162 repdata_t *find_repdata P((disk_t *dp, char *datestamp, int level));
165 static int ColWidth(int From, int To) {
167 for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
168 Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
173 static char *Rule(int From, int To) {
175 int Leng= ColWidth(0, ColumnDataCount());
176 char *RuleSpace= alloc(Leng+1);
177 ThisLeng= ColWidth(From, To);
178 for (i=0;i<ColumnData[From].PrefixSpace; i++)
180 for (; i<ThisLeng; i++)
182 RuleSpace[ThisLeng]= '\0';
186 static char *TextRule(int From, int To, char *s) {
187 ColumnInfo *cd= &ColumnData[From];
188 int leng, nbrules, i, txtlength;
189 int RuleSpaceSize= ColWidth(0, ColumnDataCount());
190 char *RuleSpace= alloc(RuleSpaceSize), *tmp;
193 if(leng >= (RuleSpaceSize - cd->PrefixSpace))
194 leng = RuleSpaceSize - cd->PrefixSpace - 1;
195 ap_snprintf(RuleSpace, RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "",
197 txtlength = cd->PrefixSpace + leng + 1;
198 nbrules = ColWidth(From,To) - txtlength;
199 for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
205 char *sDivZero(float a, float b, int cn) {
206 ColumnInfo *cd= &ColumnData[cn];
207 static char PrtBuf[256];
209 ap_snprintf(PrtBuf, sizeof(PrtBuf),
210 "%*s", cd->Width, "-- ");
212 ap_snprintf(PrtBuf, sizeof(PrtBuf),
213 cd->Format, cd->Width, cd->Precision, a/b);
229 void addline(lp, str)
235 /* allocate new line node */
236 new = (line_t *) alloc(sizeof(line_t));
238 new->str = stralloc(str);
240 /* add to end of list */
241 for(p = *lp, q = NULL; p != NULL; q = p, p = p->next);
242 if(q == NULL) *lp = new;
248 error("Usage: amreport conf [-f output-file] [-l logfile] [-p postscript-file]");
259 char *logfname, *psfname, *outfname, *subj_str = NULL;
262 unsigned long malloc_hist_1, malloc_size_1;
263 unsigned long malloc_hist_2, malloc_size_2;
264 char *mail_cmd = NULL, *printer_cmd = NULL;
266 char my_cwd[STR_SIZE];
267 char *ColumnSpec = "";
271 for(fd = 3; fd < FD_SETSIZE; fd++) {
273 * Make sure nobody spoofs us with a lot of extra open files
274 * that would cause an open we do to get a very high file
275 * descriptor, which in turn might be used as an index into
276 * an array (e.g. an fd_set).
281 set_pname("amreport");
283 malloc_size_1 = malloc_inuse(&malloc_hist_1);
285 /* Process options */
287 erroutput_type = ERR_INTERACTIVE;
292 if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
293 error("cannot determine current working directory");
297 config_dir = stralloc2(my_cwd, "/");
298 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
299 config_name = stralloc(config_name + 1);
302 if (argv[1][0] == '-') {
306 config_name = stralloc(argv[1]);
307 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
309 while((opt = getopt(argc, argv, "f:l:p:")) != EOF) {
312 if (outfname != NULL) {
313 error("you may specify at most one -f");
315 if (*optarg == '/') {
316 outfname = stralloc(optarg);
318 outfname = vstralloc(my_cwd, "/", optarg, NULL);
322 if (logfname != NULL) {
323 error("you may specify at most one -l");
325 if (*optarg == '/') {
326 logfname = stralloc(optarg);
328 logfname = vstralloc(my_cwd, "/", optarg, NULL);
332 if (psfname != NULL) {
333 error("you may specify at most one -p");
335 if (*optarg == '/') {
336 psfname = stralloc(optarg);
338 psfname = vstralloc(my_cwd, "/", optarg, NULL);
359 printf("You must run amreport with '-f <output file>' because configure\n");
360 printf("didn't find a mailer.\n");
367 /* read configuration files */
369 conffile = stralloc2(config_dir, CONFFILE_NAME);
370 if(read_conffile(conffile)) {
371 error("errors processing config file \"%s\"", conffile);
374 conf_diskfile = getconf_str(CNF_DISKFILE);
375 if (*conf_diskfile == '/') {
376 conf_diskfile = stralloc(conf_diskfile);
378 conf_diskfile = stralloc2(config_dir, conf_diskfile);
380 if((diskq = read_diskfile(conf_diskfile)) == NULL) {
381 error("could not load disklist \"%s\"", conf_diskfile);
383 amfree(conf_diskfile);
384 conf_tapelist = getconf_str(CNF_TAPELIST);
385 if (*conf_tapelist == '/') {
386 conf_tapelist = stralloc(conf_tapelist);
388 conf_tapelist = stralloc2(config_dir, conf_tapelist);
390 if(read_tapelist(conf_tapelist)) {
391 error("could not read tapelist \"%s\"", conf_tapelist);
393 amfree(conf_tapelist);
394 conf_infofile = getconf_str(CNF_INFOFILE);
395 if (*conf_infofile == '/') {
396 conf_infofile = stralloc(conf_infofile);
398 conf_infofile = stralloc2(config_dir, conf_infofile);
400 if(open_infofile(conf_infofile)) {
401 error("could not open info db \"%s\"", conf_infofile);
403 amfree(conf_infofile);
405 today_datestamp = construct_datestamp(NULL);
407 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
408 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
410 curprog = P_REPORTER;
415 ColumnSpec = ""; /* use the default */
416 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
418 curprog = P_REPORTER;
425 for (cn = 0; ColumnData[cn].Name != NULL; cn++) {
426 if (ColumnData[cn].MaxWidth) {
427 MaxWidthsRequested = 1;
435 conf_logdir = getconf_str(CNF_LOGDIR);
436 if (*conf_logdir == '/') {
437 conf_logdir = stralloc(conf_logdir);
439 conf_logdir = stralloc2(config_dir, conf_logdir);
441 logfname = vstralloc(conf_logdir, "/", "log", NULL);
445 if((logfile = fopen(logfname, "r")) == NULL) {
447 curprog = P_REPORTER;
448 curstr = vstralloc("could not open log ",
457 while(logfile && get_logline(logfile)) {
459 case L_START: handle_start(); break;
460 case L_FINISH: handle_finish(); break;
462 case L_INFO: handle_note(); break;
463 case L_WARNING: handle_note(); break;
465 case L_SUMMARY: handle_summary(); break;
466 case L_STATS: handle_stats(); break;
468 case L_ERROR: handle_error(); break;
469 case L_FATAL: handle_error(); break;
471 case L_DISK: handle_disk(); break;
473 case L_SUCCESS: handle_success(); break;
474 case L_STRANGE: handle_strange(); break;
475 case L_FAIL: handle_failed(); break;
479 curprog = P_REPORTER;
480 curstr = stralloc2("unexpected log line: ", curstr);
490 subj_str = vstralloc(getconf_str(CNF_ORG),
491 " ", amflush_run ? "AMFLUSH" : "AMANDA",
492 " ", "MAIL REPORT FOR",
493 " ", nicedate(run_datestamp ? atoi(run_datestamp) : 0),
496 /* lookup the tapetype and printer type from the amanda.conf file. */
497 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
498 printer = getconf_str(CNF_PRINTER);
500 /* ignore SIGPIPE so if a child process dies we do not also go away */
501 signal(SIGPIPE, SIG_IGN);
503 /* open pipe to mailer */
506 /* output to a file */
507 if((mailf = fopen(outfname,"w")) == NULL) {
508 error("could not open output file: %s %s", outfname, strerror(errno));
510 fprintf(mailf, "To: %s\n", getconf_str(CNF_MAILTO));
511 fprintf(mailf, "Subject: %s\n\n", subj_str);
515 mail_cmd = vstralloc(MAILER,
516 " -s", " \"", subj_str, "\"",
517 " ", getconf_str(CNF_MAILTO),
519 if((mailf = popen(mail_cmd, "w")) == NULL)
520 error("could not open pipe to \"%s\": %s",
521 mail_cmd, strerror(errno));
525 /* open pipe to print spooler if necessary) */
528 /* if the postscript_label_template (tp->lbl_templ) field is not */
529 /* the empty string (i.e. it is set to something), open the */
530 /* postscript debugging file for writing. */
531 if ((strcmp(tp->lbl_templ, "")) != 0) {
532 if ((postscript = fopen(psfname, "w")) == NULL) {
534 curprog = P_REPORTER;
535 curstr = vstralloc("could not open ",
546 if (strcmp(printer, "") != 0) /* alternate printer is defined */
547 /* print to the specified printer */
549 printer_cmd = vstralloc(LPRCMD, " ", LPRFLAG, printer, NULL);
551 printer_cmd = vstralloc(LPRCMD, NULL);
554 /* print to the default printer */
555 printer_cmd = vstralloc(LPRCMD, NULL);
558 if ((strcmp(tp->lbl_templ, "")) != 0) {
560 if ((postscript = popen(printer_cmd, "w")) == NULL) {
562 curprog = P_REPORTER;
563 curstr = vstralloc("could not open pipe to ",
573 curprog = P_REPORTER;
574 curstr = stralloc("no printer command defined");
584 if(!got_finish) fputs("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n", mailf);
589 fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
590 output_lines(errsum, mailf);
592 fputs("\n\n", mailf);
597 fprintf(mailf,"\n\014\nFAILED AND STRANGE DUMP DETAILS:\n");
598 output_lines(errdet, mailf);
601 fprintf(mailf,"\n\014\nNOTES:\n");
602 output_lines(notes, mailf);
605 if(sortq.head != NULL) {
606 fprintf(mailf,"\n\014\nDUMP SUMMARY:\n");
609 fprintf(mailf,"\n(brought to you by Amanda version %s)\n",
613 do_postscript_output();
617 /* close postscript file */
618 if (psfname && postscript) {
619 /* it may be that postscript is NOT opened */
623 if (postscript != NULL && pclose(postscript) != 0)
624 error("printer command failed: %s", printer_cmd);
628 /* close output file */
633 if(pclose(mailf) != 0)
634 error("mail command failed: %s", mail_cmd);
638 amfree(run_datestamp);
646 malloc_size_2 = malloc_inuse(&malloc_hist_2);
648 if(malloc_size_1 != malloc_size_2) {
649 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
657 #define mb(f) ((f)/1024.0) /* kbytes -> mbytes */
658 #define pct(f) ((f)*100.0) /* percent */
659 #define hrmn(f) ((int)(f)+30)/3600, (((int)(f)+30)%3600)/60
660 #define mnsc(f) ((int)(f+0.5))/60, ((int)(f+0.5)) % 60
662 #define divzero(fp,a,b) \
666 fprintf((fp)," -- "); \
667 else if ((q = (a)/q) >= 999.95) \
668 fprintf((fp), "###.#"); \
670 fprintf((fp), "%5.1f",q); \
672 #define divzero_wide(fp,a,b) \
676 fprintf((fp)," -- "); \
677 else if ((q = (a)/q) >= 99999.95) \
678 fprintf((fp), "#####.#"); \
680 fprintf((fp), "%7.1f",q); \
686 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
687 int tapesize, marksize, lv, first;
689 tapesize = tp->length;
690 marksize = tp->filemark;
692 stats[2].dumpdisks = stats[0].dumpdisks + stats[1].dumpdisks;
693 stats[2].tapedisks = stats[0].tapedisks + stats[1].tapedisks;
694 stats[2].outsize = stats[0].outsize + stats[1].outsize;
695 stats[2].origsize = stats[0].origsize + stats[1].origsize;
696 stats[2].tapesize = stats[0].tapesize + stats[1].tapesize;
697 stats[2].coutsize = stats[0].coutsize + stats[1].coutsize;
698 stats[2].corigsize = stats[0].corigsize + stats[1].corigsize;
699 stats[2].taper_time = stats[0].taper_time + stats[1].taper_time;
700 stats[2].dumper_time = stats[0].dumper_time + stats[1].dumper_time;
702 if(!got_finish) /* no driver finish line, estimate total run time */
703 total_time = stats[2].taper_time + startup_time;
705 idle_time = (total_time - startup_time) - stats[2].taper_time;
706 if(idle_time < 0) idle_time = 0.0;
708 fprintf(mailf,"STATISTICS:\n");
710 " Total Full Daily\n");
712 " -------- -------- --------\n");
715 "Estimate Time (hrs:min) %2d:%02d\n", hrmn(startup_time));
718 "Run Time (hrs:min) %2d:%02d\n", hrmn(total_time));
721 "Dump Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n",
722 hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
723 hrmn(stats[1].dumper_time));
726 "Output Size (meg) %8.1f %8.1f %8.1f\n",
727 mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
730 "Original Size (meg) %8.1f %8.1f %8.1f\n",
731 mb(stats[2].origsize), mb(stats[0].origsize),
732 mb(stats[1].origsize));
734 fprintf(mailf, "Avg Compressed Size (%%) ");
735 divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
737 divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
739 divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
741 if(stats[1].dumpdisks > 0) fputs(" (level:#disks ...)", mailf);
745 "Filesystems Dumped %4d %4d %4d",
746 stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
748 if(stats[1].dumpdisks > 0) {
750 for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
751 fputs(first?" (":" ", mailf);
753 fprintf(mailf, "%d:%d", lv, dumpdisks[lv]);
759 fprintf(mailf, "Avg Dump Rate (k/s) ");
760 divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
762 divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
764 divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
769 "Tape Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n",
770 hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
771 hrmn(stats[1].taper_time));
774 "Tape Size (meg) %8.1f %8.1f %8.1f\n",
775 mb(stats[2].tapesize), mb(stats[0].tapesize),
776 mb(stats[1].tapesize));
778 fprintf(mailf, "Tape Used (%%) ");
779 divzero(mailf, pct(stats[2].tapesize+marksize*stats[2].tapedisks),tapesize);
781 divzero(mailf, pct(stats[0].tapesize+marksize*stats[0].tapedisks),tapesize);
783 divzero(mailf, pct(stats[1].tapesize+marksize*stats[1].tapedisks),tapesize);
785 if(stats[1].tapedisks > 0) fputs(" (level:#disks ...)", mailf);
789 "Filesystems Taped %4d %4d %4d",
790 stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
792 if(stats[1].tapedisks > 0) {
794 for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
795 fputs(first?" (":" ", mailf);
797 fprintf(mailf, "%d:%d", lv, tapedisks[lv]);
803 fprintf(mailf, "Avg Tp Write Rate (k/s) ");
804 divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
806 divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
808 divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
812 int label_length = strlen(stats_by_tape->label) + 5;
813 fprintf(mailf,"\nUSAGE BY TAPE:\n");
814 fprintf(mailf," %-*s Time Size %% Nb\n",
815 label_length, "Label");
816 for(current_tape = stats_by_tape; current_tape != NULL;
817 current_tape = current_tape->next) {
818 fprintf(mailf, " %-*s", label_length, current_tape->label);
819 fprintf(mailf, " %2d:%02d", hrmn(current_tape->taper_time));
820 fprintf(mailf, " %9.1f ", mb(current_tape->coutsize));
821 divzero(mailf, pct(current_tape->coutsize +
822 marksize * current_tape->tapedisks),
824 fprintf(mailf, " %4d\n", current_tape->tapedisks);
832 void output_tapeinfo()
838 if (last_run_tapes > 0) {
840 fprintf(mailf, "The dumps were flushed to tape%s %s.\n",
841 last_run_tapes == 1 ? "" : "s",
842 tape_labels ? tape_labels : "");
844 fprintf(mailf, "These dumps were to tape%s %s.\n",
845 last_run_tapes == 1 ? "" : "s",
846 tape_labels ? tape_labels : "");
851 "*** A TAPE ERROR OCCURRED: %s.\n", tapestart_error);
852 fputs("Some dumps may have been left in the holding disk.\n", mailf);
854 "Run amflush%s to flush them to tape.\n",
855 amflush_run ? " again" : "");
858 tp = lookup_last_reusable_tape(skip);
860 run_tapes = getconf_int(CNF_RUNTAPES);
863 fputs("The next tape Amanda expects to use is: ", mailf);
865 fprintf(mailf, "The next %d tapes Amanda expects to used are: ",
868 while(run_tapes > 0) {
870 fprintf(mailf, "%s", tp->label);
872 fputs("a new tape", mailf);
874 if(run_tapes > 1) fputs(", ", mailf);
878 tp = lookup_last_reusable_tape(skip);
882 lasttp = lookup_tapepos(lookup_nb_tape());
883 run_tapes = getconf_int(CNF_RUNTAPES);
884 if(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
886 while(lasttp && run_tapes > 0 && lasttp->datestamp == 0) {
888 lasttp = lasttp->prev;
891 lasttp = lookup_tapepos(lookup_nb_tape());
893 fprintf(mailf, "The next new tape already labelled is: %s.\n",
897 fprintf(mailf, "The next %d new tapes already labelled are: %s", c,
899 lasttp = lasttp->prev;
901 while(lasttp && c > 0 && lasttp->datestamp == 0) {
902 fprintf(mailf, ", %s", lasttp->label);
903 lasttp = lasttp->prev;
906 fprintf(mailf, ".\n");
913 void output_lines(lp, f)
931 int sort_by_time(a, b)
934 return data(b)->dumper.sec - data(a)->dumper.sec;
937 int sort_by_name(a, b)
942 rc = strcmp(a->host->hostname, b->host->hostname);
943 if(rc == 0) rc = strcmp(a->name, b->name);
951 sortq.head = sortq.tail = NULL;
952 while(!empty(*diskq)) {
953 dp = dequeue_disk(diskq);
954 if(data(dp) == NULL) { /* create one */
955 find_repdata(dp, run_datestamp, 0);
957 insert_disk(&sortq, dp, sort_by_name);
961 void CheckStringMax(ColumnInfo *cd, char *s) {
969 void CheckIntMax(ColumnInfo *cd, int n) {
973 ap_snprintf(testBuf, sizeof(testBuf),
974 cd->Format, cd->Width, cd->Precision, n);
981 void CheckFloatMax(ColumnInfo *cd, double d) {
985 ap_snprintf(testBuf, sizeof(testBuf),
986 cd->Format, cd->Width, cd->Precision, d);
1000 static int DumpRate;
1001 static int TapeTime;
1002 static int TapeRate;
1004 void CalcMaxWidth() {
1005 /* we have to look for columspec's, that require the recalculation.
1006 * we do here the same loops over the sortq as is done in
1007 * output_summary. So, if anything is changed there, we have to
1008 * change this here also.
1015 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1017 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1019 char TimeRateBuffer[40];
1021 CheckStringMax(&ColumnData[HostName], dp->host->hostname);
1022 CheckStringMax(&ColumnData[Disk], dp->name);
1023 if (repdata->dumper.result == L_BOGUS &&
1024 repdata->taper.result == L_BOGUS)
1026 CheckIntMax(&ColumnData[Level], repdata->level);
1027 if(repdata->dumper.result == L_SUCCESS) {
1028 CheckFloatMax(&ColumnData[OrigKB], repdata->dumper.origsize);
1029 CheckFloatMax(&ColumnData[OutKB], repdata->dumper.outsize);
1030 if(dp->compress == COMP_NONE)
1033 f = repdata->dumper.origsize;
1034 CheckStringMax(&ColumnData[Disk],
1035 sDivZero(pct(repdata->dumper.outsize), f, Compress));
1038 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1039 "%3d:%02d", mnsc(repdata->dumper.sec));
1041 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1043 CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
1045 CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps);
1048 cd= &ColumnData[TapeTime];
1049 if(repdata->taper.result == L_FAIL) {
1050 CheckStringMax(cd, "FAILED");
1053 if(repdata->taper.result == L_SUCCESS)
1054 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1055 "%3d:%02d", mnsc(repdata->taper.sec));
1057 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1059 CheckStringMax(cd, TimeRateBuffer);
1061 cd= &ColumnData[TapeRate];
1062 if(repdata->taper.result == L_SUCCESS)
1063 CheckFloatMax(cd, repdata->taper.kps);
1065 CheckStringMax(cd, "N/A ");
1071 void output_summary()
1075 char *ds="DUMPER STATS";
1076 char *ts=" TAPER STATS";
1079 int i, h, w1, wDump, wTape;
1080 float outsize, origsize;
1083 HostName = StringToColumn("HostName");
1084 Disk = StringToColumn("Disk");
1085 Level = StringToColumn("Level");
1086 OrigKB = StringToColumn("OrigKB");
1087 OutKB = StringToColumn("OutKB");
1088 Compress = StringToColumn("Compress");
1089 DumpTime = StringToColumn("DumpTime");
1090 DumpRate = StringToColumn("DumpRate");
1091 TapeTime = StringToColumn("TapeTime");
1092 TapeRate = StringToColumn("TapeRate");
1094 /* at first determine if we have to recalculate our widths */
1095 if (MaxWidthsRequested)
1098 /* title for Dumper-Stats */
1099 w1= ColWidth(HostName, Level);
1100 wDump= ColWidth(OrigKB, DumpRate);
1101 wTape= ColWidth(TapeTime, TapeRate);
1103 /* print centered top titles */
1110 fprintf(mailf, "%*s", w1+h, "");
1111 fprintf(mailf, "%-*s", wDump-h, ds);
1118 fprintf(mailf, "%*s", h, "");
1119 fprintf(mailf, "%-*s", wTape-h, ts);
1122 /* print the titles */
1123 for (i=0; ColumnData[i].Name != NULL; i++) {
1125 ColumnInfo *cd= &ColumnData[i];
1126 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1127 if (cd->Format[1] == '-')
1131 fprintf(mailf, fmt, cd->Width, cd->Title);
1135 /* print the rules */
1136 fputs(tmp=Rule(HostName, Level), mailf); amfree(tmp);
1137 fputs(tmp=Rule(OrigKB, DumpRate), mailf); amfree(tmp);
1138 fputs(tmp=Rule(TapeTime, TapeRate), mailf); amfree(tmp);
1141 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1144 char TimeRateBuffer[40];
1145 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1148 cd= &ColumnData[HostName];
1149 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1150 fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
1152 cd= &ColumnData[Disk];
1153 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1154 devlen= strlen(dp->name);
1155 if (devlen > cd->Width) {
1157 fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
1158 dp->name+devlen - (cd->Width-1) );
1161 fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->name);
1163 cd= &ColumnData[Level];
1164 if (repdata->dumper.result == L_BOGUS &&
1165 repdata->taper.result == L_BOGUS) {
1167 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1168 tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
1170 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1171 tmp=TextRule(OrigKB, TapeRate, "MISSING"));
1177 cd= &ColumnData[Level];
1178 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1179 fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
1181 if (repdata->dumper.result == L_SKIPPED) {
1182 fprintf(mailf, "%s\n",
1183 tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
1187 if (repdata->dumper.result == L_FAIL) {
1188 fprintf(mailf, "%s\n",
1189 tmp=TextRule(OrigKB, TapeRate, "FAILED"));
1194 if(repdata->dumper.result == L_SUCCESS)
1195 origsize = repdata->dumper.origsize;
1197 origsize = repdata->taper.origsize;
1199 if(repdata->taper.result == L_SUCCESS)
1200 outsize = repdata->taper.outsize;
1202 outsize = repdata->dumper.outsize;
1204 cd= &ColumnData[OrigKB];
1205 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1207 fprintf(mailf, cd->Format, cd->Width, cd->Precision, origsize);
1209 fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
1211 cd= &ColumnData[OutKB];
1212 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1214 fprintf(mailf, cd->Format, cd->Width, cd->Precision, outsize);
1216 cd= &ColumnData[Compress];
1217 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1219 if(dp->compress == COMP_NONE)
1221 else if(origsize < 1.0)
1226 fputs(sDivZero(pct(outsize), f, Compress), mailf);
1228 cd= &ColumnData[DumpTime];
1229 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1230 if(repdata->dumper.result == L_SUCCESS)
1231 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1232 "%3d:%02d", mnsc(repdata->dumper.sec));
1234 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1236 fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1238 cd= &ColumnData[DumpRate];
1239 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1240 if(repdata->dumper.result == L_SUCCESS)
1241 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
1243 fprintf(mailf, "%*s", cd->Width, "N/A ");
1245 cd= &ColumnData[TapeTime];
1246 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1247 if(repdata->taper.result == L_FAIL) {
1248 fprintf(mailf, "%s\n",
1249 tmp=TextRule(TapeTime, TapeRate, "FAILED "));
1254 if(repdata->taper.result == L_SUCCESS)
1255 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1256 "%3d:%02d", mnsc(repdata->taper.sec));
1258 ap_snprintf(TimeRateBuffer, sizeof(TimeRateBuffer),
1260 fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1262 cd= &ColumnData[TapeRate];
1263 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1264 if(repdata->taper.result == L_SUCCESS)
1265 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
1267 fprintf(mailf, "%*s", cd->Width, "N/A ");
1276 printf("line %d of log is bogus\n", curlinenum);
1280 char *nicedate(datestamp)
1283 * Formats an integer of the form YYYYMMDD into the string
1284 * "Monthname DD, YYYY". A pointer to the statically allocated string
1285 * is returned, so it must be copied to other storage (or just printed)
1286 * before calling nicedate() again.
1289 static char nice[64];
1290 static char *months[13] = { "BogusMonth",
1291 "January", "February", "March", "April", "May", "June",
1292 "July", "August", "September", "October", "November", "December"
1294 int year, month, day;
1296 year = datestamp / 10000;
1297 day = datestamp % 100;
1298 month = (datestamp / 100) % 100;
1300 ap_snprintf(nice, sizeof(nice), "%s %d, %d", months[month], day, year);
1307 static int started = 0;
1317 skip_whitespace(s, ch);
1318 #define sc "datestamp"
1319 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1326 skip_whitespace(s, ch);
1332 skip_non_whitespace(s, ch);
1334 run_datestamp = newstralloc(run_datestamp, fp);
1337 skip_whitespace(s, ch);
1339 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1346 skip_whitespace(s, ch);
1352 skip_non_whitespace(s, ch);
1355 label = stralloc(fp);
1359 fp = vstralloc(tape_labels, ", ", label, NULL);
1360 amfree(tape_labels);
1363 tape_labels = stralloc(label);
1368 if(stats_by_tape == NULL) {
1369 stats_by_tape = current_tape = (taper_t *)alloc(sizeof(taper_t));
1372 current_tape->next = (taper_t *)alloc(sizeof(taper_t));
1373 current_tape = current_tape->next;
1375 current_tape->label = label;
1376 current_tape->taper_time = 0.0;
1377 current_tape->coutsize = 0.0;
1378 current_tape->corigsize = 0.0;
1379 current_tape->tapedisks = 0;
1380 current_tape->next = NULL;
1400 skip_whitespace(s, ch);
1402 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1403 return; /* ignore bogus line */
1408 skip_whitespace(s, ch);
1414 skip_non_whitespace(s, ch);
1416 run_datestamp = newstralloc(run_datestamp, fp);
1421 if(amflush_run && normal_run) {
1424 " reporter: both amflush and planner output in log, ignoring amflush.");
1429 void handle_finish()
1434 if(curprog == P_DRIVER || curprog == P_AMFLUSH) {
1438 skip_whitespace(s, ch);
1440 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1448 skip_whitespace(s, ch);
1453 skip_non_whitespace(s, ch); /* ignore the date string */
1455 skip_whitespace(s, ch);
1457 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1465 skip_whitespace(s, ch);
1470 if(sscanf(s - 1, "%f", &total_time) != 1) {
1484 if(curprog == P_DRIVER) {
1488 skip_whitespace(s, ch);
1489 #define sc "startup time"
1490 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1498 skip_whitespace(s, ch);
1503 if(sscanf(s - 1, "%f", &startup_time) != 1) {
1515 str = vstralloc(" ", program_str[curprog], ": ", curstr, NULL);
1516 addline(¬es, str);
1525 char *s = NULL, *nl;
1528 if(curlog == L_ERROR && curprog == P_TAPER) {
1532 skip_whitespace(s, ch);
1533 #define sc "no-tape"
1534 if(ch == '\0' || strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
1542 skip_whitespace(s, ch);
1544 if((nl = strchr(s - 1, '\n')) != NULL) {
1547 tapestart_error = newstralloc(tapestart_error, s - 1);
1552 /* else some other tape error, handle like other errors */
1554 s = vstralloc(" ", program_str[curprog], ": ",
1555 logtype_str[curlog], " ", curstr, NULL);
1556 addline(&errsum, s);
1562 void handle_summary()
1575 char *hostname = NULL, *diskname = NULL;
1577 if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
1583 for(dp = diskq->head; dp != NULL; dp = dp->next)
1591 skip_whitespace(s, ch);
1597 skip_non_whitespace(s, ch);
1599 hostname = newstralloc(hostname, fp);
1602 skip_whitespace(s, ch);
1608 skip_non_whitespace(s, ch);
1610 diskname = newstralloc(diskname, fp);
1613 dp = lookup_disk(hostname, diskname);
1615 dp = add_disk(hostname, diskname);
1623 repdata_t *handle_success()
1626 float sec, kps, kbytes, origkb;
1631 char *hostname = NULL;
1632 char *diskname = NULL;
1637 if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER) {
1645 skip_whitespace(s, ch);
1651 skip_non_whitespace(s, ch);
1653 hostname = stralloc(fp);
1656 skip_whitespace(s, ch);
1663 skip_non_whitespace(s, ch);
1665 diskname = stralloc(fp);
1668 skip_whitespace(s, ch);
1676 skip_non_whitespace(s, ch);
1678 datestamp = stralloc(fp);
1681 level = atoi(datestamp);
1683 datestamp = newstralloc(datestamp, run_datestamp);
1686 skip_whitespace(s, ch);
1687 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1694 skip_integer(s, ch);
1697 skip_whitespace(s, ch);
1698 /* Planner success messages (for skipped
1699 dumps) do not contain statistics */
1700 if(curprog != P_PLANNER) {
1701 if(curprog != P_DUMPER ||
1702 sscanf(s - 1,"[sec %f kb %f kps %f orig-kb %f",
1703 &sec, &kbytes, &kps, &origkb) != 4) {
1705 if(sscanf(s - 1,"[sec %f kb %f kps %f",
1706 &sec, &kbytes, &kps) != 3) {
1715 if(origkb == 0.0) origkb = 0.1;
1720 dp = lookup_disk(hostname, diskname);
1724 str = vstralloc(" ", prefix(hostname, diskname, level),
1725 " ", "ERROR [not in disklist]",
1727 addline(&errsum, str);
1735 repdata = find_repdata(dp, datestamp, level);
1737 if(curprog == P_PLANNER) {
1738 repdata->dumper.result = L_SKIPPED;
1745 if(curprog == P_TAPER)
1746 sp = &(repdata->taper);
1747 else sp = &(repdata->dumper);
1756 get_info(hostname, diskname, &inf);
1757 tm = localtime(&inf.inf[level].date);
1758 Idatestamp = 10000*(tm->tm_year+1900) +
1759 100*(tm->tm_mon+1) + tm->tm_mday;
1761 if(atoi(datestamp) == Idatestamp) {
1762 /* grab original size from record */
1763 origkb = (double)inf.inf[level].size;
1772 sp->result = L_SUCCESS;
1773 sp->datestamp = repdata->datestamp;
1776 sp->origsize = origkb;
1777 sp->outsize = kbytes;
1779 if(curprog == P_TAPER) {
1780 if(current_tape == NULL) {
1781 error("current_tape == NULL");
1783 stats[i].taper_time += sec;
1784 sp->filenum = ++tapefcount;
1785 sp->tapelabel = current_tape->label;
1786 tapedisks[level] +=1;
1787 stats[i].tapedisks +=1;
1788 stats[i].tapesize += kbytes;
1789 current_tape->taper_time += sec;
1790 current_tape->coutsize += kbytes;
1791 current_tape->corigsize += origkb;
1792 current_tape->tapedisks += 1;
1795 if(curprog == P_DUMPER) {
1796 stats[i].dumper_time += sec;
1797 if(dp->compress == COMP_NONE) {
1798 sp->origsize = kbytes;
1801 stats[i].coutsize += kbytes;
1802 stats[i].corigsize += sp->origsize;
1804 dumpdisks[level] +=1;
1805 stats[i].dumpdisks +=1;
1806 stats[i].origsize += sp->origsize;
1807 stats[i].outsize += kbytes;
1813 void handle_strange()
1818 repdata = handle_success();
1820 str = vstralloc(" ", prefix(repdata->disk->host->hostname,
1821 repdata->disk->name, repdata->level),
1824 addline(&errsum, str);
1827 addline(&errdet,"");
1828 str = vstralloc("/-- ", prefix(repdata->disk->host->hostname,
1829 repdata->disk->name, repdata->level),
1832 addline(&errdet, str);
1835 while(contline_next()) {
1836 get_logline(logfile);
1837 addline(&errdet, curstr);
1839 addline(&errdet,"\\--------");
1842 void handle_failed()
1862 skip_whitespace(s, ch);
1868 skip_non_whitespace(s, ch);
1871 skip_whitespace(s, ch);
1877 skip_non_whitespace(s, ch);
1880 skip_whitespace(s, ch);
1886 skip_non_whitespace(s, ch);
1888 datestamp = stralloc(fp);
1890 if(strlen(datestamp) < 3) { /* there is no datestamp, it's the level */
1891 level = atoi(datestamp);
1892 datestamp = newstralloc(datestamp, run_datestamp);
1894 else { /* read the level */
1895 skip_whitespace(s, ch);
1896 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1901 skip_integer(s, ch);
1904 skip_whitespace(s, ch);
1911 if((s = strchr(errstr, '\n')) != NULL) {
1915 dp = lookup_disk(hostname, diskname);
1917 str = vstralloc(" ", prefix(hostname, diskname, level),
1918 " ", "ERROR [not in disklist]",
1920 addline(&errsum, str);
1923 repdata = find_repdata(dp, datestamp, level);
1925 if(curprog == P_TAPER)
1926 sp = &(repdata->taper);
1927 else sp = &(repdata->dumper);
1929 if(sp->result != L_SUCCESS)
1930 sp->result = L_FAIL;
1934 str = vstralloc(" ", prefix(hostname, diskname, level),
1938 addline(&errsum, str);
1941 if(curprog == P_DUMPER) {
1942 addline(&errdet,"");
1943 str = vstralloc("/-- ", prefix(hostname, diskname, level),
1947 addline(&errdet, str);
1949 while(contline_next()) {
1950 get_logline(logfile);
1951 addline(&errdet, curstr);
1953 addline(&errdet,"\\--------");
1958 void generate_missing()
1963 for(dp = diskq->head; dp != NULL; dp = dp->next) {
1964 if(dp->todo && data(dp) == NULL) {
1965 str = vstralloc(" ", prefix(dp->host->hostname, dp->name, -987),
1966 " ", "RESULTS MISSING",
1968 addline(&errsum, str);
1975 prefix (host, disk, level)
1982 char number[NUM_STR_SIZE];
1983 static char *str = NULL;
1985 ap_snprintf(number, sizeof(number), "%d", level);
1987 strncpy(h, host, sizeof(h)-1);
1989 strncpy(h, "(host?)", sizeof(h)-1);
1991 h[sizeof(h)-1] = '\0';
1992 for(l = strlen(h); l < sizeof(h)-1; l++) {
1995 str = newvstralloc(str,
1997 " ", disk ? disk : "(disk?)",
1998 level != -987 ? " lev " : "",
1999 level != -987 ? number : "",
2004 void copy_template_file(lbl_templ)
2011 if (strchr(lbl_templ, '/') == NULL) {
2012 lbl_templ = stralloc2(config_dir, lbl_templ);
2014 lbl_templ = stralloc(lbl_templ);
2016 if ((fd = open(lbl_templ, 0)) < 0) {
2018 curprog = P_REPORTER;
2019 curstr = vstralloc("could not open PostScript template file ",
2026 afclose(postscript);
2029 while ((numread = read(fd, buf, sizeof(buf))) > 0) {
2030 if (fwrite(buf, numread, 1, postscript) != 1) {
2032 curprog = P_REPORTER;
2033 curstr = vstralloc("error copying PostScript template file ",
2040 afclose(postscript);
2046 curprog = P_REPORTER;
2047 curstr = vstralloc("error reading PostScript template file ",
2054 afclose(postscript);
2061 repdata_t *find_repdata(dp, datestamp, level)
2066 repdata_t *repdata, *prev;
2069 datestamp = run_datestamp;
2071 for(repdata = data(dp); repdata != NULL && (repdata->level != level || strcmp(repdata->datestamp,datestamp)!=0); repdata = repdata->next) {
2075 repdata = (repdata_t *)alloc(sizeof(repdata_t));
2076 memset(repdata, '\0',sizeof(repdata_t));
2078 repdata->datestamp = stralloc(datestamp ? datestamp : "");
2079 repdata->level = level;
2080 repdata->dumper.result = L_BOGUS;
2081 repdata->taper.result = L_BOGUS;
2082 repdata->next = NULL;
2084 prev->next = repdata;
2086 dp->up = (void *)repdata;
2092 void do_postscript_output()
2094 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
2097 float outsize, origsize;
2098 int tapesize, marksize;
2100 tapesize = tp->length;
2101 marksize = tp->filemark;
2103 for(current_tape = stats_by_tape; current_tape != NULL;
2104 current_tape = current_tape->next) {
2106 if (current_tape->label == NULL) {
2110 copy_template_file(tp->lbl_templ);
2112 /* generate a few elements */
2113 fprintf(postscript,"(%s) DrawDate\n\n",
2114 nicedate(run_datestamp ? atoi(run_datestamp) : 0));
2115 fprintf(postscript,"(Amanda Version %s) DrawVers\n",version());
2116 fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
2119 fprintf(postscript, "(Total Size: %6.1f MB) DrawStat\n",
2120 mb(current_tape->coutsize));
2121 fprintf(postscript, "(Tape Used (%%) ");
2122 divzero(postscript, pct(current_tape->coutsize +
2123 marksize * current_tape->tapedisks),
2125 fprintf(postscript," %%) DrawStat\n");
2126 fprintf(postscript, "(Compression Ratio: ");
2127 divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
2128 fprintf(postscript," %%) DrawStat\n");
2129 fprintf(postscript,"(Filesystems Taped: %4d) DrawStat\n",
2130 current_tape->tapedisks);
2135 "(-) (%s) (-) ( 0) ( 32) ( 32) DrawHost\n",
2136 current_tape->label);
2138 for(dp = sortq.head; dp != NULL; dp = dp->next) {
2139 if (dp->todo == 0) {
2142 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2144 if(repdata->taper.tapelabel != current_tape->label) {
2148 if(repdata->dumper.result == L_SUCCESS)
2149 origsize = repdata->dumper.origsize;
2151 origsize = repdata->taper.origsize;
2153 if(repdata->taper.result == L_SUCCESS)
2154 outsize = repdata->taper.outsize;
2156 outsize = repdata->dumper.outsize;
2158 if (repdata->taper.result == L_SUCCESS) {
2159 if(origsize != 0.0) {
2160 fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0f) (%8.0f) DrawHost\n",
2161 dp->host->hostname, dp->name, repdata->level,
2162 repdata->taper.filenum, origsize,
2166 fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0f) DrawHost\n",
2167 dp->host->hostname, dp->name, repdata->level,
2168 repdata->taper.filenum, "N/A",
2175 fprintf(postscript,"\nshowpage\n");