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