Imported Upstream version 2.4.5
[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.6.2.4.4.2.2.5.2.1 2004/02/02 20:29:12 martinea Exp $
29  *
30  * controlling process for the Amanda backup system
31  */
32 #include "amanda.h"
33 #include "conffile.h"
34 #include "tapefile.h"
35 #include "logfile.h"
36 #include "holding.h"
37 #include "find.h"
38
39 void find P((int argc, char **argv));
40 int find_match P((char *host, char *disk));
41 int search_logfile P((find_result_t **output_find, char *label, int datestamp, int datestamp_aux, char *logfile));
42 void search_holding_disk P((find_result_t **output_find));
43 char *find_nicedate P((int datestamp));
44
45 static char *find_sort_order = NULL;
46 int dynamic_disklist = 0;
47 disklist_t* find_diskqp = NULL;
48
49 find_result_t *find_dump(dyna_disklist, diskqp)
50 int dyna_disklist;
51 disklist_t* diskqp;
52 {
53     char *conf_logdir, *logfile = NULL;
54     int tape, maxtape, seq, logs;
55     tape_t *tp;
56     find_result_t *output_find = NULL;
57
58     dynamic_disklist = dyna_disklist;
59     find_diskqp = diskqp;
60     conf_logdir = getconf_str(CNF_LOGDIR);
61     if (*conf_logdir == '/') {
62         conf_logdir = stralloc(conf_logdir);
63     } else {
64         conf_logdir = stralloc2(config_dir, conf_logdir);
65     }
66     maxtape = lookup_nb_tape();
67
68     for(tape = 1; tape <= maxtape; tape++) {
69         char ds_str[NUM_STR_SIZE];
70
71         tp = lookup_tapepos(tape);
72         if(tp == NULL) continue;
73         ap_snprintf(ds_str, sizeof(ds_str), "%d", tp->datestamp);
74
75         /* search log files */
76
77         logs = 0;
78
79         /* new-style log.<date>.<seq> */
80
81         for(seq = 0; 1; seq++) {
82             char seq_str[NUM_STR_SIZE];
83
84             ap_snprintf(seq_str, sizeof(seq_str), "%d", seq);
85             logfile = newvstralloc(logfile,
86                         conf_logdir, "/log.", ds_str, ".", seq_str, NULL);
87             if(access(logfile, R_OK) != 0) break;
88             logs += search_logfile(&output_find, tp->label, tp->datestamp, seq, logfile);
89         }
90
91         /* search old-style amflush log, if any */
92
93         logfile = newvstralloc(logfile,
94                                conf_logdir, "/log.", ds_str, ".amflush", NULL);
95         if(access(logfile,R_OK) == 0) {
96             logs += search_logfile(&output_find, tp->label, tp->datestamp, 1000, logfile);
97         }
98
99         /* search old-style main log, if any */
100
101         logfile = newvstralloc(logfile, conf_logdir, "/log.", ds_str, NULL);
102         if(access(logfile,R_OK) == 0) {
103             logs += search_logfile(&output_find, tp->label, tp->datestamp, -1, logfile);
104         }
105         if(logs == 0 && tp->datestamp != 0)
106             printf("Warning: no log files found for tape %s written %s\n",
107                    tp->label, find_nicedate(tp->datestamp));
108     }
109     amfree(logfile);
110     amfree(conf_logdir);
111
112     search_holding_disk(&output_find);
113     return(output_find);
114 }
115
116 char **find_log()
117 {
118     char *conf_logdir, *logfile = NULL;
119     int tape, maxtape, seq, logs;
120     tape_t *tp;
121     char **output_find_log = NULL;
122     char **current_log;
123
124     conf_logdir = getconf_str(CNF_LOGDIR);
125     if (*conf_logdir == '/') {
126         conf_logdir = stralloc(conf_logdir);
127     } else {
128         conf_logdir = stralloc2(config_dir, conf_logdir);
129     }
130     maxtape = lookup_nb_tape();
131
132     output_find_log = alloc((maxtape*5+10) * sizeof(char *));
133     current_log = output_find_log;
134
135     for(tape = 1; tape <= maxtape; tape++) {
136         char ds_str[NUM_STR_SIZE];
137
138         tp = lookup_tapepos(tape);
139         if(tp == NULL) continue;
140         ap_snprintf(ds_str, sizeof(ds_str), "%d", tp->datestamp);
141
142         /* search log files */
143
144         logs = 0;
145
146         /* new-style log.<date>.<seq> */
147
148         for(seq = 0; 1; seq++) {
149             char seq_str[NUM_STR_SIZE];
150
151             ap_snprintf(seq_str, sizeof(seq_str), "%d", seq);
152             logfile = newvstralloc(logfile,
153                         conf_logdir, "/log.", ds_str, ".", seq_str, NULL);
154             if(access(logfile, R_OK) != 0) break;
155             if( search_logfile(NULL, tp->label, tp->datestamp, seq, logfile)) {
156                 *current_log = vstralloc("log.", ds_str, ".", seq_str, NULL);
157                 current_log++;
158                 logs++;
159                 break;
160             }
161         }
162
163         /* search old-style amflush log, if any */
164
165         logfile = newvstralloc(logfile,
166                                conf_logdir, "/log.", ds_str, ".amflush", NULL);
167         if(access(logfile,R_OK) == 0) {
168             if( search_logfile(NULL, tp->label, tp->datestamp, 1000, logfile)) {
169                 *current_log = vstralloc("log.", ds_str, ".amflush", NULL);
170                 current_log++;
171                 logs++;
172             }
173         }
174
175         /* search old-style main log, if any */
176
177         logfile = newvstralloc(logfile, conf_logdir, "/log.", ds_str, NULL);
178         if(access(logfile,R_OK) == 0) {
179             if(search_logfile(NULL, tp->label, tp->datestamp, -1, logfile)) {
180                 *current_log = vstralloc("log.", ds_str, NULL);
181                 current_log++;
182                 logs++;
183             }
184         }
185         if(logs == 0 && tp->datestamp != 0)
186             printf("Warning: no log files found for tape %s written %s\n",
187                    tp->label, find_nicedate(tp->datestamp));
188     }
189     amfree(logfile);
190     amfree(conf_logdir);
191     *current_log = NULL;
192     return(output_find_log);
193 }
194
195 void search_holding_disk(output_find)
196 find_result_t **output_find;
197 {
198     holdingdisk_t *hdisk;
199     sl_t  *holding_list;
200     sle_t *dir;
201     char *sdirname = NULL;
202     char *destname = NULL;
203     char *hostname = NULL;
204     char *diskname = NULL;
205     DIR *workdir;
206     struct dirent *entry;
207     int level;
208     disk_t *dp;
209
210     holding_list = pick_all_datestamp(1);
211
212     for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next) {
213         for(dir = holding_list->first; dir != NULL; dir = dir->next) {
214             sdirname = newvstralloc(sdirname,
215                                     hdisk->diskdir, "/", dir->name,
216                                     NULL);
217             if((workdir = opendir(sdirname)) == NULL) {
218                 continue;
219             }
220
221             while((entry = readdir(workdir)) != NULL) {
222                 if(is_dot_or_dotdot(entry->d_name)) {
223                     continue;
224                 }
225                 destname = newvstralloc(destname,
226                                         sdirname, "/", entry->d_name,
227                                         NULL);
228                 if(is_emptyfile(destname)) {
229                     continue;
230                 }
231                 amfree(hostname);
232                 amfree(diskname);
233                 if(get_amanda_names(destname, &hostname, &diskname, &level) != F_DUMPFILE) {
234                     continue;
235                 }
236                 if(level < 0 || level > 9)
237                     continue;
238
239                 dp = NULL;
240                 for(;;) {
241                     char *s;
242                     if((dp = lookup_disk(hostname, diskname)))
243                         break;
244                     if((s = strrchr(hostname,'.')) == NULL)
245                         break;
246                     *s = '\0';
247                 }
248                 if ( dp == NULL ) {
249                     continue;
250                 }
251
252                 if(find_match(hostname,diskname)) {
253                     find_result_t *new_output_find =
254                         alloc(sizeof(find_result_t));
255                     new_output_find->next=*output_find;
256                     if(strlen(dir->name) == 8) {
257                         new_output_find->datestamp=atoi(dir->name);
258                         new_output_find->timestamp=stralloc2(dir->name, "000000");
259                     }
260                     else if(strlen(dir->name) == 14) {
261                         char *name = stralloc(dir->name);
262                         name[8] = '\0';
263                         new_output_find->datestamp=atoi(name);
264                         new_output_find->timestamp=stralloc(dir->name);
265                         amfree(name);
266                     }
267                     else {
268                         error("Bad date\n");
269                     }
270                     new_output_find->datestamp_aux=1001;
271                     new_output_find->hostname=hostname;
272                     hostname = NULL;
273                     new_output_find->diskname=diskname;
274                     diskname = NULL;
275                     new_output_find->level=level;
276                     new_output_find->label=stralloc(destname);
277                     new_output_find->filenum=0;
278                     new_output_find->status=stralloc("OK");
279                     *output_find=new_output_find;
280                 }
281             }
282             closedir(workdir);
283         }       
284     }
285     free_sl(holding_list);
286     holding_list = NULL;
287     amfree(destname);
288     amfree(sdirname);
289     amfree(hostname);
290     amfree(diskname);
291 }
292
293 static int find_compare(i1, j1)
294 const void *i1;
295 const void *j1;
296 {
297     int compare=0;
298     find_result_t **i = (find_result_t **)i1;
299     find_result_t **j = (find_result_t **)j1;
300
301     int nb_compare=strlen(find_sort_order);
302     int k;
303
304     for(k=0;k<nb_compare;k++) {
305         switch (find_sort_order[k]) {
306         case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
307                    break;
308         case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
309                    break;
310         case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
311                    break;
312         case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
313                    break;
314         case 'd' : compare=(*i)->datestamp - (*j)->datestamp;
315                    if (compare == 0)
316                         compare = (*i)->datestamp_aux - (*j)->datestamp_aux;
317                    break;
318         case 'D' : compare=(*j)->datestamp - (*i)->datestamp;
319                    if (compare == 0)
320                         compare = (*j)->datestamp_aux - (*i)->datestamp_aux;
321                    break;
322         case 'l' : compare=(*j)->level - (*i)->level;
323                    break;
324         case 'L' : compare=(*i)->level - (*j)->level;
325                    break;
326         case 'b' : compare=strcmp((*i)->label,(*j)->label);
327                    break;
328         case 'B' : compare=strcmp((*j)->label,(*i)->label);
329                    break;
330         }
331         if(compare != 0)
332             return compare;
333     }
334     return 0;
335 }
336
337 void sort_find_result(sort_order, output_find)
338 char *sort_order;
339 find_result_t **output_find;
340 {
341     find_result_t *output_find_result;
342     find_result_t **array_find_result = NULL;
343     int nb_result=0;
344     int no_result;
345
346     find_sort_order = sort_order;
347     /* qsort core dump if nothing to sort */
348     if(*output_find==NULL)
349         return;
350
351     /* How many result */
352     for(output_find_result=*output_find;
353         output_find_result;
354         output_find_result=output_find_result->next) {
355         nb_result++;
356     }
357
358     /* put the list in an array */
359     array_find_result=alloc(nb_result * sizeof(find_result_t *));
360     for(output_find_result=*output_find,no_result=0;
361         output_find_result;
362         output_find_result=output_find_result->next,no_result++) {
363         array_find_result[no_result]=output_find_result;
364     }
365
366     /* sort the array */
367     qsort(array_find_result,nb_result,sizeof(find_result_t *),
368           find_compare);
369
370     /* put the sorted result in the list */
371     for(no_result=0;
372         no_result<nb_result-1; no_result++) {
373         array_find_result[no_result]->next = array_find_result[no_result+1];
374     }
375     array_find_result[nb_result-1]->next=NULL;
376     *output_find=array_find_result[0];
377     amfree(array_find_result);
378 }
379
380 void print_find_result(output_find)
381 find_result_t *output_find;
382 {
383     find_result_t *output_find_result;
384     int max_len_datestamp = 4;
385     int max_len_hostname  = 4;
386     int max_len_diskname  = 4;
387     int max_len_level     = 2;
388     int max_len_label     =12;
389     int max_len_filenum   = 4;
390     int max_len_status    = 6;
391     int len;
392
393     for(output_find_result=output_find;
394         output_find_result;
395         output_find_result=output_find_result->next) {
396
397         len=strlen(find_nicedate(output_find_result->datestamp));
398         if(len>max_len_datestamp) max_len_datestamp=len;
399
400         len=strlen(output_find_result->hostname);
401         if(len>max_len_hostname) max_len_hostname=len;
402
403         len=strlen(output_find_result->diskname);
404         if(len>max_len_diskname) max_len_diskname=len;
405
406         len=strlen(output_find_result->label);
407         if(len>max_len_label) max_len_label=len;
408
409         len=strlen(output_find_result->status);
410         if(len>max_len_status) max_len_status=len;
411     }
412
413     /*
414      * Since status is the rightmost field, we zap the maximum length
415      * because it is not needed.  The code is left in place in case
416      * another column is added later.
417      */
418     max_len_status = 1;
419
420     if(output_find==NULL) {
421         printf("\nNo dump to list\n");
422     }
423     else {
424         printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s status\n",
425                max_len_datestamp-4,"",
426                max_len_hostname-4 ,"",
427                max_len_diskname-4 ,"",
428                max_len_level-2    ,"",
429                max_len_label-12   ,"",
430                max_len_filenum-4  ,"");
431         for(output_find_result=output_find;
432                 output_find_result;
433                 output_find_result=output_find_result->next) {
434
435             printf("%-*s %-*s %-*s %*d %-*s %*d %-*s\n",
436                     max_len_datestamp, 
437                         find_nicedate(output_find_result->datestamp),
438                     max_len_hostname,  output_find_result->hostname,
439                     max_len_diskname,  output_find_result->diskname,
440                     max_len_level,     output_find_result->level,
441                     max_len_label,     output_find_result->label,
442                     max_len_filenum,   output_find_result->filenum,
443                     max_len_status,    output_find_result->status);
444         }
445     }
446 }
447
448 void free_find_result(output_find)
449 find_result_t **output_find;
450 {
451     find_result_t *output_find_result, *prev;
452
453     prev=NULL;
454     for(output_find_result=*output_find;
455             output_find_result;
456             output_find_result=output_find_result->next) {
457         if(prev != NULL) amfree(prev);
458         amfree(output_find_result->hostname);
459         amfree(output_find_result->diskname);
460         amfree(output_find_result->label);
461         amfree(output_find_result->status);
462         prev = output_find_result;
463     }
464     if(prev != NULL) amfree(prev);
465     output_find = NULL;
466 }
467
468 int find_match(host, disk)
469 char *host, *disk;
470 {
471     disk_t *dp = lookup_disk(host,disk);
472     return (dp && dp->todo);
473 }
474
475 char *find_nicedate(datestamp)
476 int datestamp;
477 {
478     static char nice[20];
479     int year, month, day;
480
481     year  = datestamp / 10000;
482     month = (datestamp / 100) % 100;
483     day   = datestamp % 100;
484
485     ap_snprintf(nice, sizeof(nice), "%4d-%02d-%02d", year, month, day);
486
487     return nice;
488 }
489
490 static int parse_taper_datestamp_log(logline, datestamp, label)
491 char *logline;
492 int *datestamp;
493 char **label;
494 {
495     char *s;
496     int ch;
497
498     s = logline;
499     ch = *s++;
500
501     skip_whitespace(s, ch);
502     if(ch == '\0') {
503         return 0;
504     }
505 #define sc "datestamp"
506     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
507         return 0;
508     }
509     s += sizeof(sc)-1;
510     ch = s[-1];
511 #undef sc
512
513     skip_whitespace(s, ch);
514     if(ch == '\0' || sscanf(s - 1, "%d", datestamp) != 1) {
515         return 0;
516     }
517     skip_integer(s, ch);
518
519     skip_whitespace(s, ch);
520     if(ch == '\0') {
521         return 0;
522     }
523 #define sc "label"
524     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
525         return 0;
526     }
527     s += sizeof(sc)-1;
528     ch = s[-1];
529 #undef sc
530
531     skip_whitespace(s, ch);
532     if(ch == '\0') {
533         return 0;
534     }
535     *label = s - 1;
536     skip_non_whitespace(s, ch);
537     s[-1] = '\0';
538
539     return 1;
540 }
541
542 /* if output_find is NULL                                       */
543 /*      return 1 if this is the logfile for this label          */
544 /*      return 0 if this is not the logfile for this label      */
545 /* else                                                         */
546 /*      add to output_find all the dump for this label          */
547 /*      return the number of dump added.                        */
548 int search_logfile(output_find, label, datestamp, datestamp_aux, logfile)
549 find_result_t **output_find;
550 char *label, *logfile;
551 int datestamp, datestamp_aux;
552 {
553     FILE *logf;
554     char *host, *host_undo;
555     char *disk, *disk_undo;
556     int   datestampI;
557     char *rest;
558     char *ck_label;
559     int level, filenum, ck_datestamp, tapematch;
560     int passlabel, ck_datestamp2;
561     char *s;
562     int ch;
563     disk_t *dp;
564
565     if((logf = fopen(logfile, "r")) == NULL)
566         error("could not open logfile %s: %s", logfile, strerror(errno));
567
568     /* check that this log file corresponds to the right tape */
569     tapematch = 0;
570     while(!tapematch && get_logline(logf)) {
571         if(curlog == L_START && curprog == P_TAPER) {
572             if(parse_taper_datestamp_log(curstr,
573                                          &ck_datestamp, &ck_label) == 0) {
574                 printf("strange log line \"start taper %s\"\n", curstr);
575             } else if(ck_datestamp == datestamp
576                       && strcmp(ck_label, label) == 0) {
577                 tapematch = 1;
578             }
579         }
580     }
581
582     if(output_find == NULL) {
583         afclose(logf);
584         if(tapematch == 0)
585             return 0;
586         else
587             return 1;
588     }
589
590     if(tapematch == 0) {
591         afclose(logf);
592         return 0;
593     }
594
595     filenum = 0;
596     passlabel = 1;
597     while(get_logline(logf) && passlabel) {
598         if(curlog == L_SUCCESS && curprog == P_TAPER && passlabel) filenum++;
599         if(curlog == L_START && curprog == P_TAPER) {
600             if(parse_taper_datestamp_log(curstr,
601                                          &ck_datestamp2, &ck_label) == 0) {
602                 printf("strange log line \"start taper %s\"\n", curstr);
603             } else if (strcmp(ck_label, label)) {
604                 passlabel = !passlabel;
605             }
606         }
607         if(curlog == L_SUCCESS || curlog == L_FAIL) {
608             s = curstr;
609             ch = *s++;
610
611             skip_whitespace(s, ch);
612             if(ch == '\0') {
613                 printf("strange log line \"%s\"\n", curstr);
614                 continue;
615             }
616             host = s - 1;
617             skip_non_whitespace(s, ch);
618             host_undo = s - 1;
619             *host_undo = '\0';
620
621             skip_whitespace(s, ch);
622             if(ch == '\0') {
623                 printf("strange log line \"%s\"\n", curstr);
624                 continue;
625             }
626             disk = s - 1;
627             skip_non_whitespace(s, ch);
628             disk_undo = s - 1;
629             *disk_undo = '\0';
630
631             skip_whitespace(s, ch);
632             if(ch == '\0' || sscanf(s - 1, "%d", &datestampI) != 1) {
633                 printf("strange log line \"%s\"\n", curstr);
634                 continue;
635             }
636             skip_integer(s, ch);
637
638             if(datestampI < 100)  { /* old log didn't have datestamp */
639                 level = datestampI;
640                 datestampI = datestamp;
641             }
642             else {
643                 skip_whitespace(s, ch);
644                 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
645                     printf("strange log line \"%s\"\n", curstr);
646                     continue;
647                 }
648                 skip_integer(s, ch);
649             }
650
651             skip_whitespace(s, ch);
652             if(ch == '\0') {
653                 printf("strange log line \"%s\"\n", curstr);
654                 continue;
655             }
656             rest = s - 1;
657             if((s = strchr(s, '\n')) != NULL) {
658                 *s = '\0';
659             }
660
661             dp = lookup_disk(host,disk);
662             if ( dp == NULL ) {
663                 if (dynamic_disklist == 0) {
664                     continue;
665                 }
666                 dp = add_disk(host, disk);
667                 enqueue_disk(find_diskqp , dp);
668             }
669             if(find_match(host, disk)) {
670                 if(curprog == P_TAPER) {
671                     find_result_t *new_output_find =
672                         (find_result_t *)alloc(sizeof(find_result_t));
673                     new_output_find->next=*output_find;
674                     new_output_find->datestamp=datestampI;
675                     new_output_find->timestamp = alloc(15);
676                     ap_snprintf(new_output_find->timestamp, 15, "%d000000", datestampI);
677                     new_output_find->datestamp_aux=datestamp_aux;
678                     new_output_find->hostname=stralloc(host);
679                     new_output_find->diskname=stralloc(disk);
680                     new_output_find->level=level;
681                     new_output_find->label=stralloc(label);
682                     new_output_find->filenum=filenum;
683                     if(curlog == L_SUCCESS) 
684                         new_output_find->status=stralloc("OK");
685                     else
686                         new_output_find->status=stralloc(rest);
687                     *output_find=new_output_find;
688                 }
689                 else if(curlog == L_FAIL) {     /* print other failures too */
690                     find_result_t *new_output_find =
691                         (find_result_t *)alloc(sizeof(find_result_t));
692                     new_output_find->next=*output_find;
693                     new_output_find->datestamp=datestamp;
694                     new_output_find->datestamp_aux=datestamp_aux;
695                     new_output_find->timestamp = alloc(15);
696                     ap_snprintf(new_output_find->timestamp, 15, "%d000000", datestamp);
697                     new_output_find->hostname=stralloc(host);
698                     new_output_find->diskname=stralloc(disk);
699                     new_output_find->level=level;
700                     new_output_find->label=stralloc("---");
701                     new_output_find->filenum=0;
702                     new_output_find->status=vstralloc(
703                          "FAILED (",
704                          program_str[(int)curprog],
705                          ") ",
706                          rest,
707                          NULL);
708                     *output_find=new_output_find;
709                 }
710             }
711         }
712     }
713     afclose(logf);
714     return 1;
715 }
716
717 find_result_t *dump_exist(output_find, hostname, diskname, datestamp, level)
718 find_result_t *output_find;
719 char *hostname;
720 char *diskname;
721 int datestamp;
722 int level;
723 {
724     find_result_t *output_find_result;
725
726     for(output_find_result=output_find;
727         output_find_result;
728         output_find_result=output_find_result->next) {
729         if( !strcmp(output_find_result->hostname, hostname) &&
730             !strcmp(output_find_result->diskname, diskname) &&
731             output_find_result->datestamp == datestamp &&
732             output_find_result->level == level) {
733
734             return output_find_result;
735         }
736     }
737     return(NULL);
738 }