Imported Upstream version 2.5.1p1
[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.33.2.1 2006/09/19 19:34:27 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 int find_match(char *host, char *disk);
40 int search_logfile(find_result_t **output_find, char *label, char *datestamp, char *logfile);
41 void search_holding_disk(find_result_t **output_find);
42 void strip_failed_chunks(find_result_t **output_find);
43 char *find_nicedate(char *datestamp);
44 static int find_compare(const void *, const void *);
45 static int parse_taper_datestamp_log(char *logline, char **datestamp, char **level);
46 static int seen_chunk_of(find_result_t *output_find, char *date, char *host, char *disk, int level);
47
48 static char *find_sort_order = NULL;
49 int dynamic_disklist = 0;
50 disklist_t* find_diskqp = NULL;
51
52 find_result_t *
53 find_dump(
54     int dyna_disklist,
55     disklist_t* diskqp)
56 {
57     char *conf_logdir, *logfile = NULL;
58     int tape, maxtape, logs;
59     unsigned seq;
60     tape_t *tp;
61     find_result_t *output_find = NULL;
62
63     dynamic_disklist = dyna_disklist;
64     find_diskqp = diskqp;
65     conf_logdir = getconf_str(CNF_LOGDIR);
66     if (*conf_logdir == '/') {
67         conf_logdir = stralloc(conf_logdir);
68     } else {
69         conf_logdir = stralloc2(config_dir, conf_logdir);
70     }
71     maxtape = lookup_nb_tape();
72
73     for(tape = 1; tape <= maxtape; tape++) {
74
75         tp = lookup_tapepos(tape);
76         if(tp == NULL) continue;
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), "%u", seq);
88             logfile = newvstralloc(logfile,
89                         conf_logdir, "/log.", tp->datestamp, ".", seq_str, NULL);
90             if(access(logfile, R_OK) != 0) break;
91             logs += search_logfile(&output_find, tp->label, tp->datestamp, logfile);
92         }
93
94         /* search old-style amflush log, if any */
95
96         logfile = newvstralloc(logfile,
97                                conf_logdir, "/log.", tp->datestamp, ".amflush", NULL);
98         if(access(logfile,R_OK) == 0) {
99             logs += search_logfile(&output_find, tp->label, tp->datestamp, logfile);
100         }
101
102         /* search old-style main log, if any */
103
104         logfile = newvstralloc(logfile, conf_logdir, "/log.", tp->datestamp, NULL);
105         if(access(logfile,R_OK) == 0) {
106             logs += search_logfile(&output_find, tp->label, tp->datestamp, logfile);
107         }
108         if(logs == 0 && strcmp(tp->datestamp,"0") != 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 **
123 find_log(void)
124 {
125     char *conf_logdir, *logfile = NULL;
126     int tape, maxtape, logs;
127     unsigned seq;
128     tape_t *tp;
129     char **output_find_log = NULL;
130     char **current_log;
131
132     conf_logdir = getconf_str(CNF_LOGDIR);
133     if (*conf_logdir == '/') {
134         conf_logdir = stralloc(conf_logdir);
135     } else {
136         conf_logdir = stralloc2(config_dir, conf_logdir);
137     }
138     maxtape = lookup_nb_tape();
139
140     output_find_log = alloc((maxtape*5+10) * SIZEOF(char *));
141     current_log = output_find_log;
142
143     for(tape = 1; tape <= maxtape; tape++) {
144
145         tp = lookup_tapepos(tape);
146         if(tp == NULL) continue;
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), "%u", seq);
158             logfile = newvstralloc(logfile,
159                         conf_logdir, "/log.", tp->datestamp, ".", seq_str, NULL);
160             if(access(logfile, R_OK) != 0) break;
161             if( search_logfile(NULL, tp->label, tp->datestamp, logfile)) {
162                 *current_log = vstralloc("log.", tp->datestamp, ".", 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.", tp->datestamp, ".amflush", NULL);
173         if(access(logfile,R_OK) == 0) {
174             if( search_logfile(NULL, tp->label, tp->datestamp, logfile)) {
175                 *current_log = vstralloc("log.", tp->datestamp, ".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.", tp->datestamp, NULL);
184         if(access(logfile,R_OK) == 0) {
185             if(search_logfile(NULL, tp->label, tp->datestamp, logfile)) {
186                 *current_log = vstralloc("log.", tp->datestamp, NULL);
187                 current_log++;
188                 logs++;
189             }
190         }
191         if(logs == 0 && strcmp(tp->datestamp,"0") != 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(
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 ||
212            !cur->timestamp || !cur->label)
213             continue;
214
215         if(strcmp(cur->status, "OK")){
216             failed = alloc(SIZEOF(find_result_t));
217             memcpy(failed, cur, SIZEOF(find_result_t));
218             failed->next = failures;
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         cur = *output_find;
227         while (cur != NULL) {
228             find_result_t *next = cur->next;
229             if(!cur->hostname  || !cur->diskname || 
230                !cur->timestamp || !cur->label    || !cur->partnum ||
231                !strcmp(cur->partnum, "--") || strcmp(cur->status, "OK")) {
232                 prev = cur;
233                 cur = next;
234             }
235             else if(!strcmp(cur->hostname, failed->hostname) &&
236                  !strcmp(cur->diskname, failed->diskname) &&
237                  !strcmp(cur->timestamp, failed->timestamp) &&
238                  !strcmp(cur->label, failed->label) &&
239                  cur->level == failed->level){
240                 amfree(cur->diskname);
241                 amfree(cur->hostname);
242                 amfree(cur->label);
243                 amfree(cur->timestamp);
244                 amfree(cur->partnum);
245                 amfree(cur->status);
246                 cur = next;
247                 if (prev) {
248                     amfree(prev->next);
249                     prev->next = next;
250                 } else {
251                     amfree(*output_find);
252                     *output_find = next;
253                 }
254             }
255             else {
256                 prev = cur;
257                 cur = next;
258             }
259
260         }
261     }
262
263     for(failed=failures; failed;) {
264         find_result_t *fai = failed->next;
265         fai = failed->next;
266         amfree(failed);
267         failed=fai;
268     }
269 }
270
271 void
272 search_holding_disk(
273     find_result_t **output_find)
274 {
275     holdingdisk_t *hdisk;
276     sl_t  *holding_list;
277     sle_t *dir;
278     char *sdirname = NULL;
279     char *destname = NULL;
280     char *hostname = NULL;
281     char *diskname = NULL;
282     DIR *workdir;
283     struct dirent *entry;
284     int level = 0;
285     disk_t *dp;
286     int fd;
287     ssize_t result;
288     char buf[DISK_BLOCK_BYTES];
289     dumpfile_t file;
290
291     holding_list = pick_all_datestamp(1);
292
293     for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next) {
294         for(dir = holding_list->first; dir != NULL; dir = dir->next) {
295             sdirname = newvstralloc(sdirname,
296                                     holdingdisk_get_diskdir(hdisk), "/", dir->name,
297                                     NULL);
298             if((workdir = opendir(sdirname)) == NULL) {
299                 continue;
300             }
301
302             while((entry = readdir(workdir)) != NULL) {
303                 if(is_dot_or_dotdot(entry->d_name)) {
304                     continue;
305                 }
306                 destname = newvstralloc(destname,
307                                         sdirname, "/", entry->d_name,
308                                         NULL);
309                 if(is_emptyfile(destname)) {
310                     continue;
311                 }
312                 amfree(hostname);
313                 amfree(diskname);
314                 if(get_amanda_names(destname, &hostname, &diskname, &level) != F_DUMPFILE) {
315                     continue;
316                 }
317                 if(level < 0 || level > 9)
318                     continue;
319                 if ((fd = open(destname, O_RDONLY)) == -1) {
320                     continue;
321                 }
322                 if((result = read(fd, &buf, DISK_BLOCK_BYTES)) <= 0) {
323                     continue;
324                 }
325                 close(fd);
326
327                 parse_file_header(buf, &file, (size_t)result);
328                 if (strcmp(file.name, hostname) != 0 ||
329                     strcmp(file.disk, diskname) != 0 ||
330                     file.dumplevel != level ||
331                     !match_datestamp(file.datestamp, dir->name)) {
332                     continue;
333                 }
334
335                 dp = NULL;
336                 for(;;) {
337                     char *s;
338                     if((dp = lookup_disk(hostname, diskname)))
339                         break;
340                     if((s = strrchr(hostname,'.')) == NULL)
341                         break;
342                     *s = '\0';
343                 }
344                 if ( dp == NULL ) {
345                     continue;
346                 }
347
348                 if(find_match(hostname,diskname)) {
349                     find_result_t *new_output_find =
350                         alloc(SIZEOF(find_result_t));
351                     new_output_find->next=*output_find;
352                     new_output_find->timestamp=stralloc(file.datestamp);
353                     new_output_find->hostname=hostname;
354                     hostname = NULL;
355                     new_output_find->diskname=diskname;
356                     diskname = NULL;
357                     new_output_find->level=level;
358                     new_output_find->label=stralloc(destname);
359                     new_output_find->partnum=stralloc("--");
360                     new_output_find->filenum=0;
361                     new_output_find->status=stralloc("OK");
362                     *output_find=new_output_find;
363                 }
364             }
365             closedir(workdir);
366         }       
367     }
368     free_sl(holding_list);
369     holding_list = NULL;
370     amfree(destname);
371     amfree(sdirname);
372     amfree(hostname);
373     amfree(diskname);
374 }
375
376 static int
377 find_compare(
378     const void *i1,
379     const void *j1)
380 {
381     int compare=0;
382     find_result_t **i = (find_result_t **)i1;
383     find_result_t **j = (find_result_t **)j1;
384
385     size_t nb_compare=strlen(find_sort_order);
386     size_t k;
387
388     for(k=0;k<nb_compare;k++) {
389         switch (find_sort_order[k]) {
390         case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
391                    break;
392         case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
393                    break;
394         case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
395                    break;
396         case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
397                    break;
398         case 'd' : compare=strcmp((*i)->timestamp,(*j)->timestamp);
399                    break;
400         case 'D' : compare=strcmp((*j)->timestamp,(*i)->timestamp);
401                    break;
402         case 'l' : compare=(*j)->level - (*i)->level;
403                    break;
404         case 'f' : compare=((*i)->filenum == (*j)->filenum) ? 0 :
405                            (((*i)->filenum < (*j)->filenum) ? -1 : 1);
406                    break;
407         case 'F' : compare=((*j)->filenum == (*i)->filenum) ? 0 :
408                            (((*j)->filenum < (*i)->filenum) ? -1 : 1);
409                    break;
410         case 'L' : compare=(*i)->level - (*j)->level;
411                    break;
412         case 'b' : compare=strcmp((*i)->label,(*j)->label);
413                    break;
414         case 'B' : compare=strcmp((*j)->label,(*i)->label);
415                    break;
416         case 'p' :
417                    if(strcmp((*i)->partnum, "--") != 0 &&
418                       strcmp((*j)->partnum, "--") != 0){
419                       compare = atoi((*i)->partnum) - atoi((*j)->partnum);
420                    }
421                    else compare=strcmp((*i)->partnum,(*j)->partnum);
422                    break;
423         case 'P' :
424                    if(strcmp((*i)->partnum, "--") != 0 &&
425                       strcmp((*j)->partnum, "--") != 0){
426                       compare = atoi((*j)->partnum) - atoi((*i)->partnum);
427                    }
428                    else compare=strcmp((*j)->partnum,(*i)->partnum);
429                    break;
430         }
431         if(compare != 0)
432             return compare;
433     }
434     return 0;
435 }
436
437 void
438 sort_find_result(
439     char *sort_order,
440     find_result_t **output_find)
441 {
442     find_result_t *output_find_result;
443     find_result_t **array_find_result = NULL;
444     size_t nb_result=0;
445     size_t no_result;
446
447     find_sort_order = sort_order;
448     /* qsort core dump if nothing to sort */
449     if(*output_find==NULL)
450         return;
451
452     /* How many result */
453     for(output_find_result=*output_find;
454         output_find_result;
455         output_find_result=output_find_result->next) {
456         nb_result++;
457     }
458
459     /* put the list in an array */
460     array_find_result=alloc(nb_result * SIZEOF(find_result_t *));
461     for(output_find_result=*output_find,no_result=0;
462         output_find_result;
463         output_find_result=output_find_result->next,no_result++) {
464         array_find_result[no_result]=output_find_result;
465     }
466
467     /* sort the array */
468     qsort(array_find_result,nb_result,SIZEOF(find_result_t *),
469           find_compare);
470
471     /* put the sorted result in the list */
472     for(no_result=0;
473         no_result<nb_result-1; no_result++) {
474         array_find_result[no_result]->next = array_find_result[no_result+1];
475     }
476     array_find_result[nb_result-1]->next=NULL;
477     *output_find=array_find_result[0];
478     amfree(array_find_result);
479 }
480
481 void
482 print_find_result(
483     find_result_t *output_find)
484 {
485     find_result_t *output_find_result;
486     int max_len_datestamp = 4;
487     int max_len_hostname  = 4;
488     int max_len_diskname  = 4;
489     int max_len_level     = 2;
490     int max_len_label     =12;
491     int max_len_filenum   = 4;
492     int max_len_part      = 4;
493     int max_len_status    = 6;
494     size_t len;
495
496     for(output_find_result=output_find;
497         output_find_result;
498         output_find_result=output_find_result->next) {
499
500         len=strlen(find_nicedate(output_find_result->timestamp));
501         if((int)len > max_len_datestamp)
502             max_len_datestamp=(int)len;
503
504         len=strlen(output_find_result->hostname);
505         if((int)len > max_len_hostname)
506             max_len_hostname = (int)len;
507
508         len=strlen(output_find_result->diskname);
509         if((int)len > max_len_diskname)
510             max_len_diskname = (int)len;
511
512         len=strlen(output_find_result->label);
513         if((int)len > max_len_label)
514             max_len_label = (int)len;
515
516         len=strlen(output_find_result->status);
517         if((int)len > max_len_status)
518             max_len_status = (int)len;
519
520         len=strlen(output_find_result->partnum);
521         if((int)len > max_len_part)
522             max_len_part = (int)len;
523     }
524
525     /*
526      * Since status is the rightmost field, we zap the maximum length
527      * because it is not needed.  The code is left in place in case
528      * another column is added later.
529      */
530     max_len_status = 1;
531
532     if(output_find==NULL) {
533         printf("\nNo dump to list\n");
534     }
535     else {
536         printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n",
537                max_len_datestamp-4,"",
538                max_len_hostname-4 ,"",
539                max_len_diskname-4 ,"",
540                max_len_level-2    ,"",
541                max_len_label-12   ,"",
542                max_len_filenum-4  ,"",
543                max_len_part-4  ,"");
544         for(output_find_result=output_find;
545                 output_find_result;
546                 output_find_result=output_find_result->next) {
547             char *qdiskname;
548
549             qdiskname = quote_string(output_find_result->diskname);
550             /*@ignore@*/
551             printf("%-*s %-*s %-*s %*d %-*s %*" OFF_T_RFMT " %*s %-*s\n",
552                     max_len_datestamp, 
553                         find_nicedate(output_find_result->timestamp),
554                     max_len_hostname,  output_find_result->hostname,
555                     max_len_diskname,  qdiskname,
556                     max_len_level,     output_find_result->level,
557                     max_len_label,     output_find_result->label,
558                     max_len_filenum,   (OFF_T_FMT_TYPE)output_find_result->filenum,
559                     max_len_part,      output_find_result->partnum,
560                     max_len_status,    output_find_result->status
561                     );
562             /*@end@*/
563             amfree(qdiskname);
564         }
565     }
566 }
567
568 void
569 free_find_result(
570     find_result_t **output_find)
571 {
572     find_result_t *output_find_result, *prev;
573
574     prev=NULL;
575     for(output_find_result=*output_find;
576             output_find_result;
577             output_find_result=output_find_result->next) {
578         amfree(prev);
579         amfree(output_find_result->timestamp);
580         amfree(output_find_result->hostname);
581         amfree(output_find_result->diskname);
582         amfree(output_find_result->label);
583         amfree(output_find_result->partnum);
584         amfree(output_find_result->status);
585         amfree(output_find_result->timestamp);
586         prev = output_find_result;
587     }
588     amfree(prev);
589     *output_find = NULL;
590 }
591
592 int
593 find_match(
594     char *host,
595     char *disk)
596 {
597     disk_t *dp = lookup_disk(host,disk);
598     return (dp && dp->todo);
599 }
600
601 char *
602 find_nicedate(
603     char *datestamp)
604 {
605     static char nice[20];
606     int year, month, day;
607     int hours, minutes, seconds;
608     char date[9], atime[7];
609     int  numdate, numtime;
610
611     strncpy(date, datestamp, 8);
612     date[8] = '\0';
613     numdate = atoi(date);
614     year  = numdate / 10000;
615     month = (numdate / 100) % 100;
616     day   = numdate % 100;
617
618     if(strlen(datestamp) <= 8) {
619         snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
620                 year, month, day);
621     }
622     else {
623         strncpy(atime, &(datestamp[8]), 6);
624         atime[6] = '\0';
625         numtime = atoi(atime);
626         hours = numtime / 10000;
627         minutes = (numtime / 100) % 100;
628         seconds = numtime % 100;
629
630         snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d %02d:%02d:%02d",
631                 year, month, day, hours, minutes, seconds);
632     }
633
634     return nice;
635 }
636
637 static int
638 parse_taper_datestamp_log(
639     char *logline,
640     char **datestamp,
641     char **label)
642 {
643     char *s;
644     int ch;
645
646     s = logline;
647     ch = *s++;
648
649     skip_whitespace(s, ch);
650     if(ch == '\0') {
651         return 0;
652     }
653 #define sc "datestamp"
654     if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
655         return 0;
656     }
657     s += SIZEOF(sc)-1;
658     ch = s[-1];
659 #undef sc
660
661     skip_whitespace(s, ch);
662     if(ch == '\0') {
663         return 0;
664     }
665     *datestamp = s - 1;
666     skip_non_whitespace(s, ch);
667     s[-1] = '\0';
668
669     skip_whitespace(s, ch);
670     if(ch == '\0') {
671         return 0;
672     }
673 #define sc "label"
674     if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
675         return 0;
676     }
677     s += SIZEOF(sc)-1;
678     ch = s[-1];
679 #undef sc
680
681     skip_whitespace(s, ch);
682     if(ch == '\0') {
683         return 0;
684     }
685     *label = s - 1;
686     skip_non_whitespace(s, ch);
687     s[-1] = '\0';
688
689     return 1;
690 }
691
692 /*
693  * Check whether we've already seen a CHUNK log entry for the given dump.
694  * This is so we can interpret the final SUCCESS entry for a split dump as 
695  * 'list its parts' instead.  Return 1 if we have, 0 if not.
696  */
697 int
698 seen_chunk_of(
699     find_result_t *output_find,
700     char *date,
701     char *host,
702     char *disk,
703     int level)
704 {
705     find_result_t *cur;
706
707     if(!host || !disk) return(0);
708
709     for(cur=output_find; cur; cur=cur->next) {
710         if(atoi(cur->partnum) < 1 || !cur->hostname || !cur->diskname) continue;
711
712         if(strcmp(cur->timestamp, date) == 0 && strcmp(cur->hostname, host) == 0 &&
713                 strcmp(cur->diskname, disk) == 0 && cur->level == level){
714             return(1);
715         }
716     }
717     return(0);
718 }
719
720 /* if output_find is NULL                                       */
721 /*      return 1 if this is the logfile for this label          */
722 /*      return 0 if this is not the logfile for this label      */
723 /* else                                                         */
724 /*      add to output_find all the dump for this label          */
725 /*      return the number of dump added.                        */
726 int
727 search_logfile(
728     find_result_t **output_find,
729     char *label,
730     char *datestamp,
731     char *logfile)
732 {
733     FILE *logf;
734     char *host, *host_undo;
735     char *disk, *qdisk, *disk_undo;
736     char *date, *date_undo;
737     char *partnum=NULL, *partnum_undo;
738     char *rest;
739     char *ck_label=NULL;
740     int level = 0; 
741     int tapematch;
742     off_t filenum;
743     int passlabel;
744     char *ck_datestamp, *ck_datestamp2;
745     char *s;
746     int ch;
747     disk_t *dp;
748
749     if((logf = fopen(logfile, "r")) == NULL) {
750         error("could not open logfile %s: %s", logfile, strerror(errno));
751         /*NOTREACHED*/
752     }
753
754     /* check that this log file corresponds to the right tape */
755     tapematch = 0;
756     while(!tapematch && get_logline(logf)) {
757         if(curlog == L_START && curprog == P_TAPER) {
758             if(parse_taper_datestamp_log(curstr,
759                                          &ck_datestamp, &ck_label) == 0) {
760                 printf("strange log line \"start taper %s\" curstr='%s'\n",
761                     logfile, curstr);
762             } else if(strcmp(ck_datestamp, datestamp) == 0
763                       && strcmp(ck_label, label) == 0) {
764                 tapematch = 1;
765             }
766         }
767     }
768
769     if(output_find == NULL) {
770         afclose(logf);
771         if(tapematch == 0)
772             return 0;
773         else
774             return 1;
775     }
776
777     if(tapematch == 0) {
778         afclose(logf);
779         return 0;
780     }
781
782     filenum = (off_t)0;
783     passlabel = 1;
784     while(get_logline(logf) && passlabel) {
785         if((curlog == L_SUCCESS || curlog == L_CHUNK || curlog == L_PARTIAL) &&
786                                 curprog == P_TAPER && passlabel){
787             filenum++;
788         }
789         if(curlog == L_START && curprog == P_TAPER) {
790             if(parse_taper_datestamp_log(curstr,
791                                          &ck_datestamp2, &ck_label) == 0) {
792                 printf("strange log line in %s \"start taper %s\"\n",
793                     logfile, curstr);
794             } else if (strcmp(ck_label, label)) {
795                 passlabel = !passlabel;
796             }
797         }
798         partnum = "--";
799         if(curlog == L_SUCCESS || curlog == L_PARTIAL || curlog == L_FAIL || curlog == L_CHUNK) {
800             s = curstr;
801             ch = *s++;
802
803             skip_whitespace(s, ch);
804             if(ch == '\0') {
805                 printf("strange log line in %s \"%s\"\n",
806                     logfile, curstr);
807                 continue;
808             }
809             host = s - 1;
810             skip_non_whitespace(s, ch);
811             host_undo = s - 1;
812             *host_undo = '\0';
813
814             skip_whitespace(s, ch);
815             if(ch == '\0') {
816                 printf("strange log line in %s \"%s\"\n",
817                     logfile, curstr);
818                 continue;
819             }
820             qdisk = s - 1;
821             skip_quoted_string(s, ch);
822             disk_undo = s - 1;
823             *disk_undo = '\0';
824             disk = unquote_string(qdisk);
825
826             skip_whitespace(s, ch);
827             if(ch == '\0') {
828                 printf("strange log line in %s \"%s\"\n",
829                     logfile, curstr);
830                 continue;
831             }
832             date = s - 1;
833             skip_non_whitespace(s, ch);
834             date_undo = s - 1;
835             *date_undo = '\0';
836
837             if(strlen(date) < 3) { /* old log didn't have datestamp */
838                 level = atoi(date);
839                 date = stralloc(datestamp);
840             }
841             else {
842                 if(curlog == L_CHUNK){
843                     skip_whitespace(s, ch);
844                     partnum = s - 1;
845                     skip_non_whitespace(s, ch);
846                     partnum_undo = s - 1;
847                     *partnum_undo = '\0';
848                 }
849                 skip_whitespace(s, ch);
850                 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
851                     printf("strange log line in %s \"%s\"\n",
852                     logfile, curstr);
853                     continue;
854                 }
855                 skip_integer(s, ch);
856             }
857
858             skip_whitespace(s, ch);
859             if(ch == '\0') {
860                 printf("strange log line in %s \"%s\"\n",
861                     logfile, curstr);
862                 continue;
863             }
864             rest = s - 1;
865             if((s = strchr(s, '\n')) != NULL) {
866                 *s = '\0';
867             }
868
869             dp = lookup_disk(host,disk);
870             if ( dp == NULL ) {
871                 if (dynamic_disklist == 0) {
872                     continue;
873                 }
874                 dp = add_disk(find_diskqp, host, disk);
875                 enqueue_disk(find_diskqp, dp);
876             }
877             if(find_match(host, disk) && (curlog != L_SUCCESS ||
878                 !seen_chunk_of(*output_find, date, host, disk, level))) {
879                 if(curprog == P_TAPER) {
880                     find_result_t *new_output_find =
881                         (find_result_t *)alloc(SIZEOF(find_result_t));
882                     new_output_find->next=*output_find;
883                     new_output_find->timestamp = stralloc(date);
884                     new_output_find->hostname=stralloc(host);
885                     new_output_find->diskname=stralloc(disk);
886                     new_output_find->level=level;
887                     new_output_find->partnum = stralloc(partnum);
888                     new_output_find->label=stralloc(label);
889                     new_output_find->filenum=filenum;
890                     if(curlog == L_SUCCESS || curlog == L_CHUNK) 
891                         new_output_find->status=stralloc("OK");
892                     else if(curlog == L_PARTIAL)
893                         new_output_find->status=stralloc("PARTIAL");
894                     else
895                         new_output_find->status=stralloc(rest);
896                     *output_find=new_output_find;
897                 }
898                 else if(curlog == L_FAIL) {     /* print other failures too */
899                     find_result_t *new_output_find =
900                         (find_result_t *)alloc(SIZEOF(find_result_t));
901                     new_output_find->next=*output_find;
902                     new_output_find->timestamp = stralloc(date);
903                     new_output_find->hostname=stralloc(host);
904                     new_output_find->diskname=stralloc(disk);
905                     new_output_find->level=level;
906                     new_output_find->label=stralloc(label);
907                     new_output_find->partnum=stralloc(partnum);
908                     new_output_find->filenum=0;
909                     new_output_find->status=vstralloc(
910                          "FAILED (",
911                          program_str[(int)curprog],
912                          ") ",
913                          rest,
914                          NULL);
915                     *output_find=new_output_find;
916                 }
917             }
918             amfree(disk);
919         }
920     }
921     afclose(logf);
922     return 1;
923 }
924
925
926 /*
927  * Return the set of dumps that match *all* of the given patterns (we consider
928  * an empty pattern to match .*, though).  If 'ok' is true, will only match
929  * dumps with SUCCESS status.
930  */
931 find_result_t *
932 dumps_match(
933     find_result_t *output_find,
934     char *hostname,
935     char *diskname,
936     char *datestamp,
937     char *level,
938     int ok)
939 {
940     find_result_t *cur_result;
941     find_result_t *matches = NULL;
942
943     for(cur_result=output_find;
944         cur_result;
945         cur_result=cur_result->next) {
946         char level_str[NUM_STR_SIZE];
947         snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
948         if((*hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
949            (*diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
950            (*datestamp== '\0' || match_datestamp(datestamp, cur_result->timestamp)) &&
951            (*level== '\0' || match_level(level, level_str)) &&
952            (!ok || !strcmp(cur_result->status, "OK"))){
953
954             find_result_t *curmatch = alloc(SIZEOF(find_result_t));
955             memcpy(curmatch, cur_result, SIZEOF(find_result_t));
956
957 /*
958             curmatch->hostname = stralloc(cur_result->hostname);
959             curmatch->diskname = stralloc(cur_result->diskname);
960             curmatch->datestamp = stralloc(cur_result->datestamp);
961             curmatch->partnum = stralloc(cur_result->partnum);
962             curmatch->status = stralloc(cur_result->status);
963             curmatch->level = stralloc(cur_result->level);
964 */          
965             curmatch->next = matches;
966             matches = curmatch;
967         }
968     }
969
970     return(matches);
971 }
972
973 find_result_t *
974 dump_exist(
975     find_result_t *output_find,
976     char *hostname,
977     char *diskname,
978     char *datestamp,
979     int level)
980 {
981     find_result_t *output_find_result;
982
983     for(output_find_result=output_find;
984         output_find_result;
985         output_find_result=output_find_result->next) {
986         if( !strcmp(output_find_result->hostname, hostname) &&
987             !strcmp(output_find_result->diskname, diskname) &&
988             !strcmp(output_find_result->timestamp, datestamp) &&
989             output_find_result->level == level) {
990
991             return output_find_result;
992         }
993     }
994     return(NULL);
995 }