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