Imported Upstream version 2.4.4p3
[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 2003/10/27 18:33:03 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                     new_output_find->datestamp=atoi(dir->name);
257                     new_output_find->datestamp_aux=1001;
258                     new_output_find->hostname=hostname;
259                     hostname = NULL;
260                     new_output_find->diskname=diskname;
261                     diskname = NULL;
262                     new_output_find->level=level;
263                     new_output_find->label=stralloc(destname);
264                     new_output_find->filenum=0;
265                     new_output_find->status=stralloc("OK");
266                     *output_find=new_output_find;
267                 }
268             }
269             closedir(workdir);
270         }       
271     }
272     free_sl(holding_list);
273     holding_list = NULL;
274     amfree(destname);
275     amfree(sdirname);
276     amfree(hostname);
277     amfree(diskname);
278 }
279
280 static int find_compare(i1, j1)
281 const void *i1;
282 const void *j1;
283 {
284     int compare=0;
285     find_result_t **i = (find_result_t **)i1;
286     find_result_t **j = (find_result_t **)j1;
287
288     int nb_compare=strlen(find_sort_order);
289     int k;
290
291     for(k=0;k<nb_compare;k++) {
292         switch (find_sort_order[k]) {
293         case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
294                    break;
295         case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
296                    break;
297         case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
298                    break;
299         case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
300                    break;
301         case 'd' : compare=(*i)->datestamp - (*j)->datestamp;
302                    if (compare == 0)
303                         compare = (*i)->datestamp_aux - (*j)->datestamp_aux;
304                    break;
305         case 'D' : compare=(*j)->datestamp - (*i)->datestamp;
306                    if (compare == 0)
307                         compare = (*j)->datestamp_aux - (*i)->datestamp_aux;
308                    break;
309         case 'l' : compare=(*j)->level - (*i)->level;
310                    break;
311         case 'L' : compare=(*i)->level - (*j)->level;
312                    break;
313         case 'b' : compare=strcmp((*i)->label,(*j)->label);
314                    break;
315         case 'B' : compare=strcmp((*j)->label,(*i)->label);
316                    break;
317         }
318         if(compare != 0)
319             return compare;
320     }
321     return 0;
322 }
323
324 void sort_find_result(sort_order, output_find)
325 char *sort_order;
326 find_result_t **output_find;
327 {
328     find_result_t *output_find_result;
329     find_result_t **array_find_result = NULL;
330     int nb_result=0;
331     int no_result;
332
333     find_sort_order = sort_order;
334     /* qsort core dump if nothing to sort */
335     if(*output_find==NULL)
336         return;
337
338     /* How many result */
339     for(output_find_result=*output_find;
340         output_find_result;
341         output_find_result=output_find_result->next) {
342         nb_result++;
343     }
344
345     /* put the list in an array */
346     array_find_result=alloc(nb_result * sizeof(find_result_t *));
347     for(output_find_result=*output_find,no_result=0;
348         output_find_result;
349         output_find_result=output_find_result->next,no_result++) {
350         array_find_result[no_result]=output_find_result;
351     }
352
353     /* sort the array */
354     qsort(array_find_result,nb_result,sizeof(find_result_t *),
355           find_compare);
356
357     /* put the sorted result in the list */
358     for(no_result=0;
359         no_result<nb_result-1; no_result++) {
360         array_find_result[no_result]->next = array_find_result[no_result+1];
361     }
362     array_find_result[nb_result-1]->next=NULL;
363     *output_find=array_find_result[0];
364     amfree(array_find_result);
365 }
366
367 void print_find_result(output_find)
368 find_result_t *output_find;
369 {
370     find_result_t *output_find_result;
371     int max_len_datestamp = 4;
372     int max_len_hostname  = 4;
373     int max_len_diskname  = 4;
374     int max_len_level     = 2;
375     int max_len_label     =12;
376     int max_len_filenum   = 4;
377     int max_len_status    = 6;
378     int len;
379
380     for(output_find_result=output_find;
381         output_find_result;
382         output_find_result=output_find_result->next) {
383
384         len=strlen(find_nicedate(output_find_result->datestamp));
385         if(len>max_len_datestamp) max_len_datestamp=len;
386
387         len=strlen(output_find_result->hostname);
388         if(len>max_len_hostname) max_len_hostname=len;
389
390         len=strlen(output_find_result->diskname);
391         if(len>max_len_diskname) max_len_diskname=len;
392
393         len=strlen(output_find_result->label);
394         if(len>max_len_label) max_len_label=len;
395
396         len=strlen(output_find_result->status);
397         if(len>max_len_status) max_len_status=len;
398     }
399
400     /*
401      * Since status is the rightmost field, we zap the maximum length
402      * because it is not needed.  The code is left in place in case
403      * another column is added later.
404      */
405     max_len_status = 1;
406
407     if(output_find==NULL) {
408         printf("\nNo dump to list\n");
409     }
410     else {
411         printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s status\n",
412                max_len_datestamp-4,"",
413                max_len_hostname-4 ,"",
414                max_len_diskname-4 ,"",
415                max_len_level-2    ,"",
416                max_len_label-12   ,"",
417                max_len_filenum-4  ,"");
418         for(output_find_result=output_find;
419                 output_find_result;
420                 output_find_result=output_find_result->next) {
421
422             printf("%-*s %-*s %-*s %*d %-*s %*d %-*s\n",
423                     max_len_datestamp, 
424                         find_nicedate(output_find_result->datestamp),
425                     max_len_hostname,  output_find_result->hostname,
426                     max_len_diskname,  output_find_result->diskname,
427                     max_len_level,     output_find_result->level,
428                     max_len_label,     output_find_result->label,
429                     max_len_filenum,   output_find_result->filenum,
430                     max_len_status,    output_find_result->status);
431         }
432     }
433 }
434
435 void free_find_result(output_find)
436 find_result_t **output_find;
437 {
438     find_result_t *output_find_result, *prev;
439
440     prev=NULL;
441     for(output_find_result=*output_find;
442             output_find_result;
443             output_find_result=output_find_result->next) {
444         if(prev != NULL) amfree(prev);
445         amfree(output_find_result->hostname);
446         amfree(output_find_result->diskname);
447         amfree(output_find_result->label);
448         amfree(output_find_result->status);
449         prev = output_find_result;
450     }
451     if(prev != NULL) amfree(prev);
452     output_find = NULL;
453 }
454
455 int find_match(host, disk)
456 char *host, *disk;
457 {
458     disk_t *dp = lookup_disk(host,disk);
459     return (dp && dp->todo);
460 }
461
462 char *find_nicedate(datestamp)
463 int datestamp;
464 {
465     static char nice[20];
466     int year, month, day;
467
468     year  = datestamp / 10000;
469     month = (datestamp / 100) % 100;
470     day   = datestamp % 100;
471
472     ap_snprintf(nice, sizeof(nice), "%4d-%02d-%02d", year, month, day);
473
474     return nice;
475 }
476
477 static int parse_taper_datestamp_log(logline, datestamp, label)
478 char *logline;
479 int *datestamp;
480 char **label;
481 {
482     char *s;
483     int ch;
484
485     s = logline;
486     ch = *s++;
487
488     skip_whitespace(s, ch);
489     if(ch == '\0') {
490         return 0;
491     }
492 #define sc "datestamp"
493     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
494         return 0;
495     }
496     s += sizeof(sc)-1;
497     ch = s[-1];
498 #undef sc
499
500     skip_whitespace(s, ch);
501     if(ch == '\0' || sscanf(s - 1, "%d", datestamp) != 1) {
502         return 0;
503     }
504     skip_integer(s, ch);
505
506     skip_whitespace(s, ch);
507     if(ch == '\0') {
508         return 0;
509     }
510 #define sc "label"
511     if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
512         return 0;
513     }
514     s += sizeof(sc)-1;
515     ch = s[-1];
516 #undef sc
517
518     skip_whitespace(s, ch);
519     if(ch == '\0') {
520         return 0;
521     }
522     *label = s - 1;
523     skip_non_whitespace(s, ch);
524     s[-1] = '\0';
525
526     return 1;
527 }
528
529 /* if output_find is NULL                                       */
530 /*      return 1 if this is the logfile for this label          */
531 /*      return 0 if this is not the logfile for this label      */
532 /* else                                                         */
533 /*      add to output_find all the dump for this label          */
534 /*      return the number of dump added.                        */
535 int search_logfile(output_find, label, datestamp, datestamp_aux, logfile)
536 find_result_t **output_find;
537 char *label, *logfile;
538 int datestamp, datestamp_aux;
539 {
540     FILE *logf;
541     char *host, *host_undo;
542     char *disk, *disk_undo;
543     int   datestampI;
544     char *rest;
545     char *ck_label;
546     int level, filenum, ck_datestamp, tapematch;
547     int passlabel, ck_datestamp2;
548     char *s;
549     int ch;
550     disk_t *dp;
551
552     if((logf = fopen(logfile, "r")) == NULL)
553         error("could not open logfile %s: %s", logfile, strerror(errno));
554
555     /* check that this log file corresponds to the right tape */
556     tapematch = 0;
557     while(!tapematch && get_logline(logf)) {
558         if(curlog == L_START && curprog == P_TAPER) {
559             if(parse_taper_datestamp_log(curstr,
560                                          &ck_datestamp, &ck_label) == 0) {
561                 printf("strange log line \"start taper %s\"\n", curstr);
562             } else if(ck_datestamp == datestamp
563                       && strcmp(ck_label, label) == 0) {
564                 tapematch = 1;
565             }
566         }
567     }
568
569     if(output_find == NULL) {
570         afclose(logf);
571         if(tapematch == 0)
572             return 0;
573         else
574             return 1;
575     }
576
577     if(tapematch == 0) {
578         afclose(logf);
579         return 0;
580     }
581
582     filenum = 0;
583     passlabel = 1;
584     while(get_logline(logf) && passlabel) {
585         if(curlog == L_SUCCESS && curprog == P_TAPER && passlabel) filenum++;
586         if(curlog == L_START && curprog == P_TAPER) {
587             if(parse_taper_datestamp_log(curstr,
588                                          &ck_datestamp2, &ck_label) == 0) {
589                 printf("strange log line \"start taper %s\"\n", curstr);
590             } else if (strcmp(ck_label, label)) {
591                 passlabel = !passlabel;
592             }
593         }
594         if(curlog == L_SUCCESS || curlog == L_FAIL) {
595             s = curstr;
596             ch = *s++;
597
598             skip_whitespace(s, ch);
599             if(ch == '\0') {
600                 printf("strange log line \"%s\"\n", curstr);
601                 continue;
602             }
603             host = s - 1;
604             skip_non_whitespace(s, ch);
605             host_undo = s - 1;
606             *host_undo = '\0';
607
608             skip_whitespace(s, ch);
609             if(ch == '\0') {
610                 printf("strange log line \"%s\"\n", curstr);
611                 continue;
612             }
613             disk = s - 1;
614             skip_non_whitespace(s, ch);
615             disk_undo = s - 1;
616             *disk_undo = '\0';
617
618             skip_whitespace(s, ch);
619             if(ch == '\0' || sscanf(s - 1, "%d", &datestampI) != 1) {
620                 printf("strange log line \"%s\"\n", curstr);
621                 continue;
622             }
623             skip_integer(s, ch);
624
625             if(datestampI < 100)  { /* old log didn't have datestamp */
626                 level = datestampI;
627                 datestampI = datestamp;
628             }
629             else {
630                 skip_whitespace(s, ch);
631                 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
632                     printf("strange log line \"%s\"\n", curstr);
633                     continue;
634                 }
635                 skip_integer(s, ch);
636             }
637
638             skip_whitespace(s, ch);
639             if(ch == '\0') {
640                 printf("strange log line \"%s\"\n", curstr);
641                 continue;
642             }
643             rest = s - 1;
644             if((s = strchr(s, '\n')) != NULL) {
645                 *s = '\0';
646             }
647
648             dp = lookup_disk(host,disk);
649             if ( dp == NULL ) {
650                 if (dynamic_disklist == 0) {
651                     continue;
652                 }
653                 dp = add_disk(host, disk);
654                 enqueue_disk(find_diskqp , dp);
655             }
656             if(find_match(host, disk)) {
657                 if(curprog == P_TAPER) {
658                     find_result_t *new_output_find =
659                         (find_result_t *)alloc(sizeof(find_result_t));
660                     new_output_find->next=*output_find;
661                     new_output_find->datestamp=datestampI;
662                     new_output_find->datestamp_aux=datestamp_aux;
663                     new_output_find->hostname=stralloc(host);
664                     new_output_find->diskname=stralloc(disk);
665                     new_output_find->level=level;
666                     new_output_find->label=stralloc(label);
667                     new_output_find->filenum=filenum;
668                     if(curlog == L_SUCCESS) 
669                         new_output_find->status=stralloc("OK");
670                     else
671                         new_output_find->status=stralloc(rest);
672                     *output_find=new_output_find;
673                 }
674                 else if(curlog == L_FAIL) {     /* print other failures too */
675                     find_result_t *new_output_find =
676                         (find_result_t *)alloc(sizeof(find_result_t));
677                     new_output_find->next=*output_find;
678                     new_output_find->datestamp=datestamp;
679                     new_output_find->datestamp_aux=datestamp_aux;
680                     new_output_find->hostname=stralloc(host);
681                     new_output_find->diskname=stralloc(disk);
682                     new_output_find->level=level;
683                     new_output_find->label=stralloc("---");
684                     new_output_find->filenum=0;
685                     new_output_find->status=vstralloc(
686                          "FAILED (",
687                          program_str[(int)curprog],
688                          ") ",
689                          rest,
690                          NULL);
691                     *output_find=new_output_find;
692                 }
693             }
694         }
695     }
696     afclose(logf);
697     return 1;
698 }
699
700 find_result_t *dump_exist(output_find, hostname, diskname, datestamp, level)
701 find_result_t *output_find;
702 char *hostname;
703 char *diskname;
704 int datestamp;
705 int level;
706 {
707     find_result_t *output_find_result;
708
709     for(output_find_result=output_find;
710         output_find_result;
711         output_find_result=output_find_result->next) {
712         if( !strcmp(output_find_result->hostname, hostname) &&
713             !strcmp(output_find_result->diskname, diskname) &&
714             output_find_result->datestamp == datestamp &&
715             output_find_result->level == level) {
716
717             return output_find_result;
718         }
719     }
720     return(NULL);
721 }