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.2.1 2007/01/26 13:12:43 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;
131 static char *tapestart_error = NULL;
133 static FILE *logfile, *mailf;
135 static FILE *postscript;
136 static char *printer;
138 static disklist_t diskq;
139 static disklist_t sortq;
141 static line_t *errsum = NULL;
142 static line_t *errdet = NULL;
143 static line_t *notes = NULL;
145 static char MaxWidthsRequested = 0; /* determined via config data */
148 long int unitdivisor;
150 /* local functions */
151 int main(int argc, char **argv);
153 static char * nicedate(const char * datestamp);
154 static char * prefix(char *host, char *disk, int level);
155 static char * prefixstrange(char *host, char *disk, int level,
156 size_t len_host, size_t len_disk);
157 static char * Rule(int From, int To);
158 static char * sDivZero(double a, double b, int cn);
159 static char * TextRule(int From, int To, char *s);
160 static int ColWidth(int From, int To);
161 static int contline_next(void);
162 static int sort_by_name(disk_t *a, disk_t *b);
163 static repdata_t *find_repdata(disk_t *dp, char *datestamp, int level);
164 static repdata_t *handle_chunk(void);
165 static repdata_t *handle_success(logtype_t logtype);
166 static void addline(line_t **lp, char *str);
167 static void addtostrange(char *host, char *disk, int level, char *str);
168 static void bogus_line(const char *);
169 static void CalcMaxWidth(void);
170 static void CheckFloatMax(ColumnInfo *cd, double d);
171 static void CheckIntMax(ColumnInfo *cd, int n);
172 static void CheckStringMax(ColumnInfo *cd, char *s);
173 static void copy_template_file(char *lbl_templ);
174 static void do_postscript_output(void);
175 static void generate_missing(void);
176 static void generate_bad_estimate(void);
177 static void handle_disk(void);
178 static void handle_error(void);
179 static void handle_failed(void);
180 static void handle_finish(void);
181 static void handle_note(void);
182 static void handle_partial(void);
183 static void handle_start(void);
184 static void handle_stats(void);
185 static void handle_strange(void);
186 static void handle_summary(void);
187 static void output_lines(line_t *lp, FILE *f);
188 static void output_stats(void);
189 static void output_strange(void);
190 static void output_summary(void);
191 static void output_tapeinfo(void);
192 static void sort_disks(void);
193 static void usage(void);
201 for (i=From; i<=To && ColumnData[i].Name != NULL; i++) {
202 Width+= ColumnData[i].PrefixSpace + ColumnData[i].Width;
213 int Leng= ColWidth(0, ColumnDataCount());
214 char *RuleSpace= alloc((size_t)(Leng+1));
215 ThisLeng= ColWidth(From, To);
216 for (i=0;i<ColumnData[From].PrefixSpace; i++)
218 for (; i<ThisLeng; i++)
220 RuleSpace[ThisLeng]= '\0';
230 ColumnInfo *cd= &ColumnData[From];
232 int nbrules, i, txtlength;
233 int RuleSpaceSize= ColWidth(0, ColumnDataCount());
234 char *RuleSpace= alloc((size_t)RuleSpaceSize), *tmp;
236 leng = (int)strlen(s);
237 if(leng >= (RuleSpaceSize - cd->PrefixSpace))
238 leng = RuleSpaceSize - cd->PrefixSpace - 1;
239 snprintf(RuleSpace, (size_t)RuleSpaceSize, "%*s%*.*s ", cd->PrefixSpace, "",
241 txtlength = cd->PrefixSpace + leng + 1;
242 nbrules = ColWidth(From,To) - txtlength;
243 for(tmp=RuleSpace + txtlength, i=nbrules ; i>0; tmp++,i--)
255 ColumnInfo *cd= &ColumnData[cn];
256 static char PrtBuf[256];
258 snprintf(PrtBuf, SIZEOF(PrtBuf),
259 "%*s", cd->Width, "-- ");
261 snprintf(PrtBuf, SIZEOF(PrtBuf),
262 cd->Format, cd->Width, cd->Precision, a/b);
271 if ((ch = getc(logfile)) != EOF) {
272 if (ungetc(ch, logfile) == EOF) {
273 if (ferror(logfile)) {
274 error("ungetc failed: %s\n", strerror(errno));
277 error("ungetc failed: EOF\n");
291 /* allocate new line node */
292 new = (line_t *) alloc(SIZEOF(line_t));
294 new->str = stralloc(str);
296 /* add to end of list */
301 while (p->next != NULL)
310 error("Usage: amreport conf [-i] [-M address] [-f output-file] [-l logfile] [-p postscript-file] [-o configoption]*");
323 char *logfname, *psfname, *outfname, *subj_str = NULL;
326 unsigned long malloc_hist_1, malloc_size_1;
327 unsigned long malloc_hist_2, malloc_size_2;
328 char *mail_cmd = NULL, *printer_cmd = NULL;
330 char my_cwd[STR_SIZE];
331 char *ColumnSpec = "";
336 int new_argc, my_argc;
337 char **new_argv, **my_argv;
338 char *lbl_templ = NULL;
342 set_pname("amreport");
344 dbopen(DBG_SUBDIR_SERVER);
346 /* Don't die when child closes pipe */
347 signal(SIGPIPE, SIG_IGN);
349 malloc_size_1 = malloc_inuse(&malloc_hist_1);
351 /* Process options */
353 erroutput_type = ERR_INTERACTIVE;
358 if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
359 error("cannot determine current working directory");
363 parse_server_conf(argc, argv, &new_argc, &new_argv);
368 config_dir = stralloc2(my_cwd, "/");
369 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
370 config_name = stralloc(config_name + 1);
373 if (my_argv[1][0] == '-') {
377 config_name = stralloc(my_argv[1]);
378 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
379 --my_argc; ++my_argv;
380 while((opt = getopt(my_argc, my_argv, "M:f:l:p:i")) != EOF) {
386 if (mailto != NULL) {
387 error("you may specify at most one -M");
390 mailto = stralloc(optarg);
391 if(!validate_mailto(mailto)) {
392 error("mail address has invalid characters");
397 if (outfname != NULL) {
398 error("you may specify at most one -f");
401 if (*optarg == '/') {
402 outfname = stralloc(optarg);
404 outfname = vstralloc(my_cwd, "/", optarg, NULL);
408 if (logfname != NULL) {
409 error("you may specify at most one -l");
412 if (*optarg == '/') {
413 logfname = stralloc(optarg);
415 logfname = vstralloc(my_cwd, "/", optarg, NULL);
419 if (psfname != NULL) {
420 error("you may specify at most one -p");
423 if (*optarg == '/') {
424 psfname = stralloc(optarg);
426 psfname = vstralloc(my_cwd, "/", optarg, NULL);
440 if( !mailout && mailto ){
441 printf("You cannot specify both -i & -M at the same time\n");
448 printf("You must run amreport with '-f <output file>' because configure\n");
449 printf("didn't find a mailer.\n");
456 /* read configuration files */
458 conffile = stralloc2(config_dir, CONFFILE_NAME);
459 /* Ignore error from read_conffile */
460 read_conffile(conffile);
463 dbrename(config_name, DBG_SUBDIR_SERVER);
465 report_bad_conf_arg();
466 conf_diskfile = getconf_str(CNF_DISKFILE);
467 if (*conf_diskfile == '/') {
468 conf_diskfile = stralloc(conf_diskfile);
470 conf_diskfile = stralloc2(config_dir, conf_diskfile);
472 /* Ignore error from read_diskfile */
473 read_diskfile(conf_diskfile, &diskq);
474 amfree(conf_diskfile);
475 if(mailout && !mailto &&
476 getconf_seen(CNF_MAILTO) && strlen(getconf_str(CNF_MAILTO)) > 0) {
477 mailto = getconf_str(CNF_MAILTO);
478 if(!validate_mailto(mailto)){
483 conf_tapelist = getconf_str(CNF_TAPELIST);
484 if (*conf_tapelist == '/') {
485 conf_tapelist = stralloc(conf_tapelist);
487 conf_tapelist = stralloc2(config_dir, conf_tapelist);
489 /* Ignore error from read_tapelist */
490 read_tapelist(conf_tapelist);
491 amfree(conf_tapelist);
492 conf_infofile = getconf_str(CNF_INFOFILE);
493 if (*conf_infofile == '/') {
494 conf_infofile = stralloc(conf_infofile);
496 conf_infofile = stralloc2(config_dir, conf_infofile);
498 if(open_infofile(conf_infofile)) {
499 error("could not open info db \"%s\"", conf_infofile);
502 amfree(conf_infofile);
504 displayunit = getconf_str(CNF_DISPLAYUNIT);
505 unitdivisor = getconf_unit_divisor();
507 ColumnSpec = getconf_str(CNF_COLUMNSPEC);
508 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
510 curprog = P_REPORTER;
515 ColumnSpec = ""; /* use the default */
516 if(SetColumDataFromString(ColumnData, ColumnSpec, &errstr) < 0) {
518 curprog = P_REPORTER;
525 for (cn = 0; ColumnData[cn].Name != NULL; cn++) {
526 if (ColumnData[cn].MaxWidth) {
527 MaxWidthsRequested = 1;
535 conf_logdir = getconf_str(CNF_LOGDIR);
536 if (*conf_logdir == '/') {
537 conf_logdir = stralloc(conf_logdir);
539 conf_logdir = stralloc2(config_dir, conf_logdir);
541 logfname = vstralloc(conf_logdir, "/", "log", NULL);
545 if((logfile = fopen(logfname, "r")) == NULL) {
547 curprog = P_REPORTER;
548 curstr = vstralloc("could not open log ",
557 while(logfile && get_logline(logfile)) {
559 case L_START: handle_start(); break;
560 case L_FINISH: handle_finish(); break;
562 case L_INFO: handle_note(); break;
563 case L_WARNING: handle_note(); break;
565 case L_SUMMARY: handle_summary(); break;
566 case L_STATS: handle_stats(); break;
568 case L_ERROR: handle_error(); break;
569 case L_FATAL: handle_error(); break;
571 case L_DISK: handle_disk(); break;
573 case L_SUCCESS: handle_success(curlog); break;
574 case L_CHUNKSUCCESS: handle_success(curlog); break;
575 case L_CHUNK: handle_chunk(); break;
576 case L_PARTIAL: handle_partial(); break;
577 case L_STRANGE: handle_strange(); break;
578 case L_FAIL: handle_failed(); break;
582 curprog = P_REPORTER;
583 curstr = stralloc2("unexpected log line: ", curstr);
592 generate_bad_estimate();
595 subj_str = vstralloc(getconf_str(CNF_ORG),
596 " ", amflush_run ? "AMFLUSH" : "AMANDA",
597 " ", "MAIL REPORT FOR",
598 " ", nicedate(run_datestamp ? run_datestamp : "0"),
601 /* lookup the tapetype and printer type from the amanda.conf file. */
602 tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
603 printer = getconf_str(CNF_PRINTER);
605 /* ignore SIGPIPE so if a child process dies we do not also go away */
606 signal(SIGPIPE, SIG_IGN);
608 /* open pipe to mailer */
611 /* output to a file */
612 if((mailf = fopen(outfname,"w")) == NULL) {
613 error("could not open output file: %s %s", outfname, strerror(errno));
616 fprintf(mailf, "To: %s\n", mailto);
617 fprintf(mailf, "Subject: %s\n\n", subj_str);
622 mail_cmd = vstralloc(MAILER,
623 " -s", " \"", subj_str, "\"",
625 if((mailf = popen(mail_cmd, "w")) == NULL) {
626 error("could not open pipe to \"%s\": %s",
627 mail_cmd, strerror(errno));
633 printf("No mail sent! ");
634 printf("No valid mail address has been specified in amanda.conf or on the commmand line\n");
641 /* open pipe to print spooler if necessary) */
644 /* if the postscript_label_template (tp->lbl_templ) field is not */
645 /* the empty string (i.e. it is set to something), open the */
646 /* postscript debugging file for writing. */
648 lbl_templ = tapetype_get_lbl_templ(tp);
649 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
650 if ((postscript = fopen(psfname, "w")) == NULL) {
652 curprog = P_REPORTER;
653 curstr = vstralloc("could not open ",
664 if (strcmp(printer, "") != 0) /* alternate printer is defined */
665 /* print to the specified printer */
667 printer_cmd = vstralloc(LPRCMD, " ", LPRFLAG, printer, NULL);
669 printer_cmd = vstralloc(LPRCMD, NULL);
672 /* print to the default printer */
673 printer_cmd = vstralloc(LPRCMD, NULL);
676 lbl_templ = tapetype_get_lbl_templ(tp);
677 if (tp && lbl_templ && strcmp(lbl_templ, "") != 0) {
679 if ((postscript = popen(printer_cmd, "w")) == NULL) {
681 curprog = P_REPORTER;
682 curstr = vstralloc("could not open pipe to ",
692 curprog = P_REPORTER;
693 curstr = stralloc("no printer command defined");
704 if(!got_finish) fputs("*** THE DUMPS DID NOT FINISH PROPERLY!\n\n", mailf);
708 if(first_strange || errsum) {
709 fprintf(mailf,"\nFAILURE AND STRANGE DUMP SUMMARY:\n");
710 if(first_strange) output_strange();
711 if(errsum) output_lines(errsum, mailf);
713 fputs("\n\n", mailf);
718 fprintf(mailf,"\n\014\nFAILED AND STRANGE DUMP DETAILS:\n");
719 output_lines(errdet, mailf);
722 fprintf(mailf,"\n\014\nNOTES:\n");
723 output_lines(notes, mailf);
726 if(sortq.head != NULL) {
727 fprintf(mailf,"\n\014\nDUMP SUMMARY:\n");
730 fprintf(mailf,"\n(brought to you by Amanda version %s)\n",
735 do_postscript_output();
739 /* close postscript file */
740 if (psfname && postscript) {
741 /* it may be that postscript is NOT opened */
745 if (postscript != NULL && pclose(postscript) != 0) {
746 error("printer command failed: %s", printer_cmd);
752 /* close output file */
757 if(pclose(mailf) != 0) {
758 error("mail command failed: %s", mail_cmd);
765 free_disklist(&diskq);
766 free_new_argv(new_argc, new_argv);
767 free_server_config();
768 amfree(run_datestamp);
776 malloc_size_2 = malloc_inuse(&malloc_hist_2);
778 if(malloc_size_1 != malloc_size_2) {
779 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
788 #define mb(f) ((f)/1024) /* kbytes -> mbutes */
789 #define du(f) ((f)/unitdivisor) /* kbytes -> displayunit */
790 #define pct(f) ((f)*100.0) /* percent */
791 #define hrmn(f) ((int)(f)+30)/3600, (((int)(f)+30)%3600)/60
792 #define mnsc(f) ((int)(f+0.5))/60, ((int)(f+0.5)) % 60
794 #define divzero(fp,a,b) \
798 fprintf((fp)," -- "); \
799 else if ((q = (a)/q) >= 999.95) \
800 fprintf((fp), "###.#"); \
802 fprintf((fp), "%5.1lf",q); \
804 #define divzero_wide(fp,a,b) \
808 fprintf((fp)," -- "); \
809 else if ((q = (a)/q) >= 99999.95) \
810 fprintf((fp), "#####.#"); \
812 fprintf((fp), "%7.1lf",q); \
819 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
825 tapesize = tapetype_get_length(tp);
826 marksize = tapetype_get_filemark(tp);
828 tapesize = 100 * 1024 * 1024;
829 marksize = 1 * 1024 * 1024;
832 stats[2].dumpdisks = stats[0].dumpdisks + stats[1].dumpdisks;
833 stats[2].tapedisks = stats[0].tapedisks + stats[1].tapedisks;
834 stats[2].tapechunks = stats[0].tapechunks + stats[1].tapechunks;
835 stats[2].outsize = stats[0].outsize + stats[1].outsize;
836 stats[2].origsize = stats[0].origsize + stats[1].origsize;
837 stats[2].tapesize = stats[0].tapesize + stats[1].tapesize;
838 stats[2].coutsize = stats[0].coutsize + stats[1].coutsize;
839 stats[2].corigsize = stats[0].corigsize + stats[1].corigsize;
840 stats[2].taper_time = stats[0].taper_time + stats[1].taper_time;
841 stats[2].dumper_time = stats[0].dumper_time + stats[1].dumper_time;
843 if(!got_finish) /* no driver finish line, estimate total run time */
844 total_time = stats[2].taper_time + planner_time;
846 idle_time = (total_time - startup_time) - stats[2].taper_time;
847 if(idle_time < 0) idle_time = 0.0;
849 fprintf(mailf,"STATISTICS:\n");
851 " Total Full Incr.\n");
853 " -------- -------- --------\n");
856 "Estimate Time (hrs:min) %2d:%02d\n", hrmn(planner_time));
859 "Run Time (hrs:min) %2d:%02d\n", hrmn(total_time));
862 "Dump Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n",
863 hrmn(stats[2].dumper_time), hrmn(stats[0].dumper_time),
864 hrmn(stats[1].dumper_time));
867 "Output Size (meg) %8.1lf %8.1lf %8.1lf\n",
868 mb(stats[2].outsize), mb(stats[0].outsize), mb(stats[1].outsize));
871 "Original Size (meg) %8.1lf %8.1lf %8.1lf\n",
872 mb(stats[2].origsize), mb(stats[0].origsize),
873 mb(stats[1].origsize));
875 fprintf(mailf, "Avg Compressed Size (%%) ");
876 divzero(mailf, pct(stats[2].coutsize),stats[2].corigsize);
878 divzero(mailf, pct(stats[0].coutsize),stats[0].corigsize);
880 divzero(mailf, pct(stats[1].coutsize),stats[1].corigsize);
882 if(stats[1].dumpdisks > 0) fputs(" (level:#disks ...)", mailf);
886 "Filesystems Dumped %4d %4d %4d",
887 stats[2].dumpdisks, stats[0].dumpdisks, stats[1].dumpdisks);
889 if(stats[1].dumpdisks > 0) {
891 for(lv = 1; lv < 10; lv++) if(dumpdisks[lv]) {
892 fputs(first?" (":" ", mailf);
894 fprintf(mailf, "%d:%d", lv, dumpdisks[lv]);
900 fprintf(mailf, "Avg Dump Rate (k/s) ");
901 divzero_wide(mailf, stats[2].outsize,stats[2].dumper_time);
903 divzero_wide(mailf, stats[0].outsize,stats[0].dumper_time);
905 divzero_wide(mailf, stats[1].outsize,stats[1].dumper_time);
910 "Tape Time (hrs:min) %2d:%02d %2d:%02d %2d:%02d\n",
911 hrmn(stats[2].taper_time), hrmn(stats[0].taper_time),
912 hrmn(stats[1].taper_time));
915 "Tape Size (meg) %8.1lf %8.1lf %8.1lf\n",
916 mb(stats[2].tapesize), mb(stats[0].tapesize),
917 mb(stats[1].tapesize));
919 fprintf(mailf, "Tape Used (%%) ");
920 divzero(mailf, pct(stats[2].tapesize+marksize*(stats[2].tapedisks+stats[2].tapechunks)),(double)tapesize);
922 divzero(mailf, pct(stats[0].tapesize+marksize*(stats[0].tapedisks+stats[0].tapechunks)),(double)tapesize);
924 divzero(mailf, pct(stats[1].tapesize+marksize*(stats[1].tapedisks+stats[1].tapechunks)),(double)tapesize);
926 if(stats[1].tapedisks > 0) fputs(" (level:#disks ...)", mailf);
930 "Filesystems Taped %4d %4d %4d",
931 stats[2].tapedisks, stats[0].tapedisks, stats[1].tapedisks);
933 if(stats[1].tapedisks > 0) {
935 for(lv = 1; lv < 10; lv++) if(tapedisks[lv]) {
936 fputs(first?" (":" ", mailf);
938 fprintf(mailf, "%d:%d", lv, tapedisks[lv]);
944 if(stats[1].tapechunks > 0) fputs(" (level:#chunks ...)", mailf);
948 "Chunks Taped %4d %4d %4d",
949 stats[2].tapechunks, stats[0].tapechunks, stats[1].tapechunks);
951 if(stats[1].tapechunks > 0) {
953 for(lv = 1; lv < 10; lv++) if(tapechunks[lv]) {
954 fputs(first?" (":" ", mailf);
956 fprintf(mailf, "%d:%d", lv, tapechunks[lv]);
962 fprintf(mailf, "Avg Tp Write Rate (k/s) ");
963 divzero_wide(mailf, stats[2].tapesize,stats[2].taper_time);
965 divzero_wide(mailf, stats[0].tapesize,stats[0].taper_time);
967 divzero_wide(mailf, stats[1].tapesize,stats[1].taper_time);
971 int label_length = (int)strlen(stats_by_tape->label) + 5;
972 fprintf(mailf,"\nUSAGE BY TAPE:\n");
973 fprintf(mailf," %-*s Time Size %% Nb Nc\n",
974 label_length, "Label");
975 for(current_tape = stats_by_tape; current_tape != NULL;
976 current_tape = current_tape->next) {
977 fprintf(mailf, " %-*s", label_length, current_tape->label);
978 fprintf(mailf, " %2d:%02d", hrmn(current_tape->taper_time));
979 fprintf(mailf, " %8.0lf%s ", du(current_tape->coutsize), displayunit);
980 divzero(mailf, pct(current_tape->coutsize + marksize *
981 (current_tape->tapedisks+current_tape->tapechunks)),
983 fprintf(mailf, " %4d", current_tape->tapedisks);
984 fprintf(mailf, " %4d\n", current_tape->tapechunks);
992 output_tapeinfo(void)
998 if (last_run_tapes > 0) {
1000 fprintf(mailf, "The dumps were flushed to tape%s %s.\n",
1001 last_run_tapes == 1 ? "" : "s",
1002 tape_labels ? tape_labels : "");
1004 fprintf(mailf, "These dumps were to tape%s %s.\n",
1005 last_run_tapes == 1 ? "" : "s",
1006 tape_labels ? tape_labels : "");
1011 "*** A TAPE ERROR OCCURRED: %s.\n", tapestart_error);
1012 fputs("Some dumps may have been left in the holding disk.\n", mailf);
1014 "Run amflush%s to flush them to tape.\n",
1015 amflush_run ? " again" : "");
1018 tp = lookup_last_reusable_tape(skip);
1020 run_tapes = getconf_int(CNF_RUNTAPES);
1023 fputs("The next tape Amanda expects to use is: ", mailf);
1024 else if(run_tapes > 1)
1025 fprintf(mailf, "The next %d tapes Amanda expects to use are: ",
1028 while(run_tapes > 0) {
1030 fprintf(mailf, "%s", tp->label);
1033 fprintf(mailf, "a new tape");
1035 fprintf(mailf, "%d new tapes", run_tapes);
1039 if(run_tapes > 1) fputs(", ", mailf);
1043 tp = lookup_last_reusable_tape(skip);
1045 fputs(".\n", mailf);
1047 lasttp = lookup_tapepos(lookup_nb_tape());
1048 run_tapes = getconf_int(CNF_RUNTAPES);
1049 if(lasttp && run_tapes > 0 && strcmp(lasttp->datestamp,"0") == 0) {
1051 while(lasttp && run_tapes > 0 && strcmp(lasttp->datestamp,"0") == 0) {
1053 lasttp = lasttp->prev;
1056 lasttp = lookup_tapepos(lookup_nb_tape());
1058 fprintf(mailf, "The next new tape already labelled is: %s.\n",
1062 fprintf(mailf, "The next %d new tapes already labelled are: %s", c,
1064 lasttp = lasttp->prev;
1066 while(lasttp && c > 0 && strcmp(lasttp->datestamp,"0") == 0) {
1067 fprintf(mailf, ", %s", lasttp->label);
1068 lasttp = lasttp->prev;
1071 fprintf(mailf, ".\n");
1078 output_strange(void)
1080 size_t len_host=0, len_disk=0;
1084 for(strange=first_strange; strange != NULL; strange = strange->next) {
1085 if(strlen(strange->hostname) > len_host)
1086 len_host = strlen(strange->hostname);
1087 if(strlen(strange->diskname) > len_disk)
1088 len_disk = strlen(strange->diskname);
1090 for(strange=first_strange; strange != NULL; strange = strange->next) {
1091 str = vstralloc(" ", prefixstrange(strange->hostname, strange->diskname, strange->level, len_host, len_disk),
1092 " ", strange->str, NULL);
1093 fprintf(mailf, "%s\n", str);
1124 rc = strcmp(a->host->hostname, b->host->hostname);
1125 if(rc == 0) rc = strcmp(a->name, b->name);
1134 sortq.head = sortq.tail = NULL;
1135 while(!empty(diskq)) {
1136 dp = dequeue_disk(&diskq);
1137 if(data(dp) == NULL) { /* create one */
1138 find_repdata(dp, run_datestamp, 0);
1140 insert_disk(&sortq, dp, sort_by_name);
1150 int l = (int)strlen(s);
1166 snprintf(testBuf, SIZEOF(testBuf),
1167 cd->Format, cd->Width, cd->Precision, n);
1168 l = (int)strlen(testBuf);
1182 snprintf(testBuf, SIZEOF(testBuf),
1183 cd->Format, cd->Width, cd->Precision, d);
1184 l = (int)strlen(testBuf);
1190 static int HostName;
1195 static int Compress;
1196 static int DumpTime;
1197 static int DumpRate;
1198 static int TapeTime;
1199 static int TapeRate;
1204 /* we have to look for columspec's, that require the recalculation.
1205 * we do here the same loops over the sortq as is done in
1206 * output_summary. So, if anything is changed there, we have to
1207 * change this here also.
1213 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1215 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1216 char TimeRateBuffer[40];
1218 CheckStringMax(&ColumnData[HostName], dp->host->hostname);
1219 CheckStringMax(&ColumnData[Disk], dp->name);
1220 if (repdata->dumper.result == L_BOGUS &&
1221 repdata->taper.result == L_BOGUS)
1223 CheckIntMax(&ColumnData[Level], repdata->level);
1224 if(repdata->dumper.result == L_SUCCESS ||
1225 repdata->dumper.result == L_CHUNKSUCCESS) {
1226 CheckFloatMax(&ColumnData[OrigKB],
1227 (double)du(repdata->dumper.origsize));
1228 CheckFloatMax(&ColumnData[OutKB],
1229 (double)du(repdata->dumper.outsize));
1230 if(dp->compress == COMP_NONE)
1233 f = repdata->dumper.origsize;
1234 CheckStringMax(&ColumnData[Disk],
1235 sDivZero(pct(repdata->dumper.outsize), f, Compress));
1238 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1239 "%3d:%02d", mnsc(repdata->dumper.sec));
1241 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1243 CheckStringMax(&ColumnData[DumpTime], TimeRateBuffer);
1245 CheckFloatMax(&ColumnData[DumpRate], repdata->dumper.kps);
1248 if(repdata->taper.result == L_FAIL) {
1249 CheckStringMax(&ColumnData[TapeTime], "FAILED");
1252 if(repdata->taper.result == L_SUCCESS ||
1253 repdata->taper.result == L_CHUNKSUCCESS)
1254 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1255 "%3d:%02d", mnsc(repdata->taper.sec));
1257 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1259 CheckStringMax(&ColumnData[TapeTime], TimeRateBuffer);
1261 if(repdata->taper.result == L_SUCCESS ||
1262 repdata->taper.result == L_CHUNKSUCCESS)
1263 CheckFloatMax(&ColumnData[TapeRate], repdata->taper.kps);
1265 CheckStringMax(&ColumnData[TapeRate], "N/A ");
1272 output_summary(void)
1276 char *ds="DUMPER STATS";
1277 char *ts=" TAPER STATS";
1280 int i, h, w1, wDump, wTape;
1281 double outsize, origsize;
1284 HostName = StringToColumn("HostName");
1285 Disk = StringToColumn("Disk");
1286 Level = StringToColumn("Level");
1287 OrigKB = StringToColumn("OrigKB");
1288 OutKB = StringToColumn("OutKB");
1289 Compress = StringToColumn("Compress");
1290 DumpTime = StringToColumn("DumpTime");
1291 DumpRate = StringToColumn("DumpRate");
1292 TapeTime = StringToColumn("TapeTime");
1293 TapeRate = StringToColumn("TapeRate");
1295 /* at first determine if we have to recalculate our widths */
1296 if (MaxWidthsRequested)
1299 /* title for Dumper-Stats */
1300 w1= ColWidth(HostName, Level);
1301 wDump= ColWidth(OrigKB, DumpRate);
1302 wTape= ColWidth(TapeTime, TapeRate);
1304 /* print centered top titles */
1305 h = (int)strlen(ds);
1311 fprintf(mailf, "%*s", w1+h, "");
1312 fprintf(mailf, "%-*s", wDump-h, ds);
1313 h = (int)strlen(ts);
1319 fprintf(mailf, "%*s", h, "");
1320 fprintf(mailf, "%-*s", wTape-h, ts);
1323 /* print the titles */
1324 for (i=0; ColumnData[i].Name != NULL; i++) {
1326 ColumnInfo *cd= &ColumnData[i];
1327 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1328 if (cd->Format[1] == '-')
1332 if(strcmp(cd->Title,"ORIG-KB") == 0) {
1333 /* cd->Title must be re-allocated in write-memory */
1334 cd->Title = stralloc("ORIG-KB");
1335 cd->Title[5] = displayunit[0];
1337 if(strcmp(cd->Title,"OUT-KB") == 0) {
1338 /* cd->Title must be re-allocated in write-memory */
1339 cd->Title = stralloc("OUT-KB");
1340 cd->Title[4] = displayunit[0];
1342 fprintf(mailf, fmt, cd->Width, cd->Title);
1346 /* print the rules */
1347 fputs(tmp=Rule(HostName, Level), mailf); amfree(tmp);
1348 fputs(tmp=Rule(OrigKB, DumpRate), mailf); amfree(tmp);
1349 fputs(tmp=Rule(TapeTime, TapeRate), mailf); amfree(tmp);
1352 for(dp = sortq.head; dp != NULL; dp = dp->next) {
1355 char TimeRateBuffer[40];
1356 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
1360 cd= &ColumnData[HostName];
1361 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1362 fprintf(mailf, cd->Format, cd->Width, cd->Width, dp->host->hostname);
1364 cd= &ColumnData[Disk];
1365 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1366 devname = sanitize_string(dp->name);
1367 devlen = strlen(devname);
1368 if (devlen > (size_t)cd->Width) {
1370 fprintf(mailf, cd->Format, cd->Width-1, cd->Precision-1,
1371 devname+devlen - (cd->Width-1) );
1374 fprintf(mailf, cd->Format, cd->Width, cd->Width, devname);
1376 cd= &ColumnData[Level];
1377 if (repdata->dumper.result == L_BOGUS &&
1378 repdata->taper.result == L_BOGUS) {
1380 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1381 tmp=TextRule(OrigKB, TapeRate, "NO FILE TO FLUSH"));
1383 fprintf(mailf, "%*s%s\n", cd->PrefixSpace+cd->Width, "",
1384 tmp=TextRule(OrigKB, TapeRate, "MISSING"));
1390 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1391 fprintf(mailf, cd->Format, cd->Width, cd->Precision,repdata->level);
1393 if (repdata->dumper.result == L_SKIPPED) {
1394 fprintf(mailf, "%s\n",
1395 tmp=TextRule(OrigKB, TapeRate, "SKIPPED"));
1399 if (repdata->dumper.result == L_FAIL && (repdata->chunker.result != L_PARTIAL && repdata->taper.result != L_PARTIAL)) {
1400 fprintf(mailf, "%s\n",
1401 tmp=TextRule(OrigKB, TapeRate, "FAILED"));
1403 exit_status |= STATUS_FAILED;
1407 if(repdata->dumper.result == L_SUCCESS ||
1408 repdata->dumper.result == L_CHUNKSUCCESS)
1409 origsize = repdata->dumper.origsize;
1410 else if(repdata->taper.result == L_SUCCESS ||
1411 repdata->taper.result == L_PARTIAL)
1412 origsize = repdata->taper.origsize;
1414 origsize = repdata->chunker.origsize;
1416 if(repdata->taper.result == L_SUCCESS ||
1417 repdata->taper.result == L_PARTIAL ||
1418 repdata->taper.result == L_CHUNKSUCCESS)
1419 outsize = repdata->taper.outsize;
1420 else if(repdata->chunker.result == L_SUCCESS ||
1421 repdata->chunker.result == L_PARTIAL ||
1422 repdata->chunker.result == L_CHUNKSUCCESS)
1423 outsize = repdata->chunker.outsize;
1425 outsize = repdata->dumper.outsize;
1427 cd= &ColumnData[OrigKB];
1428 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1429 if(isnormal(origsize))
1430 fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(origsize));
1432 fprintf(mailf, "%*.*s", cd->Width, cd->Width, "N/A");
1434 cd= &ColumnData[OutKB];
1435 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1437 fprintf(mailf, cd->Format, cd->Width, cd->Precision, du(outsize));
1439 cd= &ColumnData[Compress];
1440 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1442 if(dp->compress == COMP_NONE)
1444 else if(origsize < 1.0)
1449 fputs(sDivZero(pct(outsize), f, Compress), mailf);
1451 cd= &ColumnData[DumpTime];
1452 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1453 if(repdata->dumper.result == L_SUCCESS ||
1454 repdata->dumper.result == L_CHUNKSUCCESS)
1455 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1456 "%3d:%02d", mnsc(repdata->dumper.sec));
1458 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1460 fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1462 cd= &ColumnData[DumpRate];
1463 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1464 if(repdata->dumper.result == L_SUCCESS ||
1465 repdata->dumper.result == L_CHUNKSUCCESS)
1466 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->dumper.kps);
1468 fprintf(mailf, "%*s", cd->Width, "N/A ");
1470 cd= &ColumnData[TapeTime];
1471 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1472 if(repdata->taper.result == L_FAIL) {
1473 fprintf(mailf, "%s\n",
1474 tmp=TextRule(TapeTime, TapeRate, "FAILED "));
1479 if(repdata->taper.result == L_SUCCESS ||
1480 repdata->taper.result == L_PARTIAL ||
1481 repdata->taper.result == L_CHUNKSUCCESS)
1482 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1483 "%3d:%02d", mnsc(repdata->taper.sec));
1485 snprintf(TimeRateBuffer, SIZEOF(TimeRateBuffer),
1487 fprintf(mailf, cd->Format, cd->Width, cd->Width, TimeRateBuffer);
1489 cd= &ColumnData[TapeRate];
1490 fprintf(mailf, "%*s", cd->PrefixSpace, "");
1491 if(repdata->taper.result == L_SUCCESS ||
1492 repdata->taper.result == L_PARTIAL ||
1493 repdata->taper.result == L_CHUNKSUCCESS)
1494 fprintf(mailf, cd->Format, cd->Width, cd->Precision, repdata->taper.kps);
1496 fprintf(mailf, "%*s", cd->Width, "N/A ");
1498 if(repdata->chunker.result == L_PARTIAL ||
1499 repdata->taper.result == L_PARTIAL) {
1500 fprintf(mailf, " PARTIAL");
1510 const char *err_text)
1512 printf("line %d of log is bogus: <%s>\n", curlinenum, curstr);
1513 printf(" Scan failed at: <%s>\n", err_text);
1518 * Formats an integer of the form YYYYMMDDHHMMSS into the string
1519 * "Monthname DD, YYYY". A pointer to the statically allocated string
1520 * is returned, so it must be copied to other storage (or just printed)
1521 * before calling nicedate() again.
1525 const char *datestamp)
1527 static char nice[64];
1530 static char *months[13] = { "BogusMonth",
1531 "January", "February", "March", "April", "May", "June",
1532 "July", "August", "September", "October", "November", "December"
1534 int year, month, day;
1536 strncpy(date, datestamp, 8);
1538 numdate = atoi(date);
1539 year = numdate / 10000;
1540 day = numdate % 100;
1541 month = (numdate / 100) % 100;
1545 snprintf(nice, SIZEOF(nice), "%s %d, %d", months[month], day, year);
1553 static int started = 0;
1563 skip_whitespace(s, ch);
1564 #define sc "datestamp"
1565 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1572 skip_whitespace(s, ch);
1578 skip_non_whitespace(s, ch);
1580 run_datestamp = newstralloc(run_datestamp, fp);
1583 skip_whitespace(s, ch);
1585 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1592 skip_whitespace(s, ch);
1598 skip_non_whitespace(s, ch);
1601 label = stralloc(fp);
1605 fp = vstralloc(tape_labels, ", ", label, NULL);
1606 amfree(tape_labels);
1609 tape_labels = stralloc(label);
1614 if(stats_by_tape == NULL) {
1615 stats_by_tape = current_tape = (taper_t *)alloc(SIZEOF(taper_t));
1618 current_tape->next = (taper_t *)alloc(SIZEOF(taper_t));
1619 current_tape = current_tape->next;
1621 current_tape->label = label;
1622 current_tape->taper_time = 0.0;
1623 current_tape->coutsize = 0.0;
1624 current_tape->corigsize = 0.0;
1625 current_tape->tapedisks = 0;
1626 current_tape->tapechunks = 0;
1627 current_tape->next = NULL;
1647 skip_whitespace(s, ch);
1649 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1650 return; /* ignore bogus line */
1655 skip_whitespace(s, ch);
1661 skip_non_whitespace(s, ch);
1663 run_datestamp = newstralloc(run_datestamp, fp);
1668 if(amflush_run && normal_run) {
1671 " reporter: both amflush and planner output in log, ignoring amflush.");
1683 if(curprog == P_DRIVER || curprog == P_AMFLUSH || curprog == P_PLANNER) {
1687 skip_whitespace(s, ch);
1689 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1697 skip_whitespace(s, ch);
1702 skip_non_whitespace(s, ch); /* ignore the date string */
1704 skip_whitespace(s, ch);
1706 if(ch == '\0' || strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
1707 /* older planner doesn't write time */
1708 if(curprog == P_PLANNER) return;
1716 skip_whitespace(s, ch);
1721 if(sscanf(s - 1, "%lf", &a_time) != 1) {
1725 if(curprog == P_PLANNER) {
1726 planner_time = a_time;
1729 total_time = a_time;
1740 char *hostname, *diskname, *datestamp;
1742 double sec, kps, nbytes, cbytes;
1746 if(curprog == P_DRIVER) {
1750 skip_whitespace(s, ch);
1751 #define sc "startup time"
1752 if(ch != '\0' && strncmp(s - 1, sc, sizeof(sc)-1) == 0) {
1757 skip_whitespace(s, ch);
1762 if(sscanf(s - 1, "%lf", &startup_time) != 1) {
1766 planner_time = startup_time;
1768 #define sc "estimate"
1769 else if(ch != '\0' && strncmp(s - 1, sc, sizeof(sc)-1) == 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 #define sc "no-tape"
1892 if(ch != '\0' && strncmp(s - 1, sc, SIZEOF(sc)-1) == 0) {
1897 skip_whitespace(s, ch);
1899 if((nl = strchr(s - 1, '\n')) != NULL) {
1902 tapestart_error = newstralloc(tapestart_error, s - 1);
1905 exit_status |= STATUS_TAPE;;
1908 /* else some other tape error, handle like other errors */
1910 /* else some other tape error, handle like other errors */
1912 s = vstralloc(" ", program_str[curprog], ": ",
1913 logtype_str[curlog], " ", curstr, NULL);
1914 addline(&errsum, s);
1921 handle_summary(void)
1928 static int nb_disk=0;
1933 char *s, *fp, *qdiskname;
1935 char *hostname = NULL, *diskname = NULL;
1937 if(curprog != P_PLANNER && curprog != P_AMFLUSH) {
1943 for(dp = diskq.head; dp != NULL; dp = dp->next)
1951 skip_whitespace(s, ch);
1957 skip_non_whitespace(s, ch);
1959 hostname = newstralloc(hostname, fp);
1962 skip_whitespace(s, ch);
1969 skip_quoted_string(s, ch);
1971 diskname = unquote_string(qdiskname);
1974 dp = lookup_disk(hostname, diskname);
1976 dp = add_disk(&diskq, hostname, diskname);
1984 /* XXX Just a placeholder, in case we decide to do something with L_CHUNK
1985 * log entries. Right now they're just the equivalent of L_SUCCESS, but only
1986 * for a split chunk of the overall dumpfile.
1992 double sec, kps, kbytes;
1997 char *hostname = NULL;
1998 char *diskname = NULL;
2003 if(curprog != P_TAPER) {
2011 skip_whitespace(s, ch);
2017 skip_non_whitespace(s, ch);
2019 hostname = stralloc(fp);
2022 skip_whitespace(s, ch);
2029 skip_quoted_string(s, ch);
2031 diskname = unquote_string(fp);
2034 skip_whitespace(s, ch);
2042 skip_non_whitespace(s, ch);
2044 datestamp = stralloc(fp);
2047 skip_whitespace(s, ch);
2048 if(ch == '\0' || sscanf(s - 1, "%d", &chunk) != 1) {
2055 skip_integer(s, ch);
2057 skip_whitespace(s, ch);
2058 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2065 skip_integer(s, ch);
2068 if(level < 0 || level > 9) {
2076 skip_whitespace(s, ch);
2077 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf", &sec, &kbytes, &kps) != 3) {
2086 dp = lookup_disk(hostname, diskname);
2090 str = vstralloc(" ", prefix(hostname, diskname, level),
2091 " ", "ERROR [not in disklist]",
2093 addline(&errsum, str);
2101 repdata = find_repdata(dp, datestamp, level);
2103 sp = &(repdata->taper);
2111 if(current_tape == NULL) {
2112 error("current_tape == NULL");
2114 if (sp->filenum == 0) {
2115 sp->filenum = ++tapefcount;
2116 sp->tapelabel = current_tape->label;
2118 tapechunks[level] +=1;
2119 stats[i].tapechunks +=1;
2120 current_tape->taper_time += sec;
2121 current_tape->coutsize += kbytes;
2122 current_tape->tapechunks += 1;
2133 double kbytes = 0.0;
2134 double origkb = 0.0;
2137 char *s, *fp, *qdiskname;
2139 char *hostname = NULL;
2140 char *diskname = NULL;
2145 if(curprog != P_TAPER && curprog != P_DUMPER && curprog != P_PLANNER &&
2146 curprog != P_CHUNKER) {
2154 skip_whitespace(s, ch);
2160 skip_non_whitespace(s, ch);
2162 hostname = stralloc(fp);
2165 skip_whitespace(s, ch);
2172 skip_quoted_string(s, ch);
2174 diskname = unquote_string(qdiskname);
2176 skip_whitespace(s, ch);
2184 skip_non_whitespace(s, ch);
2186 datestamp = stralloc(fp);
2189 if(strlen(datestamp) < 3) {
2190 level = atoi(datestamp);
2191 datestamp = newstralloc(datestamp, run_datestamp);
2194 skip_whitespace(s, ch);
2195 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2202 skip_integer(s, ch);
2205 if(level < 0 || level > 9) {
2212 skip_whitespace(s, ch);
2213 /* Planner success messages (for skipped
2214 dumps) do not contain statistics */
2215 if(curprog != P_PLANNER) {
2218 if((curprog != P_DUMPER)
2219 || (sscanf(s - 1,"[sec %lf kb %lf kps %lf orig-kb %lf",
2220 &sec, &kbytes, &kps, &origkb) != 4)) {
2222 if(sscanf(s - 1,"[sec %lf kb %lf kps %lf",
2223 &sec, &kbytes, &kps) != 3) {
2232 if(!isnormal(origkb))
2238 dp = lookup_disk(hostname, diskname);
2240 addtostrange(hostname, qdiskname, level, "ERROR [not in disklist]");
2241 exit_status |= STATUS_FAILED;
2248 repdata = find_repdata(dp, datestamp, level);
2250 if(curprog == P_PLANNER) {
2251 repdata->dumper.result = L_SKIPPED;
2258 if(curprog == P_TAPER)
2259 sp = &(repdata->taper);
2260 else if(curprog == P_DUMPER)
2261 sp = &(repdata->dumper);
2262 else sp = &(repdata->chunker);
2271 get_info(hostname, diskname, &inf);
2272 tm = localtime(&inf.inf[level].date);
2274 Idatestamp = 10000*(tm->tm_year+1900) +
2275 100*(tm->tm_mon+1) + tm->tm_mday;
2277 Idatestamp = 19000101;
2280 if(atoi(datestamp) == Idatestamp) {
2281 /* grab original size from record */
2282 origkb = (double)inf.inf[level].size;
2288 if (curprog == P_DUMPER &&
2289 (sp->result == L_FAIL || sp->result == L_PARTIAL)) {
2290 addtostrange(hostname, qdiskname, level, "was successfully retried");
2297 sp->result = L_SUCCESS;
2298 sp->datestamp = repdata->datestamp;
2301 sp->origsize = origkb;
2302 sp->outsize = kbytes;
2304 if(curprog == P_TAPER) {
2305 if(current_tape == NULL) {
2306 error("current_tape == NULL");
2309 stats[i].taper_time += sec;
2310 sp->filenum = ++tapefcount;
2311 sp->tapelabel = current_tape->label;
2312 tapedisks[level] +=1;
2313 stats[i].tapedisks +=1;
2314 stats[i].tapesize += kbytes;
2315 sp->outsize = kbytes;
2316 if(!isnormal(repdata->chunker.outsize) && isnormal(repdata->dumper.outsize)) { /* dump to tape */
2317 stats[i].outsize += kbytes;
2318 if(dp->compress != COMP_NONE) {
2319 stats[i].coutsize += kbytes;
2322 if (logtype == L_SUCCESS || logtype== L_PARTIAL) {
2323 current_tape->taper_time += sec;
2324 current_tape->coutsize += kbytes;
2326 current_tape->corigsize += origkb;
2327 current_tape->tapedisks += 1;
2330 if(curprog == P_DUMPER) {
2331 stats[i].dumper_time += sec;
2332 if(dp->compress == COMP_NONE) {
2333 sp->origsize = kbytes;
2336 stats[i].corigsize += sp->origsize;
2338 dumpdisks[level] +=1;
2339 stats[i].dumpdisks +=1;
2340 stats[i].origsize += sp->origsize;
2343 if(curprog == P_CHUNKER) {
2344 sp->outsize = kbytes;
2345 stats[i].outsize += kbytes;
2346 if(dp->compress != COMP_NONE) {
2347 stats[i].coutsize += kbytes;
2354 handle_partial(void)
2359 repdata = handle_success(L_PARTIAL);
2363 if(curprog == P_TAPER)
2364 sp = &(repdata->taper);
2365 else if(curprog == P_DUMPER)
2366 sp = &(repdata->dumper);
2367 else sp = &(repdata->chunker);
2369 sp->result = L_PARTIAL;
2373 handle_strange(void)
2376 char *strangestr = NULL;
2380 repdata = handle_success(L_SUCCESS);
2384 qdisk = quote_string(repdata->disk->name);
2386 addline(&errdet,"");
2387 str = vstralloc("/-- ", prefix(repdata->disk->host->hostname,
2388 qdisk, repdata->level),
2391 addline(&errdet, str);
2394 while(contline_next()) {
2395 get_logline(logfile);
2396 #define sc "sendbackup: warning "
2397 if(strncmp(curstr, sc, SIZEOF(sc)-1) == 0) {
2398 strangestr = newstralloc(strangestr, curstr+SIZEOF(sc)-1);
2400 addline(&errdet, curstr);
2402 addline(&errdet,"\\--------");
2404 str = vstralloc("STRANGE", " ", strangestr, NULL);
2405 addtostrange(repdata->disk->host->hostname, qdisk, repdata->level, str);
2406 exit_status |= STATUS_STRANGE;
2421 char *s, *fp, *qdiskname;
2433 skip_whitespace(s, ch);
2439 skip_non_whitespace(s, ch);
2442 skip_whitespace(s, ch);
2448 skip_quoted_string(s, ch);
2450 diskname = unquote_string(qdiskname);
2452 skip_whitespace(s, ch);
2459 skip_non_whitespace(s, ch);
2461 datestamp = stralloc(fp);
2463 if(strlen(datestamp) < 3) { /* there is no datestamp, it's the level */
2464 level = atoi(datestamp);
2465 datestamp = newstralloc(datestamp, run_datestamp);
2467 else { /* read the level */
2468 skip_whitespace(s, ch);
2469 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2475 skip_integer(s, ch);
2478 skip_whitespace(s, ch);
2486 if((s = strchr(errstr, '\n')) != NULL) {
2490 dp = lookup_disk(hostname, diskname);
2493 addtostrange(hostname, qdiskname, level, "ERROR [not in disklist]");
2495 repdata = find_repdata(dp, datestamp, level);
2497 if(curprog == P_TAPER)
2498 sp = &(repdata->taper);
2499 else sp = &(repdata->dumper);
2501 if(sp->result != L_SUCCESS)
2502 sp->result = L_FAIL;
2506 str = vstralloc("FAILED", " ", errstr, NULL);
2507 addtostrange(hostname, qdiskname, level, str);
2510 if(curprog == P_DUMPER) {
2511 addline(&errdet,"");
2512 str = vstralloc("/-- ", prefix(hostname, qdiskname, level),
2516 addline(&errdet, str);
2518 while(contline_next()) {
2519 get_logline(logfile);
2520 addline(&errdet, curstr);
2522 addline(&errdet,"\\--------");
2523 exit_status |= STATUS_FAILED;
2530 generate_missing(void)
2535 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2536 if(dp->todo && data(dp) == NULL) {
2537 qdisk = quote_string(dp->name);
2538 addtostrange(dp->host->hostname, qdisk, -987, "RESULTS MISSING");
2539 exit_status |= STATUS_MISSING;
2546 generate_bad_estimate(void)
2553 for(dp = diskq.head; dp != NULL; dp = dp->next) {
2555 for(repdata = data(dp); repdata != NULL; repdata = repdata->next) {
2556 if(repdata->est_csize >= 0.1) {
2557 if(repdata->taper.result == L_SUCCESS ||
2558 repdata->taper.result == L_PARTIAL ||
2559 repdata->taper.result == L_CHUNKSUCCESS)
2560 outsize = repdata->taper.outsize;
2561 else if(repdata->chunker.result == L_SUCCESS ||
2562 repdata->chunker.result == L_PARTIAL ||
2563 repdata->chunker.result == L_CHUNKSUCCESS)
2564 outsize = repdata->chunker.outsize;
2566 outsize = repdata->dumper.outsize;
2568 if(repdata->est_csize * 0.9 > outsize) {
2570 " big estimate: %s %s %d",
2571 repdata->disk->host->hostname,
2572 repdata->disk->name,
2577 " est: %.0lf%s out %.0lf%s",
2578 du(repdata->est_csize), displayunit,
2579 du(outsize), displayunit);
2583 else if(repdata->est_csize * 1.1 < outsize) {
2585 " small estimate: %s %s %d",
2586 repdata->disk->host->hostname,
2587 repdata->disk->name,
2592 " est: %.0lf%s out %.0lf%s",
2593 du(repdata->est_csize), displayunit,
2594 du(outsize), displayunit);
2610 char number[NUM_STR_SIZE];
2611 static char *str = NULL;
2613 snprintf(number, SIZEOF(number), "%d", level);
2614 str = newvstralloc(str,
2615 " ", host ? host : "(host?)",
2616 " ", disk ? disk : "(disk?)",
2617 level != -987 ? " lev " : "",
2618 level != -987 ? number : "",
2634 char number[NUM_STR_SIZE];
2635 static char *str = NULL;
2637 snprintf(number, SIZEOF(number), "%d", level);
2638 h=alloc(len_host+1);
2640 strncpy(h, host, len_host);
2642 strncpy(h, "(host?)", len_host);
2645 for(l = strlen(h); l < len_host; l++) {
2648 d=alloc(len_disk+1);
2650 strncpy(d, disk, len_disk);
2652 strncpy(d, "(disk?)", len_disk);
2655 for(l = strlen(d); l < len_disk; l++) {
2658 str = newvstralloc(str,
2661 level != -987 ? " lev " : "",
2662 level != -987 ? number : "",
2679 strange = alloc(SIZEOF(strange_t));
2680 strange->hostname = stralloc(host);
2681 strange->diskname = stralloc(disk);
2682 strange->level = level;
2683 strange->str = stralloc(str);
2684 strange->next = NULL;
2685 if(first_strange == NULL) {
2686 first_strange = strange;
2689 last_strange->next = strange;
2691 last_strange = strange;
2703 if (strchr(lbl_templ, '/') == NULL) {
2704 lbl_templ = stralloc2(config_dir, lbl_templ);
2706 lbl_templ = stralloc(lbl_templ);
2708 if ((fd = open(lbl_templ, 0)) < 0) {
2710 curprog = P_REPORTER;
2711 curstr = vstralloc("could not open PostScript template file ",
2719 afclose(postscript);
2722 while ((numread = read(fd, buf, SIZEOF(buf))) > 0) {
2723 if (fwrite(buf, (size_t)numread, 1, postscript) != 1) {
2725 curprog = P_REPORTER;
2726 curstr = vstralloc("error copying PostScript template file ",
2734 afclose(postscript);
2740 curprog = P_REPORTER;
2741 curstr = vstralloc("error reading PostScript template file ",
2749 afclose(postscript);
2758 /*@keep@*/ disk_t *dp,
2762 repdata_t *repdata, *prev;
2765 datestamp = run_datestamp;
2767 for(repdata = data(dp); repdata != NULL && (repdata->level != level || strcmp(repdata->datestamp,datestamp)!=0); repdata = repdata->next) {
2771 repdata = (repdata_t *)alloc(SIZEOF(repdata_t));
2772 memset(repdata, '\0', SIZEOF(repdata_t));
2774 repdata->datestamp = stralloc(datestamp ? datestamp : "");
2775 repdata->level = level;
2776 repdata->dumper.result = L_BOGUS;
2777 repdata->taper.result = L_BOGUS;
2778 repdata->next = NULL;
2780 prev->next = repdata;
2782 dp->up = (void *)repdata;
2789 do_postscript_output(void)
2791 tapetype_t *tp = lookup_tapetype(getconf_str(CNF_TAPETYPE));
2794 double outsize, origsize;
2801 tapesize = tapetype_get_length(tp);
2802 marksize = tapetype_get_filemark(tp);
2804 for(current_tape = stats_by_tape; current_tape != NULL;
2805 current_tape = current_tape->next) {
2807 if (current_tape->label == NULL) {
2811 copy_template_file(tapetype_get_lbl_templ(tp));
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");