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