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