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