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.132 2006/08/28 17:02:48 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
55 #define STATUS_STRANGE 2
56 #define STATUS_FAILED 4
57 #define STATUS_MISSING 8
58 #define STATUS_TAPE 16
60 typedef struct line_s {
65 typedef struct timedata_s {
67 double origsize, outsize;
74 typedef struct repdata_s {
77 double est_nsize, est_csize;
82 struct repdata_s *next;
85 #define data(dp) ((repdata_t *)(dp)->up)
87 static struct cumulative_stats {
88 int dumpdisks, tapedisks, tapechunks;
89 double taper_time, dumper_time;
90 double outsize, origsize, tapesize;
91 double coutsize, corigsize; /* compressed dump only */
94 static int dumpdisks[10], tapedisks[10], tapechunks[10]; /* by-level breakdown of disk count */
96 typedef struct taper_s {
99 double coutsize, corigsize;
100 int tapedisks, tapechunks;
101 struct taper_s *next;
104 static taper_t *stats_by_tape = NULL;
105 static taper_t *current_tape = NULL;
107 typedef struct strange_s {
112 struct strange_s *next;
115 static strange_t *first_strange=NULL, *last_strange=NULL;
117 static double total_time, startup_time, planner_time;
119 /* count files to tape */
120 static int tapefcount = 0;
122 static int exit_status = 0;
123 static char *run_datestamp;
124 static char *tape_labels = NULL;
125 static int last_run_tapes = 0;
126 static int degraded_mode = 0; /* defined in driverio too */
127 static int normal_run = 0;
128 static int amflush_run = 0;
129 static int got_finish = 0;
130 static char *ghostname = NULL;
132 static char *tapestart_error = NULL;
134 static FILE *logfile, *mailf;
136 static FILE *postscript;
137 static char *printer;
139 static disklist_t diskq;
140 static disklist_t sortq;
142 static line_t *errsum = NULL;
143 static line_t *errdet = NULL;
144 static line_t *notes = NULL;
146 static char MaxWidthsRequested = 0; /* determined via config data */
149 long int unitdivisor;
151 /* local functions */
152 int main(int argc, char **argv);
154 static char * nicedate(const char * datestamp);
155 static char * prefix(char *host, char *disk, int level);
156 static char * prefixstrange(char *host, char *disk, int level,
157 size_t len_host, size_t len_disk);
158 static char * Rule(int From, int To);
159 static char * sDivZero(double a, double b, int cn);
160 static char * TextRule(int From, int To, char *s);
161 static int ColWidth(int From, int To);
162 static int contline_next(void);
163 static int sort_by_name(disk_t *a, disk_t *b);
164 static repdata_t *find_repdata(disk_t *dp, char *datestamp, int level);
165 static repdata_t *handle_chunk(void);
166 static repdata_t *handle_success(logtype_t logtype);
167 static void addline(line_t **lp, char *str);
168 static void addtostrange(char *host, char *disk, int level, char *str);
169 static void bogus_line(const char *);
170 static void CalcMaxWidth(void);
171 static void CheckFloatMax(ColumnInfo *cd, double d);
172 static void CheckIntMax(ColumnInfo *cd, int n);
173 static void CheckStringMax(ColumnInfo *cd, char *s);
174 static void copy_template_file(char *lbl_templ);
175 static void do_postscript_output(void);
176 static void generate_missing(void);
177 static void generate_bad_estimate(void);
178 static void handle_disk(void);
179 static void handle_error(void);
180 static void handle_failed(void);
181 static void handle_finish(void);
182 static void handle_note(void);
183 static void handle_partial(void);
184 static void handle_start(void);
185 static void handle_stats(void);
186 static void handle_strange(void);
187 static void handle_summary(void);
188 static void output_lines(line_t *lp, FILE *f);
189 static void output_stats(void);
190 static void output_strange(void);
191 static void output_summary(void);
192 static void output_tapeinfo(void);
193 static void sort_disks(void);
194 static void usage(void);
202 for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
203 Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
214 int Leng= ColWidth(0, ColumnDataCount());
215 char *RuleSpace= alloc((size_t)(Leng+1));
216 ThisLeng= ColWidth(From, To);
217 for (i=0;i<ColumnData[From].PrefixSpace; i++)
219 for (; i<ThisLeng; i++)
221 RuleSpace[ThisLeng]= '\0';
231 ColumnInfo *cd= &ColumnData[From];
233 int nbrules, i, txtlength;
234 int RuleSpaceSize= ColWidth(0, ColumnDataCount());
235 char *RuleSpace= alloc((size_t)RuleSpaceSize), *tmp;
237 leng = (int)strlen(s);
238 if(leng >= (RuleSpaceSize - cd->PrefixSpace))
239 leng = RuleSpaceSize - cd->PrefixSpace - 1;
240 snprintf(RuleSpace, (size_t)RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "",
242 txtlength = cd->PrefixSpace + leng + 1;
243 nbrules = ColWidth(From,To) - txtlength;
244 for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
256 ColumnInfo *cd= &ColumnData[cn];
257 static char PrtBuf[256];
259 snprintf(PrtBuf, SIZEOF(PrtBuf),
260 "%*s", cd->Width, "-- ");
262 snprintf(PrtBuf, SIZEOF(PrtBuf),
263 cd->Format, cd->Width, cd->Precision, a/b);
272 if ((ch = getc(logfile)) != EOF) {
273 if (ungetc(ch, logfile) == EOF) {
274 if (ferror(logfile)) {
275 error("ungetc failed: %s\n", strerror(errno));
278 error("ungetc failed: EOF\n");
292 /* allocate new line node */
293 new = (line_t *) alloc(SIZEOF(line_t));
295 new->str = stralloc(str);
297 /* add to end of list */
302 while (p->next != NULL)
311 error("Usage: amreport conf [-i] [-M address] [-f output-file] [-l logfile] [-p postscript-file] [-o configoption]*");
324 char *logfname, *psfname, *outfname, *subj_str = NULL;
327 unsigned long malloc_hist_1, malloc_size_1;
328 unsigned long malloc_hist_2, malloc_size_2;
329 char *mail_cmd = NULL, *printer_cmd = NULL;
331 char my_cwd[STR_SIZE];
332 char *ColumnSpec = "";
337 int new_argc, my_argc;
338 char **new_argv, **my_argv;
339 char *lbl_templ = NULL;
343 set_pname("amreport");
345 dbopen(DBG_SUBDIR_SERVER);
347 /* Don't die when child closes pipe */
348 signal(SIGPIPE, SIG_IGN);
350 malloc_size_1 = malloc_inuse(&malloc_hist_1);
352 /* Process options */
354 erroutput_type = ERR_INTERACTIVE;
359 if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
360 error("cannot determine current working directory");
364 parse_conf(argc, argv, &new_argc, &new_argv);
369 config_dir = stralloc2(my_cwd, "/");
370 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
371 config_name = stralloc(config_name + 1);
374 if (my_argv[1][0] == '-') {
378 config_name = stralloc(my_argv[1]);
379 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
380 --my_argc; ++my_argv;
381 while((opt = getopt(my_argc, my_argv, "M:f:l:p:i")) != EOF) {
387 if (mailto != NULL) {
388 error("you may specify at most one -M");
391 mailto = stralloc(optarg);
392 if(!validate_mailto(mailto)) {
393 error("mail address has invalid characters");
398 if (outfname != NULL) {
399 error("you may specify at most one -f");
402 if (*optarg == '/') {
403 outfname = stralloc(optarg);
405 outfname = vstralloc(my_cwd, "/", optarg, NULL);
409 if (logfname != NULL) {
410 error("you may specify at most one -l");
413 if (*optarg == '/') {
414 logfname = stralloc(optarg);
416 logfname = vstralloc(my_cwd, "/", optarg, NULL);
420 if (psfname != NULL) {
421 error("you may specify at most one -p");
424 if (*optarg == '/') {
425 psfname = stralloc(optarg);
427 psfname = vstralloc(my_cwd, "/", optarg, NULL);
441 if( !mailout && mailto ){
442 printf("You cannot specify both -i & -M at the same time\n");
449 printf("You must run amreport with '-f <output file>' because configure\n");
450 printf("didn't find a mailer.\n");
457 /* read configuration files */
459 conffile = stralloc2(config_dir, CONFFILE_NAME);
460 /* Ignore error from read_conffile */
461 read_conffile(conffile);
464 dbrename(config_name, DBG_SUBDIR_SERVER);
466 report_bad_conf_arg();
467 conf_diskfile = getconf_str(CNF_DISKFILE);
468 if (*conf_diskfile == '/') {
469 conf_diskfile = stralloc(conf_diskfile);
471 conf_diskfile = stralloc2(config_dir, conf_diskfile);
473 /* Ignore error from read_diskfile */
474 read_diskfile(conf_diskfile, &diskq);
475 amfree(conf_diskfile);
476 if(mailout && !mailto &&
477 getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0) {
478 mailto = getconf_str(CNF_MAILTO);
479 if(!validate_mailto(mailto)){
484 conf_tapelist = getconf_str(CNF_TAPELIST);
485 if (*conf_tapelist == '/') {
486 conf_tapelist = stralloc(conf_tapelist);
488 conf_tapelist = stralloc2(config_dir, conf_tapelist);
490 /* Ignore error from read_tapelist */
491 read_tapelist(conf_tapelist);
492 amfree(conf_tapelist);
493 conf_infofile = getconf_str(CNF_INFOFILE);
494 if (*conf_infofile == '/') {
495 conf_infofile = stralloc(conf_infofile);
497 conf_infofile = stralloc2(config_dir, conf_infofile);
499 if(open_infofile(conf_infofile)) {
500 error("could not open info db \"%s\"", conf_infofile);
503 amfree(conf_infofile);
505 displayunit = getconf_str(CNF_DISPLAYUNIT);
506 unitdivisor = getconf_unit_divisor();
508 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
509 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
511 curprog = P_REPORTER;
516 ColumnSpec = ""; /* use the default */
517 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
519 curprog = P_REPORTER;
526 for (cn = 0; ColumnData[cn].Name != NULL; cn++) {
527 if (ColumnData[cn].MaxWidth) {
528 MaxWidthsRequested = 1;
536 conf_logdir = getconf_str(CNF_LOGDIR);
537 if (*conf_logdir == '/') {
538 conf_logdir = stralloc(conf_logdir);
540 conf_logdir = stralloc2(config_dir, conf_logdir);
542 logfname = vstralloc(conf_logdir, "/", "log", NULL);
546 if((logfile = fopen(logfname, "r")) == NULL) {
548 curprog = P_REPORTER;
549 curstr = vstralloc("could not open log ",
558 while(logfile && get_logline(logfile)) {
560 case L_START: handle_start(); break;
561 case L_FINISH: handle_finish(); break;
563 case L_INFO: handle_note(); break;
564 case L_WARNING: handle_note(); break;
566 case L_SUMMARY: handle_summary(); break;
567 case L_STATS: handle_stats(); break;
569 case L_ERROR: handle_error(); break;
570 case L_FATAL: handle_error(); break;
572 case L_DISK: handle_disk(); break;
574 case L_SUCCESS: handle_success(curlog); break;
575 case L_CHUNKSUCCESS: handle_success(curlog); break;
576 case L_CHUNK: handle_chunk(); break;
577 case L_PARTIAL: handle_partial(); break;
578 case L_STRANGE: handle_strange(); break;
579 case L_FAIL: handle_failed(); break;
583 curprog = P_REPORTER;
584 curstr = stralloc2("unexpected log line: ", curstr);
593 generate_bad_estimate();
596 subj_str = vstralloc(getconf_str(CNF_ORG),
597 " ", amflush_run ? "AMFLUSH" : "AMANDA",
598 " ", "MAIL REPORT FOR",
599 " ", nicedate(run_datestamp ? run_datestamp : "0"),
602 /* lookup the tapetype and printer type from the amanda.conf file. */
603 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
604 printer = getconf_str(CNF_PRINTER);
606 /* ignore SIGPIPE so if a child process dies we do not also go away */
607 signal(SIGPIPE, SIG_IGN);
609 /* open pipe to mailer */
612 /* output to a file */
613 if((mailf = fopen(outfname,"w")) == NULL) {
614 error("could not open output file: %s %s", outfname, strerror(errno));
617 fprintf(mailf, "To: %s\n", mailto);
618 fprintf(mailf, "Subject: %s\n\n", subj_str);
623 mail_cmd = vstralloc(MAILER,
624 " -s", " \"", subj_str, "\"",
626 if((mailf = popen(mail_cmd, "w")) == NULL) {
627 error("could not open pipe to \"%s\": %s",
628 mail_cmd, strerror(errno));
634 printf("No mail sent! ");
635 printf("No valid mail address has been specified in amanda.conf or on the commmand line\n");
642 /* open pipe to print spooler if necessary) */
645 /* if the postscript_label_template (tp->lbl_templ) field is not */
646 /* the empty string (i.e. it is set to something), open the */
647 /* postscript debugging file for writing. */
649 lbl_templ = tapetype_get_lbl_templ(tp);
650 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
651 if ((postscript = fopen(psfname, "w")) == NULL) {
653 curprog = P_REPORTER;
654 curstr = vstralloc("could not open ",
665 if (strcmp(printer, "") != 0) /* alternate printer is defined */
666 /* print to the specified printer */
668 printer_cmd = vstralloc(LPRCMD, " ", LPRFLAG, printer, NULL);
670 printer_cmd = vstralloc(LPRCMD, NULL);
673 /* print to the default printer */
674 printer_cmd = vstralloc(LPRCMD, NULL);
677 lbl_templ = tapetype_get_lbl_templ(tp);
678 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
680 if ((postscript = popen(printer_cmd, "w")) == NULL) {
682 curprog = P_REPORTER;
683 curstr = vstralloc("could not open pipe to ",
693 curprog = P_REPORTER;
694 curstr = stralloc("no printer command defined");
705 if(!got_finish) fputs("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n", mailf);
708 fprintf(mailf, "Hostname: %s\n", ghostname);
709 fprintf(mailf, "Org : %s\n", getconf_str(CNF_ORG));
710 fprintf(mailf, "Config : %s\n", config_name);
711 fprintf(mailf, "Date : %s\n",
712 nicedate(run_datestamp ? run_datestamp : "0"));
718 if(first_strange || errsum) {
719 fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
720 if(first_strange) output_strange();
721 if(errsum) output_lines(errsum, mailf);
723 fputs("\n\n", mailf);
728 fprintf(mailf,"\n\014\nFAILED AND STRANGE DUMP DETAILS:\n");
729 output_lines(errdet, mailf);
732 fprintf(mailf,"\n\014\nNOTES:\n");
733 output_lines(notes, mailf);
736 if(sortq.head != NULL) {
737 fprintf(mailf,"\n\014\nDUMP SUMMARY:\n");
740 fprintf(mailf,"\n(brought to you by Amanda version %s)\n",
745 do_postscript_output();
749 /* close postscript file */
750 if (psfname && postscript) {
751 /* it may be that postscript is NOT opened */
755 if (postscript != NULL && pclose(postscript) != 0) {
756 error("printer command failed: %s", printer_cmd);
762 /* close output file */
767 if(pclose(mailf) != 0) {
768 error("mail command failed: %s", mail_cmd);
775 free_disklist(&diskq);
776 free_new_argv(new_argc, new_argv);
777 free_server_config();
778 amfree(run_datestamp);
786 malloc_size_2 = malloc_inuse(&malloc_hist_2);
788 if(malloc_size_1 != malloc_size_2) {
789 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
798 #define mb(f) ((f)/1024) /* kbytes -> mbutes */
799 #define du(f) ((f)/unitdivisor) /* kbytes -> displayunit */
800 #define pct(f) ((f)*100.0) /* percent */
801 #define hrmn(f) ((int)(f)+30)/3600, (((int)(f)+30)%3600)/60
802 #define mnsc(f) ((int)(f+0.5))/60, ((int)(f+0.5)) % 60
804 #define divzero(fp,a,b) \
808 fprintf((fp)," -- "); \
809 else if ((q = (a)/q) >= 999.95) \
810 fprintf((fp), "###.#"); \
812 fprintf((fp), "%5.1lf",q); \
814 #define divzero_wide(fp,a,b) \
818 fprintf((fp)," -- "); \
819 else if ((q = (a)/q) >= 99999.95) \
820 fprintf((fp), "#####.#"); \
822 fprintf((fp), "%7.1lf",q); \
829 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
835 tapesize = tapetype_get_length(tp);
836 marksize = tapetype_get_filemark(tp);
838 tapesize = 100 * 1024 * 1024;
839 marksize = 1 * 1024 * 1024;
842 stats[2].dumpdisks = stats[0].dumpdisks + stats[1].dumpdisks;
843 stats[2].tapedisks = stats[0].tapedisks + stats[1].tapedisks;
844 stats[2].tapechunks = stats[0].tapechunks + stats[1].tapechunks;
845 stats[2].outsize = stats[0].outsize + stats[1].outsize;
846 stats[2].origsize = stats[0].origsize + stats[1].origsize;
847 stats[2].tapesize = stats[0].tapesize + stats[1].tapesize;
848 stats[2].coutsize = stats[0].coutsize + stats[1].coutsize;
849 stats[2].corigsize = stats[0].corigsize + stats[1].corigsize;
850 stats[2].taper_time = stats[0].taper_time + stats[1].taper_time;
851 stats[2].dumper_time = stats[0].dumper_time + stats[1].dumper_time;
853 if(!got_finish) /* no driver finish line, estimate total run time */
854 total_time = stats[2].taper_time + planner_time;
856 idle_time = (total_time - startup_time) - stats[2].taper_time;
857 if(idle_time < 0) idle_time = 0.0;
859 fprintf(mailf,"STATISTICS:\n");
861 " Total Full Incr.\n");
863 " -------- -------- --------\n");
866 "Estimate Time (hrs:min) %2d:%02d\n", hrmn(planner_time));
869 "Run Time (hrs:min) %2d:%02d\n", hrmn(total_time));
872 "Dump Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n",
873 hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
874 hrmn(stats[1].dumper_time));
877 "Output Size (meg) %8.1lf %8.1lf %8.1lf\n",
878 mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
881 "Original Size (meg) %8.1lf %8.1lf %8.1lf\n",
882 mb(stats[2].origsize), mb(stats[0].origsize),
883 mb(stats[1].origsize));
885 fprintf(mailf, "Avg Compressed Size (%%) ");
886 divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
888 divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
890 divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
892 if(stats[1].dumpdisks > 0) fputs(" (level:#disks ...)", mailf);
896 "Filesystems Dumped %4d %4d %4d",
897 stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
899 if(stats[1].dumpdisks > 0) {
901 for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
902 fputs(first?" (":" ", mailf);
904 fprintf(mailf, "%d:%d", lv, dumpdisks[lv]);
910 fprintf(mailf, "Avg Dump Rate (k/s) ");
911 divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
913 divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
915 divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
920 "Tape Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n",
921 hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
922 hrmn(stats[1].taper_time));
925 "Tape Size (meg) %8.1lf %8.1lf %8.1lf\n",
926 mb(stats[2].tapesize), mb(stats[0].tapesize),
927 mb(stats[1].tapesize));
929 fprintf(mailf, "Tape Used (%%) ");
930 divzero(mailf, pct(stats[2].tapesize+marksize*(stats[2].tapedisks+stats[2].tapechunks)),(double)tapesize);
932 divzero(mailf, pct(stats[0].tapesize+marksize*(stats[0].tapedisks+stats[0].tapechunks)),(double)tapesize);
934 divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),(double)tapesize);
936 if(stats[1].tapedisks > 0) fputs(" (level:#disks ...)", mailf);
940 "Filesystems Taped %4d %4d %4d",
941 stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
943 if(stats[1].tapedisks > 0) {
945 for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
946 fputs(first?" (":" ", mailf);
948 fprintf(mailf, "%d:%d", lv, tapedisks[lv]);
954 if(stats[1].tapechunks > 0) fputs(" (level:#chunks ...)", mailf);
958 "Chunks Taped %4d %4d %4d",
959 stats[2].tapechunks, stats[0].tapechunks, stats[1].tapechunks);
961 if(stats[1].tapechunks > 0) {
963 for(lv = 1; lv < 10; lv++) if(tapechunks[lv]) {
964 fputs(first?" (":" ", mailf);
966 fprintf(mailf, "%d:%d", lv, tapechunks[lv]);
972 fprintf(mailf, "Avg Tp Write Rate (k/s) ");
973 divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
975 divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
977 divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
981 int label_length = (int)strlen(stats_by_tape->label) + 5;
982 fprintf(mailf,"\nUSAGE BY TAPE:\n");
983 fprintf(mailf," %-*s Time Size %% Nb Nc\n",
984 label_length, "Label");
985 for(current_tape = stats_by_tape; current_tape != NULL;
986 current_tape = current_tape->next) {
987 fprintf(mailf, " %-*s", label_length, current_tape->label);
988 fprintf(mailf, " %2d:%02d", hrmn(current_tape->taper_time));
989 fprintf(mailf, " %8.0lf%s ", du(current_tape->coutsize), displayunit);
990 divzero(mailf, pct(current_tape->coutsize + marksize *
991 (current_tape->tapedisks+current_tape->tapechunks)),
993 fprintf(mailf, " %4d", current_tape->tapedisks);
994 fprintf(mailf, " %4d\n", current_tape->tapechunks);
1002 output_tapeinfo(void)
1004 tape_t *tp, *lasttp;
1008 if (last_run_tapes > 0) {
1010 fprintf(mailf, "The dumps were flushed to tape%s %s.\n",
1011 last_run_tapes == 1 ? "" : "s",
1012 tape_labels ? tape_labels : "");
1014 fprintf(mailf, "These dumps were to tape%s %s.\n",
1015 last_run_tapes == 1 ? "" : "s",
1016 tape_labels ? tape_labels : "");
1021 "*** A TAPE ERROR OCCURRED: %s.\n", tapestart_error);
1022 fputs("Some dumps may have been left in the holding disk.\n", mailf);
1024 "Run amflush%s to flush them to tape.\n",
1025 amflush_run ? " again" : "");
1028 tp = lookup_last_reusable_tape(skip);
1030 run_tapes = getconf_int(CNF_RUNTAPES);
1033 fputs("The next tape Amanda expects to use is: ", mailf);
1034 else if(run_tapes > 1)
1035 fprintf(mailf, "The next %d tapes Amanda expects to use are: ",
1038 while(run_tapes > 0) {
1040 fprintf(mailf, "%s", tp->label);
1043 fprintf(mailf, "a new tape");
1045 fprintf(mailf, "%d new tapes", run_tapes);
1049 if(run_tapes > 1) fputs(", ", mailf);
1053 tp = lookup_last_reusable_tape(skip);
1055 fputs(".\n", mailf);
1057 lasttp = lookup_tapepos(lookup_nb_tape());
1058 run_tapes = getconf_int(CNF_RUNTAPES);
1059 if(lasttp && run_tapes > 0 && strcmp(lasttp->datestamp,"0") == 0) {
1061 while(lasttp && run_tapes > 0 && strcmp(lasttp->datestamp,"0") == 0) {
1063 lasttp = lasttp->prev;
1066 lasttp = lookup_tapepos(lookup_nb_tape());
1068 fprintf(mailf, "The next new tape already labelled is: %s.\n",
1072 fprintf(mailf, "The next %d new tapes already labelled are: %s", c,
1074 lasttp = lasttp->prev;
1076 while(lasttp && c > 0 && strcmp(lasttp->datestamp,"0") == 0) {
1077 fprintf(mailf, ", %s", lasttp->label);
1078 lasttp = lasttp->prev;
1081 fprintf(mailf, ".\n");
1088 output_strange(void)
1090 size_t len_host=0, len_disk=0;
1094 for(strange=first_strange; strange != NULL; strange = strange->next) {
1095 if(strlen(strange->hostname) > len_host)
1096 len_host = strlen(strange->hostname);
1097 if(strlen(strange->diskname) > len_disk)
1098 len_disk = strlen(strange->diskname);
1100 for(strange=first_strange; strange != NULL; strange = strange->next) {
1101 str = vstralloc(" ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
1102 " ", strange->str, NULL);
1103 fprintf(mailf, "%s\n", str);
1134 rc = strcmp(a->host->hostname, b->host->hostname);
1135 if(rc == 0) rc = strcmp(a->name, b->name);
1144 sortq.head = sortq.tail = NULL;
1145 while(!empty(diskq)) {
1146 dp = dequeue_disk(&diskq);
1147 if(data(dp) == NULL) { /* create one */
1148 find_repdata(dp, run_datestamp, 0);
1150 insert_disk(&sortq, dp, sort_by_name);
1160 int l = (int)strlen(s);
1176 snprintf(testBuf, SIZEOF(testBuf),
1177 cd->Format, cd->Width, cd->Precision, n);
1178 l = (int)strlen(testBuf);
1192 snprintf(testBuf, SIZEOF(testBuf),
1193 cd->Format, cd->Width, cd->Precision, d);
1194 l = (int)strlen(testBuf);
1200 static int HostName;
1205 static int Compress;
1206 static int DumpTime;
1207 static int DumpRate;
1208 static int TapeTime;
1209 static int TapeRate;
1214 /* we have to look for columspec's, that require the recalculation.
1215 * we do here the same loops over the sortq as is done in
1216 * output_summary. So, if anything is changed there, we have to
1217 * change this here also.
1225 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1227 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1228 char TimeRateBuffer[40];
1230 CheckStringMax(&ColumnData[HostName], dp->host->hostname);
1231 qdevname = quote_string(dp->name);
1232 CheckStringMax(&ColumnData[Disk], qdevname);
1234 if (repdata->dumper.result == L_BOGUS &&
1235 repdata->taper.result == L_BOGUS)
1237 CheckIntMax(&ColumnData[Level], repdata->level);
1238 if(repdata->dumper.result == L_SUCCESS ||
1239 repdata->dumper.result == L_CHUNKSUCCESS) {
1240 CheckFloatMax(&ColumnData[OrigKB],
1241 (double)du(repdata->dumper.origsize));
1242 CheckFloatMax(&ColumnData[OutKB],
1243 (double)du(repdata->dumper.outsize));
1244 if(dp->compress == COMP_NONE)
1247 f = repdata->dumper.origsize;
1248 CheckStringMax(&ColumnData[Disk],
1249 sDivZero(pct(repdata->dumper.outsize), f, Compress));
1252 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1253 "%3d:%02d", mnsc(repdata->dumper.sec));
1255 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1257 CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
1259 CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps);
1262 if(repdata->taper.result == L_FAIL) {
1263 CheckStringMax(&ColumnData[TapeTime], "FAILED");
1266 if(repdata->taper.result == L_SUCCESS ||
1267 repdata->taper.result == L_CHUNKSUCCESS)
1268 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1269 "%3d:%02d", mnsc(repdata->taper.sec));
1271 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1273 CheckStringMax(&ColumnData[TapeTime], TimeRateBuffer);
1275 if(repdata->taper.result == L_SUCCESS ||
1276 repdata->taper.result == L_CHUNKSUCCESS)
1277 CheckFloatMax(&ColumnData[TapeRate], repdata->taper.kps);
1279 CheckStringMax(&ColumnData[TapeRate], "N/A ");
1286 output_summary(void)
1290 char *ds="DUMPER STATS";
1291 char *ts=" TAPER STATS";
1294 int i, h, w1, wDump, wTape;
1295 double outsize, origsize;
1298 HostName = StringToColumn("HostName");
1299 Disk = StringToColumn("Disk");
1300 Level = StringToColumn("Level");
1301 OrigKB = StringToColumn("OrigKB");
1302 OutKB = StringToColumn("OutKB");
1303 Compress = StringToColumn("Compress");
1304 DumpTime = StringToColumn("DumpTime");
1305 DumpRate = StringToColumn("DumpRate");
1306 TapeTime = StringToColumn("TapeTime");
1307 TapeRate = StringToColumn("TapeRate");
1309 /* at first determine if we have to recalculate our widths */
1310 if (MaxWidthsRequested)
1313 /* title for Dumper-Stats */
1314 w1= ColWidth(HostName, Level);
1315 wDump= ColWidth(OrigKB, DumpRate);
1316 wTape= ColWidth(TapeTime, TapeRate);
1318 /* print centered top titles */
1319 h = (int)strlen(ds);
1325 fprintf(mailf, "%*s", w1+h, "");
1326 fprintf(mailf, "%-*s", wDump-h, ds);
1327 h = (int)strlen(ts);
1333 fprintf(mailf, "%*s", h, "");
1334 fprintf(mailf, "%-*s", wTape-h, ts);
1337 /* print the titles */
1338 for (i=0; ColumnData[i].Name != NULL; i++) {
1340 ColumnInfo *cd= &ColumnData[i];
1341 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1342 if (cd->Format[1] == '-')
1346 if(strcmp(cd->Title,"ORIG-KB") == 0) {
1347 /* cd->Title must be re-allocated in write-memory */
1348 cd->Title = stralloc("ORIG-KB");
1349 cd->Title[5] = displayunit[0];
1351 if(strcmp(cd->Title,"OUT-KB") == 0) {
1352 /* cd->Title must be re-allocated in write-memory */
1353 cd->Title = stralloc("OUT-KB");
1354 cd->Title[4] = displayunit[0];
1356 fprintf(mailf, fmt, cd->Width, cd->Title);
1360 /* print the rules */
1361 fputs(tmp=Rule(HostName, Level), mailf); amfree(tmp);
1362 fputs(tmp=Rule(OrigKB, DumpRate), mailf); amfree(tmp);
1363 fputs(tmp=Rule(TapeTime, TapeRate), mailf); amfree(tmp);
1366 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1369 char TimeRateBuffer[40];
1370 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1375 cd= &ColumnData[HostName];
1376 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1377 fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
1379 cd= &ColumnData[Disk];
1380 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1381 devname = sanitize_string(dp->name);
1382 qdevname = quote_string(devname);
1383 devlen = strlen(qdevname);
1384 if (devlen > (size_t)cd->Width) {
1386 fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
1387 qdevname+devlen - (cd->Width-1) );
1390 fprintf(mailf, cd->Format, cd->Width, cd->Width, qdevname);
1393 cd= &ColumnData[Level];
1394 if (repdata->dumper.result == L_BOGUS &&
1395 repdata->taper.result == L_BOGUS) {
1397 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1398 tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
1400 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1401 tmp=TextRule(OrigKB, TapeRate, "MISSING"));
1407 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1408 fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
1410 if (repdata->dumper.result == L_SKIPPED) {
1411 fprintf(mailf, "%s\n",
1412 tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
1416 if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result != L_PARTIAL)) {
1417 fprintf(mailf, "%s\n",
1418 tmp=TextRule(OrigKB, TapeRate, "FAILED"));
1420 exit_status |= STATUS_FAILED;
1424 if(repdata->dumper.result == L_SUCCESS ||
1425 repdata->dumper.result == L_CHUNKSUCCESS)
1426 origsize = repdata->dumper.origsize;
1427 else if(repdata->taper.result == L_SUCCESS ||
1428 repdata->taper.result == L_PARTIAL)
1429 origsize = repdata->taper.origsize;
1431 origsize = repdata->chunker.origsize;
1433 if(repdata->taper.result == L_SUCCESS ||
1434 repdata->taper.result == L_CHUNKSUCCESS)
1435 outsize = repdata->taper.outsize;
1436 else if(repdata->chunker.result == L_SUCCESS ||
1437 repdata->chunker.result == L_PARTIAL ||
1438 repdata->chunker.result == L_CHUNKSUCCESS)
1439 outsize = repdata->chunker.outsize;
1440 else if (repdata->taper.result == L_PARTIAL)
1441 outsize = repdata->taper.outsize;
1443 outsize = repdata->dumper.outsize;
1445 cd= &ColumnData[OrigKB];
1446 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1447 if(isnormal(origsize))
1448 fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
1450 fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
1452 cd= &ColumnData[OutKB];
1453 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1455 fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
1457 cd= &ColumnData[Compress];
1458 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1460 if(dp->compress == COMP_NONE)
1462 else if(origsize < 1.0)
1467 fputs(sDivZero(pct(outsize), f, Compress), mailf);
1469 cd= &ColumnData[DumpTime];
1470 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1471 if(repdata->dumper.result == L_SUCCESS ||
1472 repdata->dumper.result == L_CHUNKSUCCESS)
1473 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1474 "%3d:%02d", mnsc(repdata->dumper.sec));
1476 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1478 fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1480 cd= &ColumnData[DumpRate];
1481 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1482 if(repdata->dumper.result == L_SUCCESS ||
1483 repdata->dumper.result == L_CHUNKSUCCESS)
1484 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
1486 fprintf(mailf, "%*s", cd->Width, "N/A ");
1488 cd= &ColumnData[TapeTime];
1489 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1490 if(repdata->taper.result == L_FAIL) {
1491 fprintf(mailf, "%s\n",
1492 tmp=TextRule(TapeTime, TapeRate, "FAILED "));
1497 if(repdata->taper.result == L_SUCCESS ||
1498 repdata->taper.result == L_PARTIAL ||
1499 repdata->taper.result == L_CHUNKSUCCESS)
1500 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1501 "%3d:%02d", mnsc(repdata->taper.sec));
1503 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1505 fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1507 cd= &ColumnData[TapeRate];
1508 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1509 if(repdata->taper.result == L_SUCCESS ||
1510 repdata->taper.result == L_PARTIAL ||
1511 repdata->taper.result == L_CHUNKSUCCESS)
1512 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
1514 fprintf(mailf, "%*s", cd->Width, "N/A ");
1516 if (repdata->chunker.result == L_PARTIAL)
1517 fprintf(mailf, " PARTIAL");
1518 else if(repdata->taper.result == L_PARTIAL)
1519 fprintf(mailf, " TAPE-PARTIAL");
1529 const char *err_text)
1531 printf("line %d of log is bogus: <%s>\n", curlinenum, curstr);
1532 printf(" Scan failed at: <%s>\n", err_text);
1537 * Formats an integer of the form YYYYMMDDHHMMSS into the string
1538 * "Monthname DD, YYYY". A pointer to the statically allocated string
1539 * is returned, so it must be copied to other storage (or just printed)
1540 * before calling nicedate() again.
1544 const char *datestamp)
1546 static char nice[64];
1549 static char *months[13] = { "BogusMonth",
1550 "January", "February", "March", "April", "May", "June",
1551 "July", "August", "September", "October", "November", "December"
1553 int year, month, day;
1555 strncpy(date, datestamp, 8);
1557 numdate = atoi(date);
1558 year = numdate / 10000;
1559 day = numdate % 100;
1560 month = (numdate / 100) % 100;
1564 snprintf(nice, SIZEOF(nice), "%s %d, %d", months[month], day, year);
1572 static int started = 0;
1582 skip_whitespace(s, ch);
1583 if(ch == '\0' || strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
1588 skip_whitespace(s, ch);
1594 skip_non_whitespace(s, ch);
1596 run_datestamp = newstralloc(run_datestamp, fp);
1599 skip_whitespace(s, ch);
1600 if(ch == '\0' || strncmp_const_skip(s - 1, "label", s, ch) != 0) {
1605 skip_whitespace(s, ch);
1611 skip_non_whitespace(s, ch);
1614 label = stralloc(fp);
1618 fp = vstralloc(tape_labels, ", ", label, NULL);
1619 amfree(tape_labels);
1622 tape_labels = stralloc(label);
1627 if(stats_by_tape == NULL) {
1628 stats_by_tape = current_tape = (taper_t *)alloc(SIZEOF(taper_t));
1631 current_tape->next = (taper_t *)alloc(SIZEOF(taper_t));
1632 current_tape = current_tape->next;
1634 current_tape->label = label;
1635 current_tape->taper_time = 0.0;
1636 current_tape->coutsize = 0.0;
1637 current_tape->corigsize = 0.0;
1638 current_tape->tapedisks = 0;
1639 current_tape->tapechunks = 0;
1640 current_tape->next = NULL;
1660 skip_whitespace(s, ch);
1661 if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
1662 return; /* ignore bogus line */
1665 skip_whitespace(s, ch);
1671 skip_non_whitespace(s, ch);
1673 run_datestamp = newstralloc(run_datestamp, fp);
1678 if(amflush_run && normal_run) {
1681 " reporter: both amflush and planner output in log, ignoring amflush.");
1693 if(curprog == P_DRIVER || curprog == P_AMFLUSH || curprog == P_PLANNER) {
1697 skip_whitespace(s, ch);
1698 if(ch == '\0' || strncmp_const_skip(s - 1, "date", s, ch) != 0) {
1703 skip_whitespace(s, ch);
1708 skip_non_whitespace(s, ch); /* ignore the date string */
1710 skip_whitespace(s, ch);
1711 if(ch == '\0' || strncmp_const_skip(s - 1, "time", s, ch) != 0) {
1712 /* older planner doesn't write time */
1713 if(curprog == P_PLANNER) return;
1718 skip_whitespace(s, ch);
1723 if(sscanf(s - 1, "%lf", &a_time) != 1) {
1727 if(curprog == P_PLANNER) {
1728 planner_time = a_time;
1731 total_time = a_time;
1742 char *hostname, *diskname, *datestamp;
1744 double sec, kps, nbytes, cbytes;
1748 if(curprog == P_DRIVER) {
1752 skip_whitespace(s, ch);
1753 if(ch != '\0' && strncmp_const_skip(s - 1, "startup time", s, ch) == 0) {
1754 skip_whitespace(s, ch);
1759 if(sscanf(s - 1, "%lf", &startup_time) != 1) {
1763 planner_time = startup_time;
1765 else if(ch != '\0' && strncmp_const_skip(s - 1, "hostname", s, ch) == 0) {
1766 skip_whitespace(s, ch);
1771 ghostname = stralloc(s-1);
1773 else if(ch != '\0' && strncmp_const_skip(s - 1, "estimate", s, ch) == 0) {
1774 skip_whitespace(s, ch);
1780 skip_non_whitespace(s, ch);
1782 hostname = stralloc(fp);
1785 skip_whitespace(s, ch);
1792 skip_non_whitespace(s, ch);
1794 diskname = stralloc(fp);
1797 skip_whitespace(s, ch);
1805 skip_non_whitespace(s, ch);
1807 datestamp = stralloc(fp);
1810 skip_whitespace(s, ch);
1811 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1818 skip_integer(s, ch);
1819 if(level < 0 || level > 9) {
1826 skip_whitespace(s, ch);
1828 if(sscanf(s - 1,"[sec %lf nkb %lf ckb %lf kps %lf",
1829 &sec, &nbytes, &cbytes, &kps) != 4) {
1837 dp = lookup_disk(hostname, diskname);
1839 addtostrange(hostname, diskname, level,
1840 "ERROR [not in disklist]");
1841 exit_status |= STATUS_FAILED;
1848 repdata = find_repdata(dp, datestamp, level);
1850 repdata->est_nsize = nbytes;
1851 repdata->est_csize = cbytes;
1872 str = vstralloc(" ", program_str[curprog], ": ", curstr, NULL);
1873 addline(¬es, str);
1883 char *s = NULL, *nl;
1886 if(curlog == L_ERROR && curprog == P_TAPER) {
1890 skip_whitespace(s, ch);
1891 if(ch != '\0' && strncmp_const_skip(s - 1, "no-tape", s, ch) == 0) {
1892 skip_whitespace(s, ch);
1894 if((nl = strchr(s - 1, '\n')) != NULL) {
1897 tapestart_error = newstralloc(tapestart_error, s - 1);
1900 exit_status |= STATUS_TAPE;;
1903 /* else some other tape error, handle like other errors */
1905 /* else some other tape error, handle like other errors */
1907 s = vstralloc(" ", program_str[curprog], ": ",
1908 logtype_str[curlog], " ", curstr, NULL);
1909 addline(&errsum, s);
1916 handle_summary(void)
1923 static int nb_disk=0;
1928 char *s, *fp, *qdiskname;
1930 char *hostname = NULL, *diskname = NULL;
1932 if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
1938 for(dp = diskq.head; dp != NULL; dp = dp->next)
1946 skip_whitespace(s, ch);
1952 skip_non_whitespace(s, ch);
1954 hostname = newstralloc(hostname, fp);
1957 skip_whitespace(s, ch);
1964 skip_quoted_string(s, ch);
1966 diskname = unquote_string(qdiskname);
1969 dp = lookup_disk(hostname, diskname);
1971 dp = add_disk(&diskq, hostname, diskname);
1979 /* XXX Just a placeholder, in case we decide to do something with L_CHUNK
1980 * log entries. Right now they're just the equivalent of L_SUCCESS, but only
1981 * for a split chunk of the overall dumpfile.
1987 double sec, kps, kbytes;
1992 char *hostname = NULL;
1993 char *diskname = NULL;
1998 if(curprog != P_TAPER) {
2006 skip_whitespace(s, ch);
2012 skip_non_whitespace(s, ch);
2014 hostname = stralloc(fp);
2017 skip_whitespace(s, ch);
2024 skip_quoted_string(s, ch);
2026 diskname = unquote_string(fp);
2029 skip_whitespace(s, ch);
2037 skip_non_whitespace(s, ch);
2039 datestamp = stralloc(fp);
2042 skip_whitespace(s, ch);
2043 if(ch == '\0' || sscanf(s - 1, "%d", &chunk) != 1) {
2050 skip_integer(s, ch);
2052 skip_whitespace(s, ch);
2053 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2060 skip_integer(s, ch);
2063 if(level < 0 || level > 9) {
2071 skip_whitespace(s, ch);
2072 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf", &sec, &kbytes, &kps) != 3) {
2081 dp = lookup_disk(hostname, diskname);
2085 str = vstralloc(" ", prefix(hostname, diskname, level),
2086 " ", "ERROR [not in disklist]",
2088 addline(&errsum, str);
2096 repdata = find_repdata(dp, datestamp, level);
2098 sp = &(repdata->taper);
2106 if(current_tape == NULL) {
2107 error("current_tape == NULL");
2109 if (sp->filenum == 0) {
2110 sp->filenum = ++tapefcount;
2111 sp->tapelabel = current_tape->label;
2113 tapechunks[level] +=1;
2114 stats[i].tapechunks +=1;
2115 current_tape->taper_time += sec;
2116 current_tape->coutsize += kbytes;
2117 current_tape->tapechunks += 1;
2128 double kbytes = 0.0;
2129 double origkb = 0.0;
2132 char *s, *fp, *qdiskname;
2134 char *hostname = NULL;
2135 char *diskname = NULL;
2140 if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
2141 curprog != P_CHUNKER) {
2149 skip_whitespace(s, ch);
2155 skip_non_whitespace(s, ch);
2157 hostname = stralloc(fp);
2160 skip_whitespace(s, ch);
2167 skip_quoted_string(s, ch);
2169 diskname = unquote_string(qdiskname);
2171 skip_whitespace(s, ch);
2179 skip_non_whitespace(s, ch);
2181 datestamp = stralloc(fp);
2184 if(strlen(datestamp) < 3) {
2185 level = atoi(datestamp);
2186 datestamp = newstralloc(datestamp, run_datestamp);
2189 skip_whitespace(s, ch);
2190 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2197 skip_integer(s, ch);
2200 if(level < 0 || level > 9) {
2207 skip_whitespace(s, ch);
2208 /* Planner success messages (for skipped
2209 dumps) do not contain statistics */
2210 if(curprog != P_PLANNER) {
2213 if((curprog != P_DUMPER)
2214 || (sscanf(s - 1,"[sec %lf kb %lf kps %lf orig-kb %lf",
2215 &sec, &kbytes, &kps, &origkb) != 4)) {
2217 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf",
2218 &sec, &kbytes, &kps) != 3) {
2227 if(!isnormal(origkb))
2233 dp = lookup_disk(hostname, diskname);
2235 addtostrange(hostname, qdiskname, level, "ERROR [not in disklist]");
2236 exit_status |= STATUS_FAILED;
2243 repdata = find_repdata(dp, datestamp, level);
2245 if(curprog == P_PLANNER) {
2246 repdata->dumper.result = L_SKIPPED;
2253 if(curprog == P_TAPER)
2254 sp = &(repdata->taper);
2255 else if(curprog == P_DUMPER)
2256 sp = &(repdata->dumper);
2257 else sp = &(repdata->chunker);
2266 get_info(hostname, diskname, &inf);
2267 tm = localtime(&inf.inf[level].date);
2269 Idatestamp = 10000*(tm->tm_year+1900) +
2270 100*(tm->tm_mon+1) + tm->tm_mday;
2272 Idatestamp = 19000101;
2275 if(atoi(datestamp) == Idatestamp) {
2276 /* grab original size from record */
2277 origkb = (double)inf.inf[level].size;
2283 if (curprog == P_DUMPER &&
2284 (sp->result == L_FAIL || sp->result == L_PARTIAL)) {
2285 addtostrange(hostname, qdiskname, level, "was successfully retried");
2292 sp->result = L_SUCCESS;
2293 sp->datestamp = repdata->datestamp;
2296 sp->origsize = origkb;
2297 sp->outsize = kbytes;
2299 if(curprog == P_TAPER) {
2300 if(current_tape == NULL) {
2301 error("current_tape == NULL");
2304 stats[i].taper_time += sec;
2305 sp->filenum = ++tapefcount;
2306 sp->tapelabel = current_tape->label;
2307 tapedisks[level] +=1;
2308 stats[i].tapedisks +=1;
2309 stats[i].tapesize += kbytes;
2310 sp->outsize = kbytes;
2311 if(!isnormal(repdata->chunker.outsize) && isnormal(repdata->dumper.outsize)) { /* dump to tape */
2312 stats[i].outsize += kbytes;
2313 if(dp->compress != COMP_NONE) {
2314 stats[i].coutsize += kbytes;
2317 if (logtype == L_SUCCESS || logtype== L_PARTIAL) {
2318 current_tape->taper_time += sec;
2319 current_tape->coutsize += kbytes;
2321 current_tape->corigsize += origkb;
2322 current_tape->tapedisks += 1;
2325 if(curprog == P_DUMPER) {
2326 stats[i].dumper_time += sec;
2327 if(dp->compress == COMP_NONE) {
2328 sp->origsize = kbytes;
2331 stats[i].corigsize += sp->origsize;
2333 dumpdisks[level] +=1;
2334 stats[i].dumpdisks +=1;
2335 stats[i].origsize += sp->origsize;
2338 if(curprog == P_CHUNKER) {
2339 sp->outsize = kbytes;
2340 stats[i].outsize += kbytes;
2341 if(dp->compress != COMP_NONE) {
2342 stats[i].coutsize += kbytes;
2349 handle_partial(void)
2354 repdata = handle_success(L_PARTIAL);
2358 if(curprog == P_TAPER)
2359 sp = &(repdata->taper);
2360 else if(curprog == P_DUMPER)
2361 sp = &(repdata->dumper);
2362 else sp = &(repdata->chunker);
2364 sp->result = L_PARTIAL;
2368 handle_strange(void)
2371 char *strangestr = NULL;
2375 repdata = handle_success(L_SUCCESS);
2379 qdisk = quote_string(repdata->disk->name);
2381 addline(&errdet,"");
2382 str = vstralloc("/-- ", prefix(repdata->disk->host->hostname,
2383 qdisk, repdata->level),
2386 addline(&errdet, str);
2389 while(contline_next()) {
2391 get_logline(logfile);
2393 if(strncmp_const_skip(curstr, "sendbackup: warning ", s, ch) == 0) {
2394 strangestr = newstralloc(strangestr, s);
2396 addline(&errdet, curstr);
2398 addline(&errdet,"\\--------");
2400 str = vstralloc("STRANGE", " ", strangestr, NULL);
2401 addtostrange(repdata->disk->host->hostname, qdisk, repdata->level, str);
2402 exit_status |= STATUS_STRANGE;
2417 char *s, *fp, *qdiskname;
2429 skip_whitespace(s, ch);
2435 skip_non_whitespace(s, ch);
2438 skip_whitespace(s, ch);
2444 skip_quoted_string(s, ch);
2446 diskname = unquote_string(qdiskname);
2448 skip_whitespace(s, ch);
2455 skip_non_whitespace(s, ch);
2457 datestamp = stralloc(fp);
2459 if(strlen(datestamp) < 3) { /* there is no datestamp, it's the level */
2460 level = atoi(datestamp);
2461 datestamp = newstralloc(datestamp, run_datestamp);
2463 else { /* read the level */
2464 skip_whitespace(s, ch);
2465 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2471 skip_integer(s, ch);
2474 skip_whitespace(s, ch);
2482 if((s = strchr(errstr, '\n')) != NULL) {
2486 dp = lookup_disk(hostname, diskname);
2489 addtostrange(hostname, qdiskname, level, "ERROR [not in disklist]");
2491 repdata = find_repdata(dp, datestamp, level);
2493 if(curprog == P_TAPER)
2494 sp = &(repdata->taper);
2495 else sp = &(repdata->dumper);
2497 if(sp->result != L_SUCCESS)
2498 sp->result = L_FAIL;
2502 str = vstralloc("FAILED", " ", errstr, NULL);
2503 addtostrange(hostname, qdiskname, level, str);
2506 if(curprog == P_DUMPER) {
2507 addline(&errdet,"");
2508 str = vstralloc("/-- ", prefix(hostname, qdiskname, level),
2512 addline(&errdet, str);
2514 while(contline_next()) {
2515 get_logline(logfile);
2516 addline(&errdet, curstr);
2518 addline(&errdet,"\\--------");
2519 exit_status |= STATUS_FAILED;
2526 generate_missing(void)
2531 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2532 if(dp->todo && data(dp) == NULL) {
2533 qdisk = quote_string(dp->name);
2534 addtostrange(dp->host->hostname, qdisk, -987, "RESULTS MISSING");
2535 exit_status |= STATUS_MISSING;
2542 generate_bad_estimate(void)
2549 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2551 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2552 if(repdata->est_csize >= 0.1) {
2553 if(repdata->taper.result == L_SUCCESS ||
2554 repdata->taper.result == L_CHUNKSUCCESS)
2555 outsize = repdata->taper.outsize;
2556 else if(repdata->chunker.result == L_SUCCESS ||
2557 repdata->chunker.result == L_PARTIAL ||
2558 repdata->chunker.result == L_CHUNKSUCCESS)
2559 outsize = repdata->chunker.outsize;
2560 else if(repdata->taper.result == L_PARTIAL)
2561 outsize = repdata->taper.outsize;
2563 outsize = repdata->dumper.outsize;
2565 if(repdata->est_csize * 0.9 > outsize) {
2567 " big estimate: %s %s %d",
2568 repdata->disk->host->hostname,
2569 repdata->disk->name,
2574 " est: %.0lf%s out %.0lf%s",
2575 du(repdata->est_csize), displayunit,
2576 du(outsize), displayunit);
2580 else if(repdata->est_csize * 1.1 < outsize) {
2582 " small estimate: %s %s %d",
2583 repdata->disk->host->hostname,
2584 repdata->disk->name,
2589 " est: %.0lf%s out %.0lf%s",
2590 du(repdata->est_csize), displayunit,
2591 du(outsize), displayunit);
2607 char number[NUM_STR_SIZE];
2608 static char *str = NULL;
2610 snprintf(number, SIZEOF(number), "%d", level);
2611 str = newvstralloc(str,
2612 " ", host ? host : "(host?)",
2613 " ", disk ? disk : "(disk?)",
2614 level != -987 ? " lev " : "",
2615 level != -987 ? number : "",
2631 char number[NUM_STR_SIZE];
2632 static char *str = NULL;
2634 snprintf(number, SIZEOF(number), "%d", level);
2635 h=alloc(len_host+1);
2637 strncpy(h, host, len_host);
2639 strncpy(h, "(host?)", len_host);
2642 for(l = strlen(h); l < len_host; l++) {
2645 d=alloc(len_disk+1);
2647 strncpy(d, disk, len_disk);
2649 strncpy(d, "(disk?)", len_disk);
2652 for(l = strlen(d); l < len_disk; l++) {
2655 str = newvstralloc(str,
2658 level != -987 ? " lev " : "",
2659 level != -987 ? number : "",
2676 strange = alloc(SIZEOF(strange_t));
2677 strange->hostname = stralloc(host);
2678 strange->diskname = stralloc(disk);
2679 strange->level = level;
2680 strange->str = stralloc(str);
2681 strange->next = NULL;
2682 if(first_strange == NULL) {
2683 first_strange = strange;
2686 last_strange->next = strange;
2688 last_strange = strange;
2700 if (strchr(lbl_templ, '/') == NULL) {
2701 lbl_templ = stralloc2(config_dir, lbl_templ);
2703 lbl_templ = stralloc(lbl_templ);
2705 if ((fd = open(lbl_templ, 0)) < 0) {
2707 curprog = P_REPORTER;
2708 curstr = vstralloc("could not open PostScript template file ",
2716 afclose(postscript);
2719 while ((numread = read(fd, buf, SIZEOF(buf))) > 0) {
2720 if (fwrite(buf, (size_t)numread, 1, postscript) != 1) {
2722 curprog = P_REPORTER;
2723 curstr = vstralloc("error copying PostScript template file ",
2731 afclose(postscript);
2737 curprog = P_REPORTER;
2738 curstr = vstralloc("error reading PostScript template file ",
2746 afclose(postscript);
2755 /*@keep@*/ disk_t *dp,
2759 repdata_t *repdata, *prev;
2762 datestamp = run_datestamp;
2764 for(repdata = data(dp); repdata != NULL && (repdata->level != level || strcmp(repdata->datestamp,datestamp)!=0); repdata = repdata->next) {
2768 repdata = (repdata_t *)alloc(SIZEOF(repdata_t));
2769 memset(repdata, '\0', SIZEOF(repdata_t));
2771 repdata->datestamp = stralloc(datestamp ? datestamp : "");
2772 repdata->level = level;
2773 repdata->dumper.result = L_BOGUS;
2774 repdata->taper.result = L_BOGUS;
2775 repdata->next = NULL;
2777 prev->next = repdata;
2779 dp->up = (void *)repdata;
2786 do_postscript_output(void)
2788 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
2791 double outsize, origsize;
2798 tapesize = tapetype_get_length(tp);
2799 marksize = tapetype_get_filemark(tp);
2801 for(current_tape = stats_by_tape; current_tape != NULL;
2802 current_tape = current_tape->next) {
2804 if (current_tape->label == NULL) {
2808 copy_template_file(tapetype_get_lbl_templ(tp));
2810 if (postscript == NULL)
2813 /* generate a few elements */
2814 fprintf(postscript,"(%s) DrawDate\n\n",
2815 nicedate(run_datestamp ? run_datestamp : "0"));
2816 fprintf(postscript,"(Amanda Version %s) DrawVers\n",version());
2817 fprintf(postscript,"(%s) DrawTitle\n", current_tape->label);
2820 fprintf(postscript, "(Total Size: %6.1lf MB) DrawStat\n",
2821 mb(current_tape->coutsize));
2822 fprintf(postscript, "(Tape Used (%%) ");
2823 divzero(postscript, pct(current_tape->coutsize +
2824 marksize * (current_tape->tapedisks + current_tape->tapechunks)),
2826 fprintf(postscript," %%) DrawStat\n");
2827 fprintf(postscript, "(Compression Ratio: ");
2828 divzero(postscript, pct(current_tape->coutsize),current_tape->corigsize);
2829 fprintf(postscript," %%) DrawStat\n");
2830 fprintf(postscript,"(Filesystems Taped: %4d) DrawStat\n",
2831 current_tape->tapedisks);
2836 "(-) (%s) (-) ( 0) ( 32) ( 32) DrawHost\n",
2837 current_tape->label);
2839 for(dp = sortq.head; dp != NULL; dp = dp->next) {
2840 if (dp->todo == 0) {
2843 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2845 if(repdata->taper.tapelabel != current_tape->label) {
2849 if(repdata->dumper.result == L_SUCCESS ||
2850 repdata->dumper.result == L_PARTIAL)
2851 origsize = repdata->dumper.origsize;
2853 origsize = repdata->taper.origsize;
2855 if(repdata->taper.result == L_SUCCESS ||
2856 repdata->taper.result == L_PARTIAL)
2857 outsize = repdata->taper.outsize;
2859 outsize = repdata->dumper.outsize;
2861 if (repdata->taper.result == L_SUCCESS ||
2862 repdata->taper.result == L_PARTIAL) {
2863 if(isnormal(origsize)) {
2864 fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8.0lf) (%8.0lf) DrawHost\n",
2865 dp->host->hostname, dp->name, repdata->level,
2866 repdata->taper.filenum, origsize,
2870 fprintf(postscript,"(%s) (%s) (%d) (%3.0d) (%8s) (%8.0lf) DrawHost\n",
2871 dp->host->hostname, dp->name, repdata->level,
2872 repdata->taper.filenum, "N/A",
2879 fprintf(postscript,"\nshowpage\n");