2f8c950f062b68bdbd84069911f6ad0cb3087463
[debian/amanda] / server-src / find.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: find.c,v 1.23 2006/01/15 21:01:00 martinea Exp $
29  *
30  * controlling process for the Amanda backup system
31  */
32 #include "amanda.h"
33 #include "conffile.h"
34 #include "tapefile.h"
35 #include "logfile.h"
36 #include "holding.h"
37 #include "find.h"
38
39 void find P((int argc, char **argv));
40 int find_match P((char *host, char *disk));
41 int search_logfile P((find_result_t **output_find, char *label, int datestamp, int datestamp_aux, char *logfile));
42 void search_holding_disk P((find_result_t **output_find));
43 void strip_failed_chunks P((find_result_t *output_find));
44 char *find_nicedate P((int datestamp));
45 static int find_compare P((const void *, const void *));
46 static int parse_taper_datestamp_log P((char *, int *, char **));
47
48 static char *find_sort_order = NULL;
49 int dynamic_disklist = 0;
50 disklist_t* find_diskqp = NULL;
51
52 find_result_t *find_dump(dyna_disklist, diskqp)
53 int dyna_disklist;
54 disklist_t* diskqp;
55 {
56     char *conf_logdir, *logfile = NULL;
57     int tape, maxtape, seq, logs;
58     tape_t *tp;
59     find_result_t *output_find = NULL;
60
61     dynamic_disklist = dyna_disklist;
62     find_diskqp = diskqp;
63     conf_logdir = getconf_str(CNF_LOGDIR);
64     if (*conf_logdir == '/') {
65         conf_logdir = stralloc(conf_logdir);
66     } else {
67         conf_logdir = stralloc2(config_dir, conf_logdir);
68     }
69     maxtape = lookup_nb_tape();
70
71     for(tape = 1; tape <= maxtape; tape++) {
72         char ds_str[NUM_STR_SIZE];
73
74         tp = lookup_tapepos(tape);
75         if(tp == NULL) continue;
76         snprintf(ds_str, sizeof(ds_str), "%d", tp->datestamp);
77
78         /* search log files */
79
80         logs = 0;
81
82         /* new-style log.<date>.<seq> */
83
84         for(seq = 0; 1; seq++) {
85             char seq_str[NUM_STR_SIZE];
86
87             snprintf(seq_str, sizeof(seq_str), "%d", seq);
88             logfile = newvstralloc(logfile,
89                         conf_logdir, "/log.", ds_str, ".", seq_str, NULL);
90             if(access(logfile, R_OK) != 0) break;
91             logs += search_logfile(&output_find, tp->label, tp->datestamp, seq, logfile);
92         }
93
94         /* search old-style amflush log, if any */
95
96         logfile = newvstralloc(logfile,
97                                conf_logdir, "/log.", ds_str, ".amflush", NULL);
98         if(access(logfile,R_OK) == 0) {
99             logs += search_logfile(&output_find, tp->label, tp->datestamp, 1000, logfile);
100         }
101
102         /* search old-style main log, if any */
103
104         logfile = newvstralloc(logfile, conf_logdir, "/log.", ds_str, NULL);
105         if(access(logfile,R_OK) == 0) {
106             logs += search_logfile(&output_find, tp->label, tp->datestamp, -1, logfile);
107         }
108         if(logs == 0 && tp->datestamp != 0)
109             printf("Warning: no log files found for tape %s written %s\n",
110                    tp->label, find_nicedate(tp->datestamp));
111     }
112     amfree(logfile);
113     amfree(conf_logdir);
114
115     search_holding_disk(&output_find);
116
117     strip_failed_chunks(output_find);
118     
119     return(output_find);
120 }
121
122 char **find_log()
123 {
124     char *conf_logdir, *logfile = NULL;
125     int tape, maxtape, seq, logs;
126     tape_t *tp;
127     char **output_find_log = NULL;
128     char **current_log;
129
130     conf_logdir = getconf_str(CNF_LOGDIR);
131     if (*conf_logdir == '/') {
132         conf_logdir = stralloc(conf_logdir);
133     } else {
134         conf_logdir = stralloc2(config_dir, conf_logdir);
135     }
136     maxtape = lookup_nb_tape();
137
138     output_find_log = alloc((maxtape*5+10) * sizeof(char *));
139     current_log = output_find_log;
140
141     for(tape = 1; tape <= maxtape; tape++) {
142         char ds_str[NUM_STR_SIZE];
143
144         tp = lookup_tapepos(tape);
145         if(tp == NULL) continue;
146         snprintf(ds_str, sizeof(ds_str), "%d", tp->datestamp);
147
148         /* search log files */
149
150         logs = 0;
151
152         /* new-style log.<date>.<seq> */
153
154         for(seq = 0; 1; seq++) {
155             char seq_str[NUM_STR_SIZE];
156
157             snprintf(seq_str, sizeof(seq_str), "%d", seq);
158             logfile = newvstralloc(logfile,
159                         conf_logdir, "/log.", ds_str, ".", seq_str, NULL);
160             if(access(logfile, R_OK) != 0) break;
161             if( search_logfile(NULL, tp->label, tp->datestamp, seq, logfile)) {
162                 *current_log = vstralloc("log.", ds_str, ".", seq_str, NULL);
163                 current_log++;
164                 logs++;
165                 break;
166             }
167         }
168
169         /* search old-style amflush log, if any */
170
171         logfile = newvstralloc(logfile,
172                                conf_logdir, "/log.", ds_str, ".amflush", NULL);
173         if(access(logfile,R_OK) == 0) {
174             if( search_logfile(NULL, tp->label, tp->datestamp, 1000, logfile)) {
175                 *current_log = vstralloc("log.", ds_str, ".amflush", NULL);
176                 current_log++;
177                 logs++;
178             }
179         }
180
181         /* search old-style main log, if any */
182
183         logfile = newvstralloc(logfile, conf_logdir, "/log.", ds_str, NULL);
184         if(access(logfile,R_OK) == 0) {
185             if(search_logfile(NULL, tp->label, tp->datestamp, -1, logfile)) {
186                 *current_log = vstralloc("log.", ds_str, NULL);
187                 current_log++;
188                 logs++;
189             }
190         }
191         if(logs == 0 && tp->datestamp != 0)
192             printf("Warning: no log files found for tape %s written %s\n",
193                    tp->label, find_nicedate(tp->datestamp));
194     }
195     amfree(logfile);
196     amfree(conf_logdir);
197     *current_log = NULL;
198     return(output_find_log);
199 }
200
201 /*
202  * Remove CHUNK entries from dumps that ultimately failed from our report.
203  */
204 void strip_failed_chunks(output_find)
205 find_result_t *output_find;
206 {
207     find_result_t *cur, *prev = NULL, *failed = NULL, *failures = NULL;
208
209     /* Generate a list of failures */
210     for(cur=output_find; cur; cur=cur->next) {
211         if(!cur->hostname || !cur->diskname) continue;
212
213         if(strcmp(cur->status, "OK")){
214             failed = alloc(sizeof(find_result_t));
215             memcpy(failed, cur, sizeof(find_result_t));
216             failed->next = failures;
217             failed->diskname = stralloc(cur->diskname);
218             failed->hostname = stralloc(cur->hostname);
219             failures = failed;
220         }
221     }
222
223     /* Now if a CHUNK matches the parameters of a failed dump, remove it */
224     for(failed=failures; failed; failed=failed->next) {
225         prev = NULL;
226         for(cur=output_find; cur; cur=cur->next) {
227             if(!cur->hostname || !cur->diskname ||
228                   !strcmp(cur->partnum, "--") || strcmp(cur->status, "OK")){
229                 prev = cur;
230                 continue;
231             }
232
233             if(!strcmp(cur->hostname, failed->hostname) &&
234                  !strcmp(cur->diskname, failed->diskname) &&
235                  cur->datestamp == failed->datestamp &&
236                  cur->datestamp_aux == failed->datestamp_aux &&
237                  cur->level == failed->level){
238                 find_result_t *next = cur->next;
239                 amfree(cur->diskname);
240                 amfree(cur->hostname);
241                 next = cur->next;
242                 amfree(cur);
243                 if(prev){
244                     prev->next = next;
245                     cur = prev;
246                 }
247                 else output_find = next;
248             }
249             else prev = cur;
250         }
251     }
252
253     for(failed=failures; failed;) {
254         find_result_t *fai = failed->next;
255         amfree(failed->diskname);
256         amfree(failed->hostname);
257         fai = failed->next;
258         amfree(failed);
259         failed=fai;
260     }
261 }
262
263 void search_holding_disk(output_find)
264 find_result_t **output_find;
265 {
266     holdingdisk_t *hdisk;
267     sl_t  *holding_list;
268     sle_t *dir;
269     char *sdirname = NULL;
270     char *destname = NULL;
271     char *hostname = NULL;
272     char *diskname = NULL;
273     DIR *workdir;
274     struct dirent *entry;
275     int level;
276     disk_t *dp;
277
278     holding_list = pick_all_datestamp(1);
279
280     for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next) {
281         for(dir = holding_list->first; dir != NULL; dir = dir->next) {
282             sdirname = newvstralloc(sdirname,
283                                     hdisk->diskdir, "/", dir->name,
284                                     NULL);
285             if((workdir = opendir(sdirname)) == NULL) {
286                 continue;
287             }
288
289             while((entry = readdir(workdir)) != NULL) {
290                 if(is_dot_or_dotdot(entry->d_name)) {
291                     continue;
292                 }
293                 destname = newvstralloc(destname,
294                                         sdirname, "/", entry->d_name,
295                                         NULL);
296                 if(is_emptyfile(destname)) {
297                     continue;
298                 }
299                 amfree(hostname);
300                 amfree(diskname);
301                 if(get_amanda_names(destname, &hostname, &diskname, &level) != F_DUMPFILE) {
302                     continue;
303                 }
304                 if(level < 0 || level > 9)
305                     continue;
306
307                 dp = NULL;
308                 for(;;) {
309                     char *s;
310                     if((dp = lookup_disk(hostname, diskname)))
311                         break;
312                     if((s = strrchr(hostname,'.')) == NULL)
313                         break;
314                     *s = '\0';
315                 }
316                 if ( dp == NULL ) {
317                     continue;
318                 }
319
320                 if(find_match(hostname,diskname)) {
321                     find_result_t *new_output_find =
322                         alloc(sizeof(find_result_t));
323                     new_output_find->next=*output_find;
324                     if(strlen(dir->name) == 8) {
325                         new_output_find->datestamp=atoi(dir->name);
326                         new_output_find->timestamp=stralloc2(dir->name, "000000");
327                     }
328                     else if(strlen(dir->name) == 14) {
329                         char *name = stralloc(dir->name);
330                         name[8] = '\0';
331                         new_output_find->datestamp=atoi(name);
332                         new_output_find->timestamp=stralloc(dir->name);
333                         amfree(name);
334                     }
335                     else {
336                         error("Bad date\n");
337                     }
338                     new_output_find->datestamp_aux=1001;
339                     new_output_find->hostname=hostname;
340                     hostname = NULL;
341                     new_output_find->diskname=diskname;
342                     diskname = NULL;
343                     new_output_find->level=level;
344                     new_output_find->label=stralloc(destname);
345                     new_output_find->partnum=stralloc("--");
346                     new_output_find->filenum=0;
347                     new_output_find->status=stralloc("OK");
348                     *output_find=new_output_find;
349                 }
350             }
351             closedir(workdir);
352         }       
353     }
354     free_sl(holding_list);
355     holding_list = NULL;
356     amfree(destname);
357     amfree(sdirname);
358     amfree(hostname);
359     amfree(diskname);
360 }
361
362 static int find_compare(i1, j1)
363 const void *i1;
364 const void *j1;
365 {
366     int compare=0;
367     find_result_t **i = (find_result_t **)i1;
368     find_result_t **j = (find_result_t **)j1;
369
370     int nb_compare=strlen(find_sort_order);
371     int k;
372
373     for(k=0;k<nb_compare;k++) {
374         switch (find_sort_order[k]) {
375         case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
376                    break;
377         case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
378                    break;
379         case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
380                    break;
381         case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
382                    break;
383         case 'd' : compare=(*i)->datestamp - (*j)->datestamp;
384                    if (compare == 0)
385                         compare = (*i)->datestamp_aux - (*j)->datestamp_aux;
386                    break;
387         case 'D' : compare=(*j)->datestamp - (*i)->datestamp;
388                    if (compare == 0)
389                         compare = (*j)->datestamp_aux - (*i)->datestamp_aux;
390                    break;
391         case 'l' : compare=(*j)->level - (*i)->level;
392                    break;
393         case 'f' : compare=(*i)->filenum - (*j)->filenum;
394                    break;
395         case 'F' : compare=(*j)->filenum - (*i)->filenum;
396                    break;
397         case 'L' : compare=(*i)->level - (*j)->level;
398                    break;
399         case 'b' : compare=strcmp((*i)->label,(*j)->label);
400                    break;
401         case 'B' : compare=strcmp((*j)->label,(*i)->label);
402                    break;
403         case 'p' :
404                    if(strcmp((*i)->partnum, "--") != 0 &&
405                       strcmp((*j)->partnum, "--") != 0){
406                       compare = atoi((*i)->partnum) - atoi((*j)->partnum);
407                    }
408                    else compare=strcmp((*i)->partnum,(*j)->partnum);
409                    break;
410         case 'P' :
411                    if(strcmp((*i)->partnum, "--") != 0 &&
412                       strcmp((*j)->partnum, "--") != 0){
413                       compare = atoi((*j)->partnum) - atoi((*i)->partnum);
414                    }
415                    else compare=strcmp((*j)->partnum,(*i)->partnum);
416                    break;
417         }
418         if(compare != 0)
419             return compare;
420     }
421     return 0;
422 }
423
424 void sort_find_result(sort_order, output_find)
425 char *sort_order;
426 find_result_t **output_find;
427 {
428     find_result_t *output_find_result;
429     find_result_t **array_find_result = NULL;
430     int nb_result=0;
431     int no_result;
432
433     find_sort_order = sort_order;
434     /* qsort core dump if nothing to sort */
435     if(*output_find==NULL)
436         return;
437
438     /* How many result */
439     for(output_find_result=*output_find;
440         output_find_result;
441         output_find_result=output_find_result->next) {
442         nb_result++;
443     }
444
445     /* put the list in an array */
446     array_find_result=alloc(nb_result * sizeof(find_result_t *));
447     for(output_find_result=*output_find,no_result=0;
448         output_find_result;
449         output_find_result=output_find_result->next,no_result++) {
450         array_find_result[no_result]=output_find_result;
451     }
452
453     /* sort the array */
454     qsort(array_find_result,nb_result,sizeof(find_result_t *),
455           find_compare);
456
457     /* put the sorted result in the list */
458     for(no_result=0;
459         no_result<nb_result-1; no_result++) {
460         array_find_result[no_result]->next = array_find_result[no_result+1];
461     }
462     array_find_result[nb_result-1]->next=NULL;
463     *output_find=array_find_result[0];
464     amfree(array_find_result);
465 }
466
467 void print_find_result(output_find)
468 find_result_t *output_find;
469 {
470     find_result_t *output_find_result;
471     int max_len_datestamp = 4;
472     int max_len_hostname  = 4;
473     int max_len_diskname  = 4;
474     int max_len_level     = 2;
475     int max_len_label     =12;
476     int max_len_filenum   = 4;
477     int max_len_part      = 4;
478     int max_len_status    = 6;
479     int len;
480
481     for(output_find_result=output_find;
482         output_find_result;
483         output_find_result=output_find_result->next) {
484
485         len=strlen(find_nicedate(output_find_result->datestamp));
486         if(len>max_len_datestamp) max_len_datestamp=len;
487
488         len=strlen(output_find_result->hostname);
489         if(len>max_len_hostname) max_len_hostname=len;
490
491         len=strlen(output_find_result->diskname);
492         if(len>max_len_diskname) max_len_diskname=len;
493
494         len=strlen(output_find_result->label);
495         if(len>max_len_label) max_len_label=len;
496
497         len=strlen(output_find_result->status);
498         if(len>max_len_status) max_len_status=len;
499
500         len=strlen(output_find_result->partnum);
501         if(len>max_len_part) max_len_part=len;
502     }
503
504     /*
505      * Since status is the rightmost field, we zap the maximum length
506      * because it is not needed.  The code is left in place in case
507      * another column is added later.
508      */
509     max_len_status = 1;
510
511     if(output_find==NULL) {
512         printf("\nNo dump to list\n");
513     }
514     else {
515         printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n",
516                max_len_datestamp-4,"",
517                max_len_hostname-4 ,"",
518                max_len_diskname-4 ,"",
519                max_len_level-2    ,"",
520                max_len_label-12   ,"",
521                max_len_filenum-4  ,"",
522                max_len_part-4  ,"");
523         for(output_find_result=output_find;
524                 output_find_result;
525                 output_find_result=output_find_result->next) {
526
527             printf("%-*s %-*s %-*s %*d %-*s %*d %*s %-*s\n",
528                     max_len_datestamp, 
529                         find_nicedate(output_find_result->datestamp),
530                     max_len_hostname,  output_find_result->hostname,
531                     max_len_diskname,  output_find_result->diskname,
532                     max_len_level,     output_find_result->level,
533                     max_len_label,     output_find_result->label,
534                     max_len_filenum,   output_find_result->filenum,
535                     max_len_part,      output_find_result->partnum,
536                     max_len_status,    output_find_result->status
537                     );
538         }
539     }
540 }
541
542 void free_find_result(output_find)
543 find_result_t **output_find;
544 {
545     find_result_t *output_find_result, *prev;
546
547     prev=NULL;
548     for(output_find_result=*output_find;
549             output_find_result;
550             output_find_result=output_find_result->next) {
551         if(prev != NULL) amfree(prev);
552         amfree(output_find_result->hostname);
553         amfree(output_find_result->diskname);
554         amfree(output_find_result->label);
555         amfree(output_find_result->partnum);
556         amfree(output_find_result->status);
557         amfree(output_find_result->timestamp);
558         prev = output_find_result;
559     }
560     if(prev != NULL) amfree(prev);
561     output_find = NULL;
562 }
563
564 int find_match(host, disk)
565 char *host, *disk;
566 {
567     disk_t *dp = lookup_disk(host,disk);
568     return (dp && dp->todo);
569 }
570
571 char *find_nicedate(datestamp)
572 int datestamp;
573 {
574     static char nice[20];
575     int year, month, day;
576
577     year  = datestamp / 10000;
578     month = (datestamp / 100) % 100;
579     day   = datestamp % 100;
580
581     snprintf(nice, sizeof(nice), "%4d-%02d-%02d", year, month, day);
582
583     return nice;
584 }
585
586 static int parse_taper_datestamp_log(logline, datestamp, label)
587 char *logline;
588 int *datestamp;
589 char **label;
590 {
591     char *s;
592     int ch;
593
594     s = logline;
595     ch = *s++;
596
597     skip_whitespace(s, ch);
598     if(ch == '\0') {
599         return 0;
600     }
601 #define sc "datestamp"
602     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
603         return 0;
604     }
605     s += sizeof(sc)-1;
606     ch = s[-1];
607 #undef sc
608
609     skip_whitespace(s, ch);
610     if(ch == '\0' || sscanf(s - 1, "%d", datestamp) != 1) {
611         return 0;
612     }
613     skip_integer(s, ch);
614
615     skip_whitespace(s, ch);
616     if(ch == '\0') {
617         return 0;
618     }
619 #define sc "label"
620     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
621         return 0;
622     }
623     s += sizeof(sc)-1;
624     ch = s[-1];
625 #undef sc
626
627     skip_whitespace(s, ch);
628     if(ch == '\0') {
629         return 0;
630     }
631     *label = s - 1;
632     skip_non_whitespace(s, ch);
633     s[-1] = '\0';
634
635     return 1;
636 }
637
638 /*
639  * Check whether we've already seen a CHUNK log entry for the given dump.
640  * This is so we can interpret the final SUCCESS entry for a split dump as 
641  * 'list its parts' instead.  Return 1 if we have, 0 if not.
642  */
643 int seen_chunk_of(output_find, date, host, disk, level)
644 find_result_t *output_find;
645 int date, level;
646 char *host, *disk;
647 {
648     find_result_t *cur;
649
650     if(!host || !disk) return(0);
651
652     for(cur=output_find; cur; cur=cur->next) {
653         if(atoi(cur->partnum) < 1 || !cur->hostname || !cur->diskname) continue;
654
655         if(cur->datestamp == date && strcmp(cur->hostname, host) == 0 &&
656                 strcmp(cur->diskname, disk) == 0 && cur->level == level){
657             return(1);
658         }
659     }
660     return(0);
661 }
662
663 /* if output_find is NULL                                       */
664 /*      return 1 if this is the logfile for this label          */
665 /*      return 0 if this is not the logfile for this label      */
666 /* else                                                         */
667 /*      add to output_find all the dump for this label          */
668 /*      return the number of dump added.                        */
669 int search_logfile(output_find, label, datestamp, datestamp_aux, logfile)
670 find_result_t **output_find;
671 char *label, *logfile;
672 int datestamp, datestamp_aux;
673 {
674     FILE *logf;
675     char *host, *host_undo;
676     char *disk, *disk_undo;
677     char *partnum=NULL, *partnum_undo;
678     int   datestampI;
679     char *rest;
680     char *ck_label;
681     int level, filenum, ck_datestamp, tapematch;
682     int passlabel, ck_datestamp2;
683     char *s;
684     int ch;
685     disk_t *dp;
686
687     if((logf = fopen(logfile, "r")) == NULL)
688         error("could not open logfile %s: %s", logfile, strerror(errno));
689
690     /* check that this log file corresponds to the right tape */
691     tapematch = 0;
692     while(!tapematch && get_logline(logf)) {
693         if(curlog == L_START && curprog == P_TAPER) {
694             if(parse_taper_datestamp_log(curstr,
695                                          &ck_datestamp, &ck_label) == 0) {
696                 printf("strange log line \"start taper %s\"\n", curstr);
697             } else if(ck_datestamp == datestamp
698                       && strcmp(ck_label, label) == 0) {
699                 tapematch = 1;
700             }
701         }
702     }
703
704     if(output_find == NULL) {
705         afclose(logf);
706         if(tapematch == 0)
707             return 0;
708         else
709             return 1;
710     }
711
712     if(tapematch == 0) {
713         afclose(logf);
714         return 0;
715     }
716
717     filenum = 0;
718     passlabel = 1;
719     while(get_logline(logf) && passlabel) {
720         if((curlog == L_SUCCESS || curlog == L_CHUNK) &&
721                                 curprog == P_TAPER && passlabel){
722             filenum++;
723         }
724         if(curlog == L_START && curprog == P_TAPER) {
725             if(parse_taper_datestamp_log(curstr,
726                                          &ck_datestamp2, &ck_label) == 0) {
727                 printf("strange log line \"start taper %s\"\n", curstr);
728             } else if (strcmp(ck_label, label)) {
729                 passlabel = !passlabel;
730             }
731         }
732         partnum = "--";
733         if(curlog == L_SUCCESS || curlog == L_FAIL || curlog == L_CHUNK) {
734             s = curstr;
735             ch = *s++;
736
737             skip_whitespace(s, ch);
738             if(ch == '\0') {
739                 printf("strange log line \"%s\"\n", curstr);
740                 continue;
741             }
742             host = s - 1;
743             skip_non_whitespace(s, ch);
744             host_undo = s - 1;
745             *host_undo = '\0';
746
747             skip_whitespace(s, ch);
748             if(ch == '\0') {
749                 printf("strange log line \"%s\"\n", curstr);
750                 continue;
751             }
752             disk = s - 1;
753             skip_non_whitespace(s, ch);
754             disk_undo = s - 1;
755             *disk_undo = '\0';
756
757             skip_whitespace(s, ch);
758             if(ch == '\0' || sscanf(s - 1, "%d", &datestampI) != 1) {
759                 printf("strange log line \"%s\"\n", curstr);
760                 continue;
761             }
762             skip_integer(s, ch);
763
764             if(datestampI < 100)  { /* old log didn't have datestamp */
765                 level = datestampI;
766                 datestampI = datestamp;
767             }
768             else {
769                 if(curlog == L_CHUNK){
770                     skip_whitespace(s, ch);
771                     partnum = s - 1;
772                     skip_non_whitespace(s, ch);
773                     partnum_undo = s - 1;
774                     *partnum_undo = '\0';
775                 }
776                 skip_whitespace(s, ch);
777                 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
778                     printf("strange log line \"%s\"\n", curstr);
779                     continue;
780                 }
781                 skip_integer(s, ch);
782             }
783
784             skip_whitespace(s, ch);
785             if(ch == '\0') {
786                 printf("strange log line \"%s\"\n", curstr);
787                 continue;
788             }
789             rest = s - 1;
790             if((s = strchr(s, '\n')) != NULL) {
791                 *s = '\0';
792             }
793
794             dp = lookup_disk(host,disk);
795             if ( dp == NULL ) {
796                 if (dynamic_disklist == 0) {
797                     continue;
798                 }
799                 dp = add_disk(find_diskqp, host, disk);
800                 enqueue_disk(find_diskqp, dp);
801             }
802             if(find_match(host, disk) && (curlog != L_SUCCESS ||
803                 !seen_chunk_of(*output_find,datestampI,host,disk,level))){
804                 if(curprog == P_TAPER) {
805                     find_result_t *new_output_find =
806                         (find_result_t *)alloc(sizeof(find_result_t));
807                     new_output_find->next=*output_find;
808                     new_output_find->datestamp=datestampI;
809                     new_output_find->timestamp = alloc(15);
810                     snprintf(new_output_find->timestamp, 15, "%d000000", datestampI);
811                     new_output_find->datestamp_aux=datestamp_aux;
812                     new_output_find->hostname=stralloc(host);
813                     new_output_find->diskname=stralloc(disk);
814                     new_output_find->level=level;
815                     new_output_find->partnum = stralloc(partnum);
816                     new_output_find->label=stralloc(label);
817                     new_output_find->filenum=filenum;
818                     if(curlog == L_SUCCESS || curlog == L_CHUNK) 
819                         new_output_find->status=stralloc("OK");
820                     else
821                         new_output_find->status=stralloc(rest);
822                     *output_find=new_output_find;
823                 }
824                 else if(curlog == L_FAIL) {     /* print other failures too */
825                     find_result_t *new_output_find =
826                         (find_result_t *)alloc(sizeof(find_result_t));
827                     new_output_find->next=*output_find;
828                     new_output_find->datestamp=datestamp;
829                     new_output_find->datestamp_aux=datestamp_aux;
830                     new_output_find->timestamp = alloc(15);
831                     snprintf(new_output_find->timestamp, 15, "%d000000", datestamp);
832                     new_output_find->hostname=stralloc(host);
833                     new_output_find->diskname=stralloc(disk);
834                     new_output_find->level=level;
835                     new_output_find->label=stralloc(label);
836                     new_output_find->partnum=stralloc(partnum);
837                     new_output_find->filenum=0;
838                     new_output_find->status=vstralloc(
839                          "FAILED (",
840                          program_str[(int)curprog],
841                          ") ",
842                          rest,
843                          NULL);
844                     *output_find=new_output_find;
845                 }
846             }
847         }
848     }
849     afclose(logf);
850     return 1;
851 }
852
853
854 /*
855  * Return the set of dumps that match *all* of the given patterns (we consider
856  * an empty pattern to match .*, though).  If 'ok' is true, will only match
857  * dumps with SUCCESS status.
858  */
859 find_result_t *dumps_match(output_find,hostname,diskname,datestamp,level,ok)
860 find_result_t *output_find;
861 char *hostname;
862 char *diskname;
863 char *datestamp;
864 char *level;
865 int ok;
866 {
867     find_result_t *cur_result;
868     find_result_t *matches = NULL;
869
870     for(cur_result=output_find;
871         cur_result;
872         cur_result=cur_result->next) {
873         char date_str[NUM_STR_SIZE];
874         char level_str[NUM_STR_SIZE];
875         snprintf(date_str, sizeof(date_str), "%d", cur_result->datestamp);
876         snprintf(level_str, sizeof(level_str), "%d", cur_result->level);
877         if((*hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
878            (*diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
879            (*datestamp== '\0' || match_datestamp(datestamp, date_str)) &&
880            (*level== '\0' || match_level(level, level_str)) &&
881            (!ok || !strcmp(cur_result->status, "OK"))){
882
883             find_result_t *curmatch = alloc(sizeof(find_result_t));
884             memcpy(curmatch, cur_result, sizeof(find_result_t));
885
886 /*
887             curmatch->hostname = stralloc(cur_result->hostname);
888             curmatch->diskname = stralloc(cur_result->diskname);
889             curmatch->datestamp = stralloc(cur_result->datestamp);
890             curmatch->partnum = stralloc(cur_result->partnum);
891             curmatch->status = stralloc(cur_result->status);
892             curmatch->level = stralloc(cur_result->level);
893 */          
894             curmatch->next = matches;
895             matches = curmatch;
896         }
897     }
898
899     return(matches);
900 }
901
902 find_result_t *dump_exist(output_find, hostname, diskname, datestamp, level)
903 find_result_t *output_find;
904 char *hostname;
905 char *diskname;
906 int datestamp;
907 int level;
908 {
909     find_result_t *output_find_result;
910
911     for(output_find_result=output_find;
912         output_find_result;
913         output_find_result=output_find_result->next) {
914         if( !strcmp(output_find_result->hostname, hostname) &&
915             !strcmp(output_find_result->diskname, diskname) &&
916             output_find_result->datestamp == datestamp &&
917             output_find_result->level == level) {
918
919             return output_find_result;
920         }
921     }
922     return(NULL);
923 }