Imported Upstream version 2.5.2p1
[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 2006/07/06 13:13:15 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             fprintf(stderr, "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             fprintf(stderr, "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     sl_t  *holding_file_list;
276     sle_t *e;
277     char *holding_file;
278     disk_t *dp;
279     dumpfile_t file;
280
281     holding_file_list = holding_get_files(NULL, NULL, 1);
282
283     for(e = holding_file_list->first; e != NULL; e = e->next) {
284         holding_file = e->name;
285
286         if (!holding_file_get_dumpfile(holding_file, &file))
287             continue;
288
289         if (file.dumplevel < 0 || file.dumplevel > 9)
290             continue;
291
292         dp = NULL;
293         for(;;) {
294             char *s;
295             if((dp = lookup_disk(file.name, file.disk)))
296                 break;
297             if((s = strrchr(file.name,'.')) == NULL)
298                 break;
299             *s = '\0';
300         }
301         if ( dp == NULL ) {
302             continue;
303         }
304
305         if(find_match(file.name,file.disk)) {
306             find_result_t *new_output_find =
307                 alloc(SIZEOF(find_result_t));
308             new_output_find->next=*output_find;
309             new_output_find->timestamp = stralloc(file.datestamp);
310             new_output_find->hostname = stralloc(file.name);
311             new_output_find->diskname = stralloc(file.disk);
312             new_output_find->level=file.dumplevel;
313             new_output_find->label=stralloc(holding_file);
314             new_output_find->partnum=stralloc("--");
315             new_output_find->filenum=0;
316             new_output_find->status=stralloc("OK");
317             *output_find=new_output_find;
318         }
319     }
320
321     free_sl(holding_file_list);
322 }
323
324 static int
325 find_compare(
326     const void *i1,
327     const void *j1)
328 {
329     int compare=0;
330     find_result_t **i = (find_result_t **)i1;
331     find_result_t **j = (find_result_t **)j1;
332
333     size_t nb_compare=strlen(find_sort_order);
334     size_t k;
335
336     for(k=0;k<nb_compare;k++) {
337         switch (find_sort_order[k]) {
338         case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
339                    break;
340         case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
341                    break;
342         case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
343                    break;
344         case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
345                    break;
346         case 'd' : compare=strcmp((*i)->timestamp,(*j)->timestamp);
347                    break;
348         case 'D' : compare=strcmp((*j)->timestamp,(*i)->timestamp);
349                    break;
350         case 'l' : compare=(*j)->level - (*i)->level;
351                    break;
352         case 'f' : compare=((*i)->filenum == (*j)->filenum) ? 0 :
353                            (((*i)->filenum < (*j)->filenum) ? -1 : 1);
354                    break;
355         case 'F' : compare=((*j)->filenum == (*i)->filenum) ? 0 :
356                            (((*j)->filenum < (*i)->filenum) ? -1 : 1);
357                    break;
358         case 'L' : compare=(*i)->level - (*j)->level;
359                    break;
360         case 'b' : compare=strcmp((*i)->label,(*j)->label);
361                    break;
362         case 'B' : compare=strcmp((*j)->label,(*i)->label);
363                    break;
364         case 'p' :
365                    if(strcmp((*i)->partnum, "--") != 0 &&
366                       strcmp((*j)->partnum, "--") != 0){
367                       compare = atoi((*i)->partnum) - atoi((*j)->partnum);
368                    }
369                    else compare=strcmp((*i)->partnum,(*j)->partnum);
370                    break;
371         case 'P' :
372                    if(strcmp((*i)->partnum, "--") != 0 &&
373                       strcmp((*j)->partnum, "--") != 0){
374                       compare = atoi((*j)->partnum) - atoi((*i)->partnum);
375                    }
376                    else compare=strcmp((*j)->partnum,(*i)->partnum);
377                    break;
378         }
379         if(compare != 0)
380             return compare;
381     }
382     return 0;
383 }
384
385 void
386 sort_find_result(
387     char *sort_order,
388     find_result_t **output_find)
389 {
390     find_result_t *output_find_result;
391     find_result_t **array_find_result = NULL;
392     size_t nb_result=0;
393     size_t no_result;
394
395     find_sort_order = sort_order;
396     /* qsort core dump if nothing to sort */
397     if(*output_find==NULL)
398         return;
399
400     /* How many result */
401     for(output_find_result=*output_find;
402         output_find_result;
403         output_find_result=output_find_result->next) {
404         nb_result++;
405     }
406
407     /* put the list in an array */
408     array_find_result=alloc(nb_result * SIZEOF(find_result_t *));
409     for(output_find_result=*output_find,no_result=0;
410         output_find_result;
411         output_find_result=output_find_result->next,no_result++) {
412         array_find_result[no_result]=output_find_result;
413     }
414
415     /* sort the array */
416     qsort(array_find_result,nb_result,SIZEOF(find_result_t *),
417           find_compare);
418
419     /* put the sorted result in the list */
420     for(no_result=0;
421         no_result<nb_result-1; no_result++) {
422         array_find_result[no_result]->next = array_find_result[no_result+1];
423     }
424     array_find_result[nb_result-1]->next=NULL;
425     *output_find=array_find_result[0];
426     amfree(array_find_result);
427 }
428
429 void
430 print_find_result(
431     find_result_t *output_find)
432 {
433     find_result_t *output_find_result;
434     int max_len_datestamp = 4;
435     int max_len_hostname  = 4;
436     int max_len_diskname  = 4;
437     int max_len_level     = 2;
438     int max_len_label     =12;
439     int max_len_filenum   = 4;
440     int max_len_part      = 4;
441     int max_len_status    = 6;
442     size_t len;
443
444     for(output_find_result=output_find;
445         output_find_result;
446         output_find_result=output_find_result->next) {
447
448         len=strlen(find_nicedate(output_find_result->timestamp));
449         if((int)len > max_len_datestamp)
450             max_len_datestamp=(int)len;
451
452         len=strlen(output_find_result->hostname);
453         if((int)len > max_len_hostname)
454             max_len_hostname = (int)len;
455
456         len=strlen(output_find_result->diskname);
457         if((int)len > max_len_diskname)
458             max_len_diskname = (int)len;
459
460         len=strlen(output_find_result->label);
461         if((int)len > max_len_label)
462             max_len_label = (int)len;
463
464         len=strlen(output_find_result->status);
465         if((int)len > max_len_status)
466             max_len_status = (int)len;
467
468         len=strlen(output_find_result->partnum);
469         if((int)len > max_len_part)
470             max_len_part = (int)len;
471     }
472
473     /*
474      * Since status is the rightmost field, we zap the maximum length
475      * because it is not needed.  The code is left in place in case
476      * another column is added later.
477      */
478     max_len_status = 1;
479
480     if(output_find==NULL) {
481         printf("\nNo dump to list\n");
482     }
483     else {
484         printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n",
485                max_len_datestamp-4,"",
486                max_len_hostname-4 ,"",
487                max_len_diskname-4 ,"",
488                max_len_level-2    ,"",
489                max_len_label-12   ,"",
490                max_len_filenum-4  ,"",
491                max_len_part-4  ,"");
492         for(output_find_result=output_find;
493                 output_find_result;
494                 output_find_result=output_find_result->next) {
495             char *qdiskname;
496
497             qdiskname = quote_string(output_find_result->diskname);
498             /*@ignore@*/
499             printf("%-*s %-*s %-*s %*d %-*s %*" OFF_T_RFMT " %*s %-*s\n",
500                     max_len_datestamp, 
501                         find_nicedate(output_find_result->timestamp),
502                     max_len_hostname,  output_find_result->hostname,
503                     max_len_diskname,  qdiskname,
504                     max_len_level,     output_find_result->level,
505                     max_len_label,     output_find_result->label,
506                     max_len_filenum,   (OFF_T_FMT_TYPE)output_find_result->filenum,
507                     max_len_part,      output_find_result->partnum,
508                     max_len_status,    output_find_result->status
509                     );
510             /*@end@*/
511             amfree(qdiskname);
512         }
513     }
514 }
515
516 void
517 free_find_result(
518     find_result_t **output_find)
519 {
520     find_result_t *output_find_result, *prev;
521
522     prev=NULL;
523     for(output_find_result=*output_find;
524             output_find_result;
525             output_find_result=output_find_result->next) {
526         amfree(prev);
527         amfree(output_find_result->timestamp);
528         amfree(output_find_result->hostname);
529         amfree(output_find_result->diskname);
530         amfree(output_find_result->label);
531         amfree(output_find_result->partnum);
532         amfree(output_find_result->status);
533         amfree(output_find_result->timestamp);
534         prev = output_find_result;
535     }
536     amfree(prev);
537     *output_find = NULL;
538 }
539
540 int
541 find_match(
542     char *host,
543     char *disk)
544 {
545     disk_t *dp = lookup_disk(host,disk);
546     return (dp && dp->todo);
547 }
548
549 char *
550 find_nicedate(
551     char *datestamp)
552 {
553     static char nice[20];
554     int year, month, day;
555     int hours, minutes, seconds;
556     char date[9], atime[7];
557     int  numdate, numtime;
558
559     strncpy(date, datestamp, 8);
560     date[8] = '\0';
561     numdate = atoi(date);
562     year  = numdate / 10000;
563     month = (numdate / 100) % 100;
564     day   = numdate % 100;
565
566     if(strlen(datestamp) <= 8) {
567         snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
568                 year, month, day);
569     }
570     else {
571         strncpy(atime, &(datestamp[8]), 6);
572         atime[6] = '\0';
573         numtime = atoi(atime);
574         hours = numtime / 10000;
575         minutes = (numtime / 100) % 100;
576         seconds = numtime % 100;
577
578         snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d %02d:%02d:%02d",
579                 year, month, day, hours, minutes, seconds);
580     }
581
582     return nice;
583 }
584
585 static int
586 parse_taper_datestamp_log(
587     char *logline,
588     char **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     if(strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
602         return 0;
603     }
604
605     skip_whitespace(s, ch);
606     if(ch == '\0') {
607         return 0;
608     }
609     *datestamp = s - 1;
610     skip_non_whitespace(s, ch);
611     s[-1] = '\0';
612
613     skip_whitespace(s, ch);
614     if(ch == '\0') {
615         return 0;
616     }
617     if(strncmp_const_skip(s - 1, "label", s, ch) != 0) {
618         return 0;
619     }
620
621     skip_whitespace(s, ch);
622     if(ch == '\0') {
623         return 0;
624     }
625     *label = s - 1;
626     skip_non_whitespace(s, ch);
627     s[-1] = '\0';
628
629     return 1;
630 }
631
632 /*
633  * Check whether we've already seen a CHUNK log entry for the given dump.
634  * This is so we can interpret the final SUCCESS entry for a split dump as 
635  * 'list its parts' instead.  Return 1 if we have, 0 if not.
636  */
637 int
638 seen_chunk_of(
639     find_result_t *output_find,
640     char *date,
641     char *host,
642     char *disk,
643     int level)
644 {
645     find_result_t *cur;
646
647     if(!host || !disk) return(0);
648
649     for(cur=output_find; cur; cur=cur->next) {
650         if(atoi(cur->partnum) < 1 || !cur->hostname || !cur->diskname) continue;
651
652         if(strcmp(cur->timestamp, date) == 0 && strcmp(cur->hostname, host) == 0 &&
653                 strcmp(cur->diskname, disk) == 0 && cur->level == level){
654             return(1);
655         }
656     }
657     return(0);
658 }
659
660 /* if output_find is NULL                                       */
661 /*      return 1 if this is the logfile for this label          */
662 /*      return 0 if this is not the logfile for this label      */
663 /* else                                                         */
664 /*      add to output_find all the dump for this label          */
665 /*      return the number of dump added.                        */
666 int
667 search_logfile(
668     find_result_t **output_find,
669     char *label,
670     char *datestamp,
671     char *logfile)
672 {
673     FILE *logf;
674     char *host, *host_undo;
675     char *disk, *qdisk, *disk_undo;
676     char *date, *date_undo;
677     char *partnum=NULL, *partnum_undo;
678     char *rest;
679     char *ck_label=NULL;
680     int level = 0; 
681     int tapematch;
682     off_t filenum;
683     int passlabel;
684     char *ck_datestamp, *ck_datestamp2;
685     char *s;
686     int ch;
687     disk_t *dp;
688
689     if((logf = fopen(logfile, "r")) == NULL) {
690         error("could not open logfile %s: %s", logfile, strerror(errno));
691         /*NOTREACHED*/
692     }
693
694     /* check that this log file corresponds to the right tape */
695     tapematch = 0;
696     while(!tapematch && get_logline(logf)) {
697         if(curlog == L_START && curprog == P_TAPER) {
698             if(parse_taper_datestamp_log(curstr,
699                                          &ck_datestamp, &ck_label) == 0) {
700                 printf("strange log line \"start taper %s\" curstr='%s'\n",
701                     logfile, curstr);
702             } else if(strcmp(ck_datestamp, datestamp) == 0
703                       && strcmp(ck_label, label) == 0) {
704                 tapematch = 1;
705             }
706         }
707     }
708
709     if(output_find == NULL) {
710         afclose(logf);
711         if(tapematch == 0)
712             return 0;
713         else
714             return 1;
715     }
716
717     if(tapematch == 0) {
718         afclose(logf);
719         return 0;
720     }
721
722     filenum = (off_t)0;
723     passlabel = 1;
724     while(get_logline(logf) && passlabel) {
725         if((curlog == L_SUCCESS || curlog == L_CHUNK || curlog == L_PARTIAL) &&
726                                 curprog == P_TAPER && passlabel){
727             filenum++;
728         }
729         if(curlog == L_START && curprog == P_TAPER) {
730             if(parse_taper_datestamp_log(curstr,
731                                          &ck_datestamp2, &ck_label) == 0) {
732                 printf("strange log line in %s \"start taper %s\"\n",
733                     logfile, curstr);
734             } else if (strcmp(ck_label, label)) {
735                 passlabel = !passlabel;
736             }
737         }
738         partnum = "--";
739         if(curlog == L_SUCCESS || curlog == L_PARTIAL || curlog == L_FAIL || curlog == L_CHUNK) {
740             s = curstr;
741             ch = *s++;
742
743             skip_whitespace(s, ch);
744             if(ch == '\0') {
745                 printf("strange log line in %s \"%s\"\n",
746                     logfile, curstr);
747                 continue;
748             }
749             host = s - 1;
750             skip_non_whitespace(s, ch);
751             host_undo = s - 1;
752             *host_undo = '\0';
753
754             skip_whitespace(s, ch);
755             if(ch == '\0') {
756                 printf("strange log line in %s \"%s\"\n",
757                     logfile, curstr);
758                 continue;
759             }
760             qdisk = s - 1;
761             skip_quoted_string(s, ch);
762             disk_undo = s - 1;
763             *disk_undo = '\0';
764             disk = unquote_string(qdisk);
765
766             skip_whitespace(s, ch);
767             if(ch == '\0') {
768                 printf("strange log line in %s \"%s\"\n",
769                     logfile, curstr);
770                 continue;
771             }
772             date = s - 1;
773             skip_non_whitespace(s, ch);
774             date_undo = s - 1;
775             *date_undo = '\0';
776
777             if(strlen(date) < 3) { /* old log didn't have datestamp */
778                 level = atoi(date);
779                 date = stralloc(datestamp);
780             }
781             else {
782                 if(curlog == L_CHUNK){
783                     skip_whitespace(s, ch);
784                     partnum = s - 1;
785                     skip_non_whitespace(s, ch);
786                     partnum_undo = s - 1;
787                     *partnum_undo = '\0';
788                 }
789                 skip_whitespace(s, ch);
790                 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
791                     printf("strange log line in %s \"%s\"\n",
792                     logfile, curstr);
793                     continue;
794                 }
795                 skip_integer(s, ch);
796             }
797
798             skip_whitespace(s, ch);
799             if(ch == '\0') {
800                 printf("strange log line in %s \"%s\"\n",
801                     logfile, curstr);
802                 continue;
803             }
804             rest = s - 1;
805             if((s = strchr(s, '\n')) != NULL) {
806                 *s = '\0';
807             }
808
809             dp = lookup_disk(host,disk);
810             if ( dp == NULL ) {
811                 if (dynamic_disklist == 0) {
812                     continue;
813                 }
814                 dp = add_disk(find_diskqp, host, disk);
815                 enqueue_disk(find_diskqp, dp);
816             }
817             if(find_match(host, disk) && (curlog != L_SUCCESS ||
818                 !seen_chunk_of(*output_find, date, host, disk, level))) {
819                 if(curprog == P_TAPER) {
820                     find_result_t *new_output_find =
821                         (find_result_t *)alloc(SIZEOF(find_result_t));
822                     new_output_find->next=*output_find;
823                     new_output_find->timestamp = stralloc(date);
824                     new_output_find->hostname=stralloc(host);
825                     new_output_find->diskname=stralloc(disk);
826                     new_output_find->level=level;
827                     new_output_find->partnum = stralloc(partnum);
828                     new_output_find->label=stralloc(label);
829                     new_output_find->filenum=filenum;
830                     if(curlog == L_SUCCESS || curlog == L_CHUNK) 
831                         new_output_find->status=stralloc("OK");
832                     else if(curlog == L_PARTIAL)
833                         new_output_find->status=stralloc("PARTIAL");
834                     else
835                         new_output_find->status=stralloc(rest);
836                     *output_find=new_output_find;
837                 }
838                 else if(curlog == L_FAIL) {     /* print other failures too */
839                     find_result_t *new_output_find =
840                         (find_result_t *)alloc(SIZEOF(find_result_t));
841                     new_output_find->next=*output_find;
842                     new_output_find->timestamp = stralloc(date);
843                     new_output_find->hostname=stralloc(host);
844                     new_output_find->diskname=stralloc(disk);
845                     new_output_find->level=level;
846                     new_output_find->label=stralloc(label);
847                     new_output_find->partnum=stralloc(partnum);
848                     new_output_find->filenum=0;
849                     new_output_find->status=vstralloc(
850                          "FAILED (",
851                          program_str[(int)curprog],
852                          ") ",
853                          rest,
854                          NULL);
855                     *output_find=new_output_find;
856                 }
857             }
858             amfree(disk);
859         }
860     }
861     afclose(logf);
862     return 1;
863 }
864
865
866 /*
867  * Return the set of dumps that match *all* of the given patterns (we consider
868  * an empty pattern to match .*, though).  If 'ok' is true, will only match
869  * dumps with SUCCESS status.
870  */
871 find_result_t *
872 dumps_match(
873     find_result_t *output_find,
874     char *hostname,
875     char *diskname,
876     char *datestamp,
877     char *level,
878     int ok)
879 {
880     find_result_t *cur_result;
881     find_result_t *matches = NULL;
882
883     for(cur_result=output_find;
884         cur_result;
885         cur_result=cur_result->next) {
886         char level_str[NUM_STR_SIZE];
887         snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
888         if((*hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
889            (*diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
890            (*datestamp== '\0' || match_datestamp(datestamp, cur_result->timestamp)) &&
891            (*level== '\0' || match_level(level, level_str)) &&
892            (!ok || !strcmp(cur_result->status, "OK"))){
893
894             find_result_t *curmatch = alloc(SIZEOF(find_result_t));
895             memcpy(curmatch, cur_result, SIZEOF(find_result_t));
896
897 /*
898             curmatch->hostname = stralloc(cur_result->hostname);
899             curmatch->diskname = stralloc(cur_result->diskname);
900             curmatch->datestamp = stralloc(cur_result->datestamp);
901             curmatch->partnum = stralloc(cur_result->partnum);
902             curmatch->status = stralloc(cur_result->status);
903             curmatch->level = stralloc(cur_result->level);
904 */          
905             curmatch->next = matches;
906             matches = curmatch;
907         }
908     }
909
910     return(matches);
911 }
912
913 find_result_t *
914 dump_exist(
915     find_result_t *output_find,
916     char *hostname,
917     char *diskname,
918     char *datestamp,
919     int level)
920 {
921     find_result_t *output_find_result;
922
923     for(output_find_result=output_find;
924         output_find_result;
925         output_find_result=output_find_result->next) {
926         if( !strcmp(output_find_result->hostname, hostname) &&
927             !strcmp(output_find_result->diskname, diskname) &&
928             !strcmp(output_find_result->timestamp, datestamp) &&
929             output_find_result->level == level) {
930
931             return output_find_result;
932         }
933     }
934     return(NULL);
935 }