Imported Upstream version 2.6.0
[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 void search_holding_disk(find_result_t **output_find);
41 char *find_nicedate(char *datestamp);
42 static int find_compare(const void *, const void *);
43 static int parse_taper_datestamp_log(char *logline, char **datestamp, char **level);
44 static gboolean logfile_has_tape(char * label, char * datestamp,
45                                  char * logfile);
46
47 static char *find_sort_order = NULL;
48
49 find_result_t * find_dump(disklist_t* diskqp) {
50     char *conf_logdir, *logfile = NULL;
51     int tape, maxtape, logs;
52     unsigned seq;
53     tape_t *tp;
54     find_result_t *output_find = NULL;
55
56     conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
57     maxtape = lookup_nb_tape();
58
59     for(tape = 1; tape <= maxtape; tape++) {
60
61         tp = lookup_tapepos(tape);
62         if(tp == NULL) continue;
63
64         /* search log files */
65
66         logs = 0;
67
68         /* new-style log.<date>.<seq> */
69
70         for(seq = 0; 1; seq++) {
71             char seq_str[NUM_STR_SIZE];
72
73             g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
74             logfile = newvstralloc(logfile,
75                         conf_logdir, "/log.", tp->datestamp, ".", seq_str, NULL);
76             if(access(logfile, R_OK) != 0) break;
77             if (search_logfile(&output_find, tp->label, tp->datestamp,
78                                logfile, diskqp)) {
79                 logs ++;
80             }
81         }
82
83         /* search old-style amflush log, if any */
84
85         logfile = newvstralloc(logfile, conf_logdir, "/log.",
86                                tp->datestamp, ".amflush", NULL);
87         if(access(logfile,R_OK) == 0) {
88             if (search_logfile(&output_find, tp->label, tp->datestamp,
89                                logfile, diskqp)) {
90                 logs ++;
91             }
92         }
93         
94         /* search old-style main log, if any */
95
96         logfile = newvstralloc(logfile, conf_logdir, "/log.", tp->datestamp,
97                                NULL);
98         if(access(logfile,R_OK) == 0) {
99             if (search_logfile(&output_find, tp->label, tp->datestamp,
100                                logfile, diskqp)) {
101                 logs ++;
102             }
103         }
104         if(logs == 0 && strcmp(tp->datestamp,"0") != 0)
105             g_fprintf(stderr,
106                       _("Warning: no log files found for tape %s written %s\n"),
107                       tp->label, find_nicedate(tp->datestamp));
108     }
109     amfree(logfile);
110     amfree(conf_logdir);
111
112     search_holding_disk(&output_find);
113
114     return(output_find);
115 }
116
117 char **
118 find_log(void)
119 {
120     char *conf_logdir, *logfile = NULL;
121     char *pathlogfile = NULL;
122     int tape, maxtape, logs;
123     unsigned seq;
124     tape_t *tp;
125     char **output_find_log = NULL;
126     char **current_log;
127
128     conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
129     maxtape = lookup_nb_tape();
130
131     output_find_log = alloc((maxtape*5+10) * SIZEOF(char *));
132     current_log = output_find_log;
133
134     for(tape = 1; tape <= maxtape; tape++) {
135
136         tp = lookup_tapepos(tape);
137         if(tp == NULL) continue;
138
139         /* search log files */
140
141         logs = 0;
142
143         /* new-style log.<date>.<seq> */
144
145         for(seq = 0; 1; seq++) {
146             char seq_str[NUM_STR_SIZE];
147
148             g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
149             logfile = newvstralloc(logfile, "log.", tp->datestamp, ".", seq_str, NULL);
150             pathlogfile = newvstralloc(pathlogfile, conf_logdir, "/", logfile, NULL);
151             if (access(pathlogfile, R_OK) != 0) break;
152             if (logfile_has_tape(tp->label, tp->datestamp, pathlogfile)) {
153                 if (current_log == output_find_log || strcmp(*(current_log-1), logfile)) {
154                     *current_log = stralloc(logfile);
155                     current_log++;
156                 }
157                 logs++;
158                 break;
159             }
160         }
161
162         /* search old-style amflush log, if any */
163
164         logfile = newvstralloc(logfile, "log.", tp->datestamp, ".amflush", NULL);
165         pathlogfile = newvstralloc(pathlogfile, conf_logdir, "/", logfile, NULL);
166         if (access(pathlogfile, R_OK) == 0) {
167             if (logfile_has_tape(tp->label, tp->datestamp, pathlogfile)) {
168                 if (current_log == output_find_log || strcmp(*(current_log-1), logfile)) {
169                     *current_log = stralloc(logfile);
170                     current_log++;
171                 }
172                 logs++;
173             }
174         }
175
176         /* search old-style main log, if any */
177
178         logfile = newvstralloc(logfile, "log.", tp->datestamp, NULL);
179         pathlogfile = newvstralloc(pathlogfile, conf_logdir, "/", logfile, NULL);
180         if (access(pathlogfile, R_OK) == 0) {
181             if (logfile_has_tape(tp->label, tp->datestamp, pathlogfile)) {
182                 if (current_log == output_find_log || strcmp(*(current_log-1), logfile)) {
183                     *current_log = stralloc(logfile);
184                     current_log++;
185                 }
186                 logs++;
187             }
188         }
189
190         if(logs == 0 && strcmp(tp->datestamp,"0") != 0)
191             g_fprintf(stderr, _("Warning: no log files found for tape %s written %s\n"),
192                    tp->label, find_nicedate(tp->datestamp));
193     }
194     amfree(logfile);
195     amfree(pathlogfile);
196     amfree(conf_logdir);
197     *current_log = NULL;
198     return(output_find_log);
199 }
200
201 void
202 search_holding_disk(
203     find_result_t **output_find)
204 {
205     GSList *holding_file_list;
206     GSList *e;
207     char *holding_file;
208     disk_t *dp;
209     dumpfile_t file;
210
211     holding_file_list = holding_get_files(NULL, 1);
212
213     for(e = holding_file_list; e != NULL; e = e->next) {
214         holding_file = (char *)e->data;
215
216         if (!holding_file_get_dumpfile(holding_file, &file))
217             continue;
218
219         if (file.dumplevel < 0 || file.dumplevel > 9)
220             continue;
221
222         dp = NULL;
223         for(;;) {
224             char *s;
225             if((dp = lookup_disk(file.name, file.disk)))
226                 break;
227             if((s = strrchr(file.name,'.')) == NULL)
228                 break;
229             *s = '\0';
230         }
231         if ( dp == NULL ) {
232             continue;
233         }
234
235         if(find_match(file.name,file.disk)) {
236             find_result_t *new_output_find =
237                 alloc(SIZEOF(find_result_t));
238             new_output_find->next=*output_find;
239             new_output_find->timestamp = stralloc(file.datestamp);
240             new_output_find->hostname = stralloc(file.name);
241             new_output_find->diskname = stralloc(file.disk);
242             new_output_find->level=file.dumplevel;
243             new_output_find->label=stralloc(holding_file);
244             new_output_find->partnum=stralloc("--");
245             new_output_find->filenum=0;
246             new_output_find->status=stralloc("OK");
247             *output_find=new_output_find;
248         }
249     }
250
251     g_slist_free_full(holding_file_list);
252 }
253
254 static int
255 find_compare(
256     const void *i1,
257     const void *j1)
258 {
259     int compare=0;
260     find_result_t *i, *j;
261
262     size_t nb_compare=strlen(find_sort_order);
263     size_t k;
264
265     for(k=0;k<nb_compare;k++) {
266         char sort_key = find_sort_order[k];
267         if (isupper((int)sort_key)) {
268             /* swap */
269             sort_key = tolower(sort_key);
270             j = *(find_result_t **)i1;
271             i = *(find_result_t **)j1;
272         } else {
273             i = *(find_result_t **)i1;
274             j = *(find_result_t **)j1;
275         }            
276         
277         switch (sort_key) {
278         case 'h' : compare=strcmp(i->hostname,j->hostname);
279                    break;
280         case 'k' : compare=strcmp(i->diskname,j->diskname);
281                    break;
282         case 'd' : compare=strcmp(i->timestamp,j->timestamp);
283                    break;
284         case 'l' : compare=j->level - i->level;
285                    break;
286         case 'f' : compare=(i->filenum == j->filenum) ? 0 :
287                            ((i->filenum < j->filenum) ? -1 : 1);
288                    break;
289         case 'b' : compare=compare_possibly_null_strings(i->label,
290                                                          j->label);
291                    break;
292         case 'p' :
293                    if(strcmp(i->partnum, "--") != 0 &&
294                       strcmp(j->partnum, "--") != 0){
295                       compare = atoi(i->partnum) - atoi(j->partnum);
296                    }
297                    else compare=strcmp(i->partnum,j->partnum);
298                    break;
299         }
300         if(compare != 0)
301             return compare;
302     }
303     return 0;
304 }
305
306 void
307 sort_find_result(
308     char *sort_order,
309     find_result_t **output_find)
310 {
311     find_result_t *output_find_result;
312     find_result_t **array_find_result = NULL;
313     size_t nb_result=0;
314     size_t no_result;
315
316     find_sort_order = sort_order;
317     /* qsort core dump if nothing to sort */
318     if(*output_find==NULL)
319         return;
320
321     /* How many result */
322     for(output_find_result=*output_find;
323         output_find_result;
324         output_find_result=output_find_result->next) {
325         nb_result++;
326     }
327
328     /* put the list in an array */
329     array_find_result=alloc(nb_result * SIZEOF(find_result_t *));
330     for(output_find_result=*output_find,no_result=0;
331         output_find_result;
332         output_find_result=output_find_result->next,no_result++) {
333         array_find_result[no_result]=output_find_result;
334     }
335
336     /* sort the array */
337     qsort(array_find_result,nb_result,SIZEOF(find_result_t *),
338           find_compare);
339
340     /* put the sorted result in the list */
341     for(no_result=0;
342         no_result<nb_result-1; no_result++) {
343         array_find_result[no_result]->next = array_find_result[no_result+1];
344     }
345     array_find_result[nb_result-1]->next=NULL;
346     *output_find=array_find_result[0];
347     amfree(array_find_result);
348 }
349
350 void
351 print_find_result(
352     find_result_t *output_find)
353 {
354     find_result_t *output_find_result;
355     int max_len_datestamp = 4;
356     int max_len_hostname  = 4;
357     int max_len_diskname  = 4;
358     int max_len_level     = 2;
359     int max_len_label     =12;
360     int max_len_filenum   = 4;
361     int max_len_part      = 4;
362     int max_len_status    = 6;
363     size_t len;
364
365     for(output_find_result=output_find;
366         output_find_result;
367         output_find_result=output_find_result->next) {
368         char *qdiskname;
369
370         len=strlen(find_nicedate(output_find_result->timestamp));
371         if((int)len > max_len_datestamp)
372             max_len_datestamp=(int)len;
373
374         len=strlen(output_find_result->hostname);
375         if((int)len > max_len_hostname)
376             max_len_hostname = (int)len;
377
378         qdiskname=quote_string(output_find_result->diskname);
379         len=strlen(qdiskname);
380         amfree(qdiskname);
381         if((int)len > max_len_diskname)
382             max_len_diskname = (int)len;
383
384         if (output_find_result->label != NULL) {
385             len=strlen(output_find_result->label);
386             if((int)len > max_len_label)
387                 max_len_label = (int)len;
388         }
389
390         len=strlen(output_find_result->status);
391         if((int)len > max_len_status)
392             max_len_status = (int)len;
393
394         len=strlen(output_find_result->partnum);
395         if((int)len > max_len_part)
396             max_len_part = (int)len;
397     }
398
399     /*
400      * Since status is the rightmost field, we zap the maximum length
401      * because it is not needed.  The code is left in place in case
402      * another column is added later.
403      */
404     max_len_status = 1;
405
406     if(output_find==NULL) {
407         g_printf(_("\nNo dump to list\n"));
408     }
409     else {
410         g_printf(_("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n"),
411                max_len_datestamp-4,"",
412                max_len_hostname-4 ,"",
413                max_len_diskname-4 ,"",
414                max_len_level-2    ,"",
415                max_len_label-12   ,"",
416                max_len_filenum-4  ,"",
417                max_len_part-4  ,"");
418         for(output_find_result=output_find;
419                 output_find_result;
420                 output_find_result=output_find_result->next) {
421             char *qdiskname;
422             char * formatted_label;
423
424             qdiskname = quote_string(output_find_result->diskname);
425             formatted_label = output_find_result->label;
426             if (formatted_label == NULL)
427                 formatted_label = "";
428             /*@ignore@*/
429             g_printf("%-*s %-*s %-*s %*d %-*s %*lld %*s %-*s\n",
430                      max_len_datestamp, 
431                      find_nicedate(output_find_result->timestamp),
432                      max_len_hostname,  output_find_result->hostname,
433                      max_len_diskname,  qdiskname,
434                      max_len_level,     output_find_result->level,
435                      max_len_label,     formatted_label,
436                      max_len_filenum,   (long long)output_find_result->filenum,
437                      max_len_part,      output_find_result->partnum,
438                      max_len_status,    output_find_result->status
439                     );
440             /*@end@*/
441             amfree(qdiskname);
442         }
443     }
444 }
445
446 void
447 free_find_result(
448     find_result_t **output_find)
449 {
450     find_result_t *output_find_result, *prev;
451
452     prev=NULL;
453     for(output_find_result=*output_find;
454             output_find_result;
455             output_find_result=output_find_result->next) {
456         amfree(prev);
457         amfree(output_find_result->timestamp);
458         amfree(output_find_result->hostname);
459         amfree(output_find_result->diskname);
460         amfree(output_find_result->label);
461         amfree(output_find_result->partnum);
462         amfree(output_find_result->status);
463         prev = output_find_result;
464     }
465     amfree(prev);
466     *output_find = NULL;
467 }
468
469 int
470 find_match(
471     char *host,
472     char *disk)
473 {
474     disk_t *dp = lookup_disk(host,disk);
475     return (dp && dp->todo);
476 }
477
478 char *
479 find_nicedate(
480     char *datestamp)
481 {
482     static char nice[20];
483     int year, month, day;
484     int hours, minutes, seconds;
485     char date[9], atime[7];
486     int  numdate, numtime;
487
488     strncpy(date, datestamp, 8);
489     date[8] = '\0';
490     numdate = atoi(date);
491     year  = numdate / 10000;
492     month = (numdate / 100) % 100;
493     day   = numdate % 100;
494
495     if(strlen(datestamp) <= 8) {
496         g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
497                 year, month, day);
498     }
499     else {
500         strncpy(atime, &(datestamp[8]), 6);
501         atime[6] = '\0';
502         numtime = atoi(atime);
503         hours = numtime / 10000;
504         minutes = (numtime / 100) % 100;
505         seconds = numtime % 100;
506
507         g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d %02d:%02d:%02d",
508                 year, month, day, hours, minutes, seconds);
509     }
510
511     return nice;
512 }
513
514 static int
515 parse_taper_datestamp_log(
516     char *logline,
517     char **datestamp,
518     char **label)
519 {
520     char *s;
521     int ch;
522
523     s = logline;
524     ch = *s++;
525
526     skip_whitespace(s, ch);
527     if(ch == '\0') {
528         return 0;
529     }
530     if(strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
531         return 0;
532     }
533
534     skip_whitespace(s, ch);
535     if(ch == '\0') {
536         return 0;
537     }
538     *datestamp = s - 1;
539     skip_non_whitespace(s, ch);
540     s[-1] = '\0';
541
542     skip_whitespace(s, ch);
543     if(ch == '\0') {
544         return 0;
545     }
546     if(strncmp_const_skip(s - 1, "label", s, ch) != 0) {
547         return 0;
548     }
549
550     skip_whitespace(s, ch);
551     if(ch == '\0') {
552         return 0;
553     }
554     *label = s - 1;
555     skip_non_whitespace(s, ch);
556     s[-1] = '\0';
557
558     return 1;
559 }
560
561 /* Returns TRUE if the given logfile mentions the given tape. */
562 static gboolean logfile_has_tape(char * label, char * datestamp,
563                                  char * logfile) {
564     FILE * logf;
565     char * ck_datestamp, *ck_label;
566     if((logf = fopen(logfile, "r")) == NULL) {
567         error(_("could not open logfile %s: %s"), logfile, strerror(errno));
568         /*NOTREACHED*/
569     }
570
571     while(get_logline(logf)) {
572         if(curlog == L_START && curprog == P_TAPER) {
573             if(parse_taper_datestamp_log(curstr,
574                                          &ck_datestamp, &ck_label) == 0) {
575                 g_printf(_("strange log line \"start taper %s\" curstr='%s'\n"),
576                          logfile, curstr);
577             } else if(strcmp(ck_datestamp, datestamp) == 0
578                       && strcmp(ck_label, label) == 0) {
579                 afclose(logf);
580                 return TRUE;
581             }
582         }
583     }
584
585     afclose(logf);
586     return FALSE;
587 }
588
589 /* Like (strcmp(label1, label2) == 0), except that NULL values force TRUE. */
590 static gboolean volume_matches(const char * label1, const char * label2) {
591     return (label1 == NULL || label2 == NULL || strcmp(label1, label2) == 0);
592 }
593
594 /* WARNING: Function accesses globals find_diskqp, curlog, curlog, curstr,
595  * dynamic_disklist */
596 gboolean
597 search_logfile(
598     find_result_t **output_find,
599     const char *label,
600     const char *passed_datestamp,
601     const char *logfile,
602     disklist_t * dynamic_disklist)
603 {
604     FILE *logf;
605     char *host, *host_undo;
606     char *disk, *qdisk, *disk_undo;
607     char *date, *date_undo;
608     char *partnum=NULL, *partnum_undo;
609     char *number;
610     int fileno;
611     char *current_label = NULL;
612     char *rest;
613     char *ck_label=NULL;
614     int level = 0; 
615     off_t filenum;
616     char *ck_datestamp, *datestamp;
617     char *s;
618     int ch;
619     disk_t *dp;
620     find_result_t *part_find = NULL;  /* List for all part of a DLE */
621     find_result_t *a_part_find;
622     gboolean right_label = FALSE;
623     gboolean found_something = FALSE;
624
625     g_return_val_if_fail(output_find != NULL, 0);
626     g_return_val_if_fail(logfile != NULL, 0);
627
628     datestamp = g_strdup(passed_datestamp);
629
630     if((logf = fopen(logfile, "r")) == NULL) {
631         error(_("could not open logfile %s: %s"), logfile, strerror(errno));
632         /*NOTREACHED*/
633     }
634
635     filenum = (off_t)0;
636     while(get_logline(logf)) {
637         if (curlog == L_START && curprog == P_TAPER) {
638             if(parse_taper_datestamp_log(curstr, &ck_datestamp,
639                                          &ck_label) == 0) {
640                 g_printf(_("strange log line in %s \"start taper %s\"\n"),
641                          logfile, curstr);
642                 continue;
643             }
644             if (datestamp != NULL) {
645                 if (strcmp(datestamp, ck_datestamp) != 0) {
646                     g_printf(_("Log file %s stamped %s, expecting %s!\n"),
647                              logfile, ck_datestamp, datestamp);
648                     break;
649                 }
650             }
651             
652             right_label = volume_matches(label, ck_label);
653             if (label && datestamp && right_label) {
654                 found_something = TRUE;
655             }
656             amfree(current_label);
657             current_label = g_strdup(ck_label);
658             if (datestamp == NULL) {
659                 datestamp = g_strdup(ck_datestamp);
660             }
661         }
662         if (!right_label) {
663             continue;
664         }
665         if ((curlog == L_SUCCESS ||
666              curlog == L_CHUNK || curlog == L_PART || curlog == L_PARTPARTIAL) &&
667             curprog == P_TAPER) {
668             filenum++;
669         }
670         partnum = "--";
671         if (curlog == L_SUCCESS || curlog == L_CHUNKSUCCESS ||
672             curlog == L_DONE    || curlog == L_FAIL ||
673             curlog == L_CHUNK   || curlog == L_PART || curlog == L_PARTIAL ||
674             curlog == L_PARTPARTIAL ) {
675             s = curstr;
676             ch = *s++;
677
678             skip_whitespace(s, ch);
679             if(ch == '\0') {
680                 g_printf(_("strange log line in %s \"%s\"\n"),
681                     logfile, curstr);
682                 continue;
683             }
684
685             if (curlog == L_PART || curlog == L_PARTPARTIAL) {
686                 char * part_label = s - 1;
687                 skip_non_whitespace(s, ch);
688                 s[-1] = '\0';
689
690                 if (strcmp(current_label, part_label) != 0) {
691                     g_printf("PART label %s doesn't match START label %s\n",
692                              part_label, current_label);
693                     continue;
694                 }
695                 skip_whitespace(s, ch);
696                 if(ch == '\0') {
697                     g_printf("strange log line in %s \"%s\"\n",
698                            logfile, curstr);
699                     continue;
700                 }
701
702                 number = s - 1;
703                 skip_non_whitespace(s, ch);
704                 s[-1] = '\0';
705                 fileno = atoi(number);
706                 filenum = fileno;
707
708                 skip_whitespace(s, ch);
709                 if(ch == '\0') {
710                     g_printf("strange log line in %s \"%s\"\n",
711                            logfile, curstr);
712                     continue;
713                 }
714             }
715
716             host = s - 1;
717             skip_non_whitespace(s, ch);
718             host_undo = s - 1;
719             *host_undo = '\0';
720
721             skip_whitespace(s, ch);
722             if(ch == '\0') {
723                 g_printf(_("strange log line in %s \"%s\"\n"),
724                     logfile, curstr);
725                 continue;
726             }
727             qdisk = s - 1;
728             skip_quoted_string(s, ch);
729             disk_undo = s - 1;
730             *disk_undo = '\0';
731             disk = unquote_string(qdisk);
732
733             skip_whitespace(s, ch);
734             if(ch == '\0') {
735                 g_printf(_("strange log line in %s \"%s\"\n"),
736                          logfile, curstr);
737                 continue;
738             }
739             date = s - 1;
740             skip_non_whitespace(s, ch);
741             date_undo = s - 1;
742             *date_undo = '\0';
743
744             if(strlen(date) < 3) { /* old log didn't have datestamp */
745                 level = atoi(date);
746                 date = stralloc(datestamp);
747             } else {
748                 if (curlog == L_CHUNK || curlog == L_PART ||
749                     curlog == L_PARTPARTIAL || curlog == L_DONE){
750                     skip_whitespace(s, ch);
751                     partnum = s - 1;
752                     skip_non_whitespace(s, ch);
753                     partnum_undo = s - 1;
754                     *partnum_undo = '\0';
755                 }
756                 skip_whitespace(s, ch);
757                 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
758                     g_printf(_("strange log line in %s \"%s\"\n"),
759                     logfile, curstr);
760                     continue;
761                 }
762                 skip_integer(s, ch);
763             }
764
765             skip_whitespace(s, ch);
766             if(ch == '\0') {
767                 g_printf(_("strange log line in %s \"%s\"\n"),
768                     logfile, curstr);
769                 continue;
770             }
771             rest = s - 1;
772             if((s = strchr(s, '\n')) != NULL) {
773                 *s = '\0';
774             }
775
776             dp = lookup_disk(host,disk);
777             if ( dp == NULL ) {
778                 if (dynamic_disklist == NULL) {
779                     continue;
780                 }
781                 dp = add_disk(dynamic_disklist, host, disk);
782                 enqueue_disk(dynamic_disklist, dp);
783             }
784             if (find_match(host, disk)) {
785                 if(curprog == P_TAPER) {
786                     find_result_t *new_output_find =
787                         (find_result_t *)alloc(SIZEOF(find_result_t));
788                     new_output_find->timestamp = stralloc(date);
789                     new_output_find->hostname=stralloc(host);
790                     new_output_find->diskname=stralloc(disk);
791                     new_output_find->level=level;
792                     new_output_find->partnum = stralloc(partnum);
793                     new_output_find->label=stralloc(current_label);
794                     new_output_find->status=NULL;
795                     new_output_find->filenum=filenum;
796                     new_output_find->next=NULL;
797                     if (curlog == L_SUCCESS) {
798                         new_output_find->status = stralloc("OK");
799                         new_output_find->next = *output_find;
800                         *output_find = new_output_find;
801                         found_something = TRUE;
802                     } else if (curlog == L_CHUNKSUCCESS || curlog == L_DONE ||
803                                curlog == L_PARTIAL      || curlog == L_FAIL) {
804                         /* result line */
805                         if (curlog == L_PARTIAL || curlog == L_FAIL) {
806                             /* change status of each part */
807                             for (a_part_find = part_find; a_part_find;
808                                  a_part_find = a_part_find->next) {
809                                 if (curlog == L_PARTIAL)
810                                     a_part_find->status = stralloc("PARTIAL");
811                                 else
812                                     a_part_find->status = stralloc(rest);
813                             }
814                         }
815                         if (part_find) { /* find last element */
816                             for (a_part_find = part_find;
817                                  a_part_find->next != NULL;
818                                  a_part_find=a_part_find->next) {
819                             }
820                             /* merge part_find to *output_find */
821                             a_part_find->next = *output_find;
822                             *output_find = part_find;
823                             part_find = NULL;
824                             found_something = TRUE;
825                         }
826                         free_find_result(&new_output_find);
827                     } else { /* part line */
828                         if (curlog == L_PART || curlog == L_CHUNK)
829                             new_output_find->status=stralloc("OK");
830                         else /* PARTPARTIAL */
831                             new_output_find->status=stralloc("PARTIAL");
832                         /* Add to part_find list */
833                         new_output_find->next = part_find;
834                         part_find = new_output_find;
835                         found_something = TRUE;
836                     }
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=NULL;
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                     found_something = TRUE;
857                 }
858             }
859             amfree(disk);
860         }
861     }
862
863     if (part_find != NULL) {
864         if (label) {
865             /* parse log file until PARTIAL/DONE/SUCCESS/FAIL from taper */
866             while(get_logline(logf)) {
867                 if (curprog == P_TAPER &&
868                     (curlog == L_DONE || curlog == L_SUCCESS ||
869                      curlog == L_PARTIAL || curlog == L_FAIL)) {
870                     break;
871                 }
872             }
873         }
874         for (a_part_find = part_find; a_part_find;
875              a_part_find = a_part_find->next) {
876             if (curlog == L_PARTIAL)
877                 a_part_find->status = stralloc("PARTIAL");
878             else if (curlog == L_FAIL)
879                 a_part_find->status = stralloc("FAIL");
880         }
881         for (a_part_find = part_find;
882              a_part_find->next != NULL;
883              a_part_find=a_part_find->next) {
884         }
885         /* merge part_find to *output_find */
886         a_part_find->next = *output_find;
887         *output_find = part_find;
888         part_find = NULL;
889     }
890
891     afclose(logf);
892     amfree(datestamp);
893     amfree(current_label);
894
895     return found_something;
896 }
897
898
899 /*
900  * Return the set of dumps that match *all* of the given patterns (we consider
901  * an empty pattern to match .*, though).  If 'ok' is true, will only match
902  * dumps with SUCCESS status.
903  *
904  * Returns a newly allocated list of results, where all strings are also newly
905  * allocated.  Apparently some part of Amanda leaks under this condition.
906  */
907 find_result_t *
908 dumps_match(
909     find_result_t *output_find,
910     char *hostname,
911     char *diskname,
912     char *datestamp,
913     char *level,
914     int ok)
915 {
916     find_result_t *cur_result;
917     find_result_t *matches = NULL;
918
919     for(cur_result=output_find;
920         cur_result;
921         cur_result=cur_result->next) {
922         char level_str[NUM_STR_SIZE];
923         g_snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
924         if((!hostname || *hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
925            (!diskname || *diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
926            (!datestamp || *datestamp== '\0' || match_datestamp(datestamp, cur_result->timestamp)) &&
927            (!level || *level== '\0' || match_level(level, level_str)) &&
928            (!ok || !strcmp(cur_result->status, "OK"))){
929
930             find_result_t *curmatch = alloc(SIZEOF(find_result_t));
931             memcpy(curmatch, cur_result, SIZEOF(find_result_t));
932
933             curmatch->timestamp = stralloc(cur_result->timestamp);
934             curmatch->hostname = stralloc(cur_result->hostname);
935             curmatch->diskname = stralloc(cur_result->diskname);
936             curmatch->level = cur_result->level;
937             curmatch->label = stralloc(cur_result->label);
938             curmatch->filenum = cur_result->filenum;
939             curmatch->status = stralloc(cur_result->status);
940             curmatch->partnum = stralloc(cur_result->partnum);
941
942             curmatch->next = matches;
943             matches = curmatch;
944         }
945     }
946
947     return(matches);
948 }
949
950 find_result_t *
951 dump_exist(
952     find_result_t *output_find,
953     char *hostname,
954     char *diskname,
955     char *datestamp,
956     int level)
957 {
958     find_result_t *output_find_result;
959
960     for(output_find_result=output_find;
961         output_find_result;
962         output_find_result=output_find_result->next) {
963         if( !strcmp(output_find_result->hostname, hostname) &&
964             !strcmp(output_find_result->diskname, diskname) &&
965             !strcmp(output_find_result->timestamp, datestamp) &&
966             output_find_result->level == level) {
967
968             return output_find_result;
969         }
970     }
971     return(NULL);
972 }