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