2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: find.c,v 1.33 2006/07/06 13:13:15 martinea Exp $
30 * controlling process for the Amanda backup system
39 int find_match(char *host, char *disk);
40 int search_logfile(find_result_t **output_find, char *label, char *datestamp, char *logfile);
41 void search_holding_disk(find_result_t **output_find);
42 void strip_failed_chunks(find_result_t **output_find);
43 char *find_nicedate(char *datestamp);
44 static int find_compare(const void *, const void *);
45 static int parse_taper_datestamp_log(char *logline, char **datestamp, char **level);
46 static int seen_chunk_of(find_result_t *output_find, char *date, char *host, char *disk, int level);
48 static char *find_sort_order = NULL;
49 int dynamic_disklist = 0;
50 disklist_t* find_diskqp = NULL;
57 char *conf_logdir, *logfile = NULL;
58 int tape, maxtape, logs;
61 find_result_t *output_find = NULL;
63 dynamic_disklist = dyna_disklist;
65 conf_logdir = getconf_str(CNF_LOGDIR);
66 if (*conf_logdir == '/') {
67 conf_logdir = stralloc(conf_logdir);
69 conf_logdir = stralloc2(config_dir, conf_logdir);
71 maxtape = lookup_nb_tape();
73 for(tape = 1; tape <= maxtape; tape++) {
75 tp = lookup_tapepos(tape);
76 if(tp == NULL) continue;
78 /* search log files */
82 /* new-style log.<date>.<seq> */
84 for(seq = 0; 1; seq++) {
85 char seq_str[NUM_STR_SIZE];
87 snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
88 logfile = newvstralloc(logfile,
89 conf_logdir, "/log.", tp->datestamp, ".", seq_str, NULL);
90 if(access(logfile, R_OK) != 0) break;
91 logs += search_logfile(&output_find, tp->label, tp->datestamp, logfile);
94 /* search old-style amflush log, if any */
96 logfile = newvstralloc(logfile,
97 conf_logdir, "/log.", tp->datestamp, ".amflush", NULL);
98 if(access(logfile,R_OK) == 0) {
99 logs += search_logfile(&output_find, tp->label, tp->datestamp, logfile);
102 /* search old-style main log, if any */
104 logfile = newvstralloc(logfile, conf_logdir, "/log.", tp->datestamp, NULL);
105 if(access(logfile,R_OK) == 0) {
106 logs += search_logfile(&output_find, tp->label, tp->datestamp, logfile);
108 if(logs == 0 && strcmp(tp->datestamp,"0") != 0)
109 fprintf(stderr, "Warning: no log files found for tape %s written %s\n",
110 tp->label, find_nicedate(tp->datestamp));
115 search_holding_disk(&output_find);
117 strip_failed_chunks(&output_find);
125 char *conf_logdir, *logfile = NULL;
126 int tape, maxtape, logs;
129 char **output_find_log = NULL;
132 conf_logdir = getconf_str(CNF_LOGDIR);
133 if (*conf_logdir == '/') {
134 conf_logdir = stralloc(conf_logdir);
136 conf_logdir = stralloc2(config_dir, conf_logdir);
138 maxtape = lookup_nb_tape();
140 output_find_log = alloc((maxtape*5+10) * SIZEOF(char *));
141 current_log = output_find_log;
143 for(tape = 1; tape <= maxtape; tape++) {
145 tp = lookup_tapepos(tape);
146 if(tp == NULL) continue;
148 /* search log files */
152 /* new-style log.<date>.<seq> */
154 for(seq = 0; 1; seq++) {
155 char seq_str[NUM_STR_SIZE];
157 snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
158 logfile = newvstralloc(logfile,
159 conf_logdir, "/log.", tp->datestamp, ".", seq_str, NULL);
160 if(access(logfile, R_OK) != 0) break;
161 if( search_logfile(NULL, tp->label, tp->datestamp, logfile)) {
162 *current_log = vstralloc("log.", tp->datestamp, ".", seq_str, NULL);
169 /* search old-style amflush log, if any */
171 logfile = newvstralloc(logfile,
172 conf_logdir, "/log.", tp->datestamp, ".amflush", NULL);
173 if(access(logfile,R_OK) == 0) {
174 if( search_logfile(NULL, tp->label, tp->datestamp, logfile)) {
175 *current_log = vstralloc("log.", tp->datestamp, ".amflush", NULL);
181 /* search old-style main log, if any */
183 logfile = newvstralloc(logfile, conf_logdir, "/log.", tp->datestamp, NULL);
184 if(access(logfile,R_OK) == 0) {
185 if(search_logfile(NULL, tp->label, tp->datestamp, logfile)) {
186 *current_log = vstralloc("log.", tp->datestamp, NULL);
191 if(logs == 0 && strcmp(tp->datestamp,"0") != 0)
192 fprintf(stderr, "Warning: no log files found for tape %s written %s\n",
193 tp->label, find_nicedate(tp->datestamp));
198 return(output_find_log);
202 * Remove CHUNK entries from dumps that ultimately failed from our report.
204 void strip_failed_chunks(
205 find_result_t **output_find)
207 find_result_t *cur, *prev = NULL, *failed = NULL, *failures = NULL;
209 /* Generate a list of failures */
210 for(cur=*output_find; cur; cur=cur->next) {
211 if(!cur->hostname || !cur->diskname ||
212 !cur->timestamp || !cur->label)
215 if(strcmp(cur->status, "OK")){
216 failed = alloc(SIZEOF(find_result_t));
217 memcpy(failed, cur, SIZEOF(find_result_t));
218 failed->next = failures;
223 /* Now if a CHUNK matches the parameters of a failed dump, remove it */
224 for(failed=failures; failed; failed=failed->next) {
227 while (cur != NULL) {
228 find_result_t *next = cur->next;
229 if(!cur->hostname || !cur->diskname ||
230 !cur->timestamp || !cur->label || !cur->partnum ||
231 !strcmp(cur->partnum, "--") || strcmp(cur->status, "OK")) {
235 else if(!strcmp(cur->hostname, failed->hostname) &&
236 !strcmp(cur->diskname, failed->diskname) &&
237 !strcmp(cur->timestamp, failed->timestamp) &&
238 !strcmp(cur->label, failed->label) &&
239 cur->level == failed->level){
240 amfree(cur->diskname);
241 amfree(cur->hostname);
243 amfree(cur->timestamp);
244 amfree(cur->partnum);
251 amfree(*output_find);
263 for(failed=failures; failed;) {
264 find_result_t *fai = failed->next;
273 find_result_t **output_find)
275 sl_t *holding_file_list;
281 holding_file_list = holding_get_files(NULL, NULL, 1);
283 for(e = holding_file_list->first; e != NULL; e = e->next) {
284 holding_file = e->name;
286 if (!holding_file_get_dumpfile(holding_file, &file))
289 if (file.dumplevel < 0 || file.dumplevel > 9)
295 if((dp = lookup_disk(file.name, file.disk)))
297 if((s = strrchr(file.name,'.')) == NULL)
305 if(find_match(file.name,file.disk)) {
306 find_result_t *new_output_find =
307 alloc(SIZEOF(find_result_t));
308 new_output_find->next=*output_find;
309 new_output_find->timestamp = stralloc(file.datestamp);
310 new_output_find->hostname = stralloc(file.name);
311 new_output_find->diskname = stralloc(file.disk);
312 new_output_find->level=file.dumplevel;
313 new_output_find->label=stralloc(holding_file);
314 new_output_find->partnum=stralloc("--");
315 new_output_find->filenum=0;
316 new_output_find->status=stralloc("OK");
317 *output_find=new_output_find;
321 free_sl(holding_file_list);
330 find_result_t **i = (find_result_t **)i1;
331 find_result_t **j = (find_result_t **)j1;
333 size_t nb_compare=strlen(find_sort_order);
336 for(k=0;k<nb_compare;k++) {
337 switch (find_sort_order[k]) {
338 case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
340 case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
342 case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
344 case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
346 case 'd' : compare=strcmp((*i)->timestamp,(*j)->timestamp);
348 case 'D' : compare=strcmp((*j)->timestamp,(*i)->timestamp);
350 case 'l' : compare=(*j)->level - (*i)->level;
352 case 'f' : compare=((*i)->filenum == (*j)->filenum) ? 0 :
353 (((*i)->filenum < (*j)->filenum) ? -1 : 1);
355 case 'F' : compare=((*j)->filenum == (*i)->filenum) ? 0 :
356 (((*j)->filenum < (*i)->filenum) ? -1 : 1);
358 case 'L' : compare=(*i)->level - (*j)->level;
360 case 'b' : compare=strcmp((*i)->label,(*j)->label);
362 case 'B' : compare=strcmp((*j)->label,(*i)->label);
365 if(strcmp((*i)->partnum, "--") != 0 &&
366 strcmp((*j)->partnum, "--") != 0){
367 compare = atoi((*i)->partnum) - atoi((*j)->partnum);
369 else compare=strcmp((*i)->partnum,(*j)->partnum);
372 if(strcmp((*i)->partnum, "--") != 0 &&
373 strcmp((*j)->partnum, "--") != 0){
374 compare = atoi((*j)->partnum) - atoi((*i)->partnum);
376 else compare=strcmp((*j)->partnum,(*i)->partnum);
388 find_result_t **output_find)
390 find_result_t *output_find_result;
391 find_result_t **array_find_result = NULL;
395 find_sort_order = sort_order;
396 /* qsort core dump if nothing to sort */
397 if(*output_find==NULL)
400 /* How many result */
401 for(output_find_result=*output_find;
403 output_find_result=output_find_result->next) {
407 /* put the list in an array */
408 array_find_result=alloc(nb_result * SIZEOF(find_result_t *));
409 for(output_find_result=*output_find,no_result=0;
411 output_find_result=output_find_result->next,no_result++) {
412 array_find_result[no_result]=output_find_result;
416 qsort(array_find_result,nb_result,SIZEOF(find_result_t *),
419 /* put the sorted result in the list */
421 no_result<nb_result-1; no_result++) {
422 array_find_result[no_result]->next = array_find_result[no_result+1];
424 array_find_result[nb_result-1]->next=NULL;
425 *output_find=array_find_result[0];
426 amfree(array_find_result);
431 find_result_t *output_find)
433 find_result_t *output_find_result;
434 int max_len_datestamp = 4;
435 int max_len_hostname = 4;
436 int max_len_diskname = 4;
437 int max_len_level = 2;
438 int max_len_label =12;
439 int max_len_filenum = 4;
440 int max_len_part = 4;
441 int max_len_status = 6;
444 for(output_find_result=output_find;
446 output_find_result=output_find_result->next) {
448 len=strlen(find_nicedate(output_find_result->timestamp));
449 if((int)len > max_len_datestamp)
450 max_len_datestamp=(int)len;
452 len=strlen(output_find_result->hostname);
453 if((int)len > max_len_hostname)
454 max_len_hostname = (int)len;
456 len=strlen(output_find_result->diskname);
457 if((int)len > max_len_diskname)
458 max_len_diskname = (int)len;
460 len=strlen(output_find_result->label);
461 if((int)len > max_len_label)
462 max_len_label = (int)len;
464 len=strlen(output_find_result->status);
465 if((int)len > max_len_status)
466 max_len_status = (int)len;
468 len=strlen(output_find_result->partnum);
469 if((int)len > max_len_part)
470 max_len_part = (int)len;
474 * Since status is the rightmost field, we zap the maximum length
475 * because it is not needed. The code is left in place in case
476 * another column is added later.
480 if(output_find==NULL) {
481 printf("\nNo dump to list\n");
484 printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n",
485 max_len_datestamp-4,"",
486 max_len_hostname-4 ,"",
487 max_len_diskname-4 ,"",
489 max_len_label-12 ,"",
490 max_len_filenum-4 ,"",
492 for(output_find_result=output_find;
494 output_find_result=output_find_result->next) {
497 qdiskname = quote_string(output_find_result->diskname);
499 printf("%-*s %-*s %-*s %*d %-*s %*" OFF_T_RFMT " %*s %-*s\n",
501 find_nicedate(output_find_result->timestamp),
502 max_len_hostname, output_find_result->hostname,
503 max_len_diskname, qdiskname,
504 max_len_level, output_find_result->level,
505 max_len_label, output_find_result->label,
506 max_len_filenum, (OFF_T_FMT_TYPE)output_find_result->filenum,
507 max_len_part, output_find_result->partnum,
508 max_len_status, output_find_result->status
518 find_result_t **output_find)
520 find_result_t *output_find_result, *prev;
523 for(output_find_result=*output_find;
525 output_find_result=output_find_result->next) {
527 amfree(output_find_result->timestamp);
528 amfree(output_find_result->hostname);
529 amfree(output_find_result->diskname);
530 amfree(output_find_result->label);
531 amfree(output_find_result->partnum);
532 amfree(output_find_result->status);
533 amfree(output_find_result->timestamp);
534 prev = output_find_result;
545 disk_t *dp = lookup_disk(host,disk);
546 return (dp && dp->todo);
553 static char nice[20];
554 int year, month, day;
555 int hours, minutes, seconds;
556 char date[9], atime[7];
557 int numdate, numtime;
559 strncpy(date, datestamp, 8);
561 numdate = atoi(date);
562 year = numdate / 10000;
563 month = (numdate / 100) % 100;
566 if(strlen(datestamp) <= 8) {
567 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
571 strncpy(atime, &(datestamp[8]), 6);
573 numtime = atoi(atime);
574 hours = numtime / 10000;
575 minutes = (numtime / 100) % 100;
576 seconds = numtime % 100;
578 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d %02d:%02d:%02d",
579 year, month, day, hours, minutes, seconds);
586 parse_taper_datestamp_log(
597 skip_whitespace(s, ch);
601 if(strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
605 skip_whitespace(s, ch);
610 skip_non_whitespace(s, ch);
613 skip_whitespace(s, ch);
617 if(strncmp_const_skip(s - 1, "label", s, ch) != 0) {
621 skip_whitespace(s, ch);
626 skip_non_whitespace(s, ch);
633 * Check whether we've already seen a CHUNK log entry for the given dump.
634 * This is so we can interpret the final SUCCESS entry for a split dump as
635 * 'list its parts' instead. Return 1 if we have, 0 if not.
639 find_result_t *output_find,
647 if(!host || !disk) return(0);
649 for(cur=output_find; cur; cur=cur->next) {
650 if(atoi(cur->partnum) < 1 || !cur->hostname || !cur->diskname) continue;
652 if(strcmp(cur->timestamp, date) == 0 && strcmp(cur->hostname, host) == 0 &&
653 strcmp(cur->diskname, disk) == 0 && cur->level == level){
660 /* if output_find is NULL */
661 /* return 1 if this is the logfile for this label */
662 /* return 0 if this is not the logfile for this label */
664 /* add to output_find all the dump for this label */
665 /* return the number of dump added. */
668 find_result_t **output_find,
674 char *host, *host_undo;
675 char *disk, *qdisk, *disk_undo;
676 char *date, *date_undo;
677 char *partnum=NULL, *partnum_undo;
684 char *ck_datestamp, *ck_datestamp2;
689 if((logf = fopen(logfile, "r")) == NULL) {
690 error("could not open logfile %s: %s", logfile, strerror(errno));
694 /* check that this log file corresponds to the right tape */
696 while(!tapematch && get_logline(logf)) {
697 if(curlog == L_START && curprog == P_TAPER) {
698 if(parse_taper_datestamp_log(curstr,
699 &ck_datestamp, &ck_label) == 0) {
700 printf("strange log line \"start taper %s\" curstr='%s'\n",
702 } else if(strcmp(ck_datestamp, datestamp) == 0
703 && strcmp(ck_label, label) == 0) {
709 if(output_find == NULL) {
724 while(get_logline(logf) && passlabel) {
725 if((curlog == L_SUCCESS || curlog == L_CHUNK || curlog == L_PARTIAL) &&
726 curprog == P_TAPER && passlabel){
729 if(curlog == L_START && curprog == P_TAPER) {
730 if(parse_taper_datestamp_log(curstr,
731 &ck_datestamp2, &ck_label) == 0) {
732 printf("strange log line in %s \"start taper %s\"\n",
734 } else if (strcmp(ck_label, label)) {
735 passlabel = !passlabel;
739 if(curlog == L_SUCCESS || curlog == L_PARTIAL || curlog == L_FAIL || curlog == L_CHUNK) {
743 skip_whitespace(s, ch);
745 printf("strange log line in %s \"%s\"\n",
750 skip_non_whitespace(s, ch);
754 skip_whitespace(s, ch);
756 printf("strange log line in %s \"%s\"\n",
761 skip_quoted_string(s, ch);
764 disk = unquote_string(qdisk);
766 skip_whitespace(s, ch);
768 printf("strange log line in %s \"%s\"\n",
773 skip_non_whitespace(s, ch);
777 if(strlen(date) < 3) { /* old log didn't have datestamp */
779 date = stralloc(datestamp);
782 if(curlog == L_CHUNK){
783 skip_whitespace(s, ch);
785 skip_non_whitespace(s, ch);
786 partnum_undo = s - 1;
787 *partnum_undo = '\0';
789 skip_whitespace(s, ch);
790 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
791 printf("strange log line in %s \"%s\"\n",
798 skip_whitespace(s, ch);
800 printf("strange log line in %s \"%s\"\n",
805 if((s = strchr(s, '\n')) != NULL) {
809 dp = lookup_disk(host,disk);
811 if (dynamic_disklist == 0) {
814 dp = add_disk(find_diskqp, host, disk);
815 enqueue_disk(find_diskqp, dp);
817 if(find_match(host, disk) && (curlog != L_SUCCESS ||
818 !seen_chunk_of(*output_find, date, host, disk, level))) {
819 if(curprog == P_TAPER) {
820 find_result_t *new_output_find =
821 (find_result_t *)alloc(SIZEOF(find_result_t));
822 new_output_find->next=*output_find;
823 new_output_find->timestamp = stralloc(date);
824 new_output_find->hostname=stralloc(host);
825 new_output_find->diskname=stralloc(disk);
826 new_output_find->level=level;
827 new_output_find->partnum = stralloc(partnum);
828 new_output_find->label=stralloc(label);
829 new_output_find->filenum=filenum;
830 if(curlog == L_SUCCESS || curlog == L_CHUNK)
831 new_output_find->status=stralloc("OK");
832 else if(curlog == L_PARTIAL)
833 new_output_find->status=stralloc("PARTIAL");
835 new_output_find->status=stralloc(rest);
836 *output_find=new_output_find;
838 else if(curlog == L_FAIL) { /* print other failures too */
839 find_result_t *new_output_find =
840 (find_result_t *)alloc(SIZEOF(find_result_t));
841 new_output_find->next=*output_find;
842 new_output_find->timestamp = stralloc(date);
843 new_output_find->hostname=stralloc(host);
844 new_output_find->diskname=stralloc(disk);
845 new_output_find->level=level;
846 new_output_find->label=stralloc(label);
847 new_output_find->partnum=stralloc(partnum);
848 new_output_find->filenum=0;
849 new_output_find->status=vstralloc(
851 program_str[(int)curprog],
855 *output_find=new_output_find;
867 * Return the set of dumps that match *all* of the given patterns (we consider
868 * an empty pattern to match .*, though). If 'ok' is true, will only match
869 * dumps with SUCCESS status.
873 find_result_t *output_find,
880 find_result_t *cur_result;
881 find_result_t *matches = NULL;
883 for(cur_result=output_find;
885 cur_result=cur_result->next) {
886 char level_str[NUM_STR_SIZE];
887 snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
888 if((*hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
889 (*diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
890 (*datestamp== '\0' || match_datestamp(datestamp, cur_result->timestamp)) &&
891 (*level== '\0' || match_level(level, level_str)) &&
892 (!ok || !strcmp(cur_result->status, "OK"))){
894 find_result_t *curmatch = alloc(SIZEOF(find_result_t));
895 memcpy(curmatch, cur_result, SIZEOF(find_result_t));
898 curmatch->hostname = stralloc(cur_result->hostname);
899 curmatch->diskname = stralloc(cur_result->diskname);
900 curmatch->datestamp = stralloc(cur_result->datestamp);
901 curmatch->partnum = stralloc(cur_result->partnum);
902 curmatch->status = stralloc(cur_result->status);
903 curmatch->level = stralloc(cur_result->level);
905 curmatch->next = matches;
915 find_result_t *output_find,
921 find_result_t *output_find_result;
923 for(output_find_result=output_find;
925 output_find_result=output_find_result->next) {
926 if( !strcmp(output_find_result->hostname, hostname) &&
927 !strcmp(output_find_result->diskname, diskname) &&
928 !strcmp(output_find_result->timestamp, datestamp) &&
929 output_find_result->level == level) {
931 return output_find_result;