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 printf("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 printf("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 holdingdisk_t *hdisk;
278 char *sdirname = NULL;
279 char *destname = NULL;
280 char *hostname = NULL;
281 char *diskname = NULL;
283 struct dirent *entry;
288 char buf[DISK_BLOCK_BYTES];
291 holding_list = pick_all_datestamp(1);
293 for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next) {
294 for(dir = holding_list->first; dir != NULL; dir = dir->next) {
295 sdirname = newvstralloc(sdirname,
296 holdingdisk_get_diskdir(hdisk), "/", dir->name,
298 if((workdir = opendir(sdirname)) == NULL) {
302 while((entry = readdir(workdir)) != NULL) {
303 if(is_dot_or_dotdot(entry->d_name)) {
306 destname = newvstralloc(destname,
307 sdirname, "/", entry->d_name,
309 if(is_emptyfile(destname)) {
314 if(get_amanda_names(destname, &hostname, &diskname, &level) != F_DUMPFILE) {
317 if(level < 0 || level > 9)
319 if ((fd = open(destname, O_RDONLY)) == -1) {
322 if((result = read(fd, &buf, DISK_BLOCK_BYTES)) <= 0) {
327 parse_file_header(buf, &file, (size_t)result);
328 if (strcmp(file.name, hostname) != 0 ||
329 strcmp(file.disk, diskname) != 0 ||
330 file.dumplevel != level ||
331 !match_datestamp(file.datestamp, dir->name)) {
338 if((dp = lookup_disk(hostname, diskname)))
340 if((s = strrchr(hostname,'.')) == NULL)
348 if(find_match(hostname,diskname)) {
349 find_result_t *new_output_find =
350 alloc(SIZEOF(find_result_t));
351 new_output_find->next=*output_find;
352 new_output_find->timestamp=stralloc(file.datestamp);
353 new_output_find->hostname=hostname;
355 new_output_find->diskname=diskname;
357 new_output_find->level=level;
358 new_output_find->label=stralloc(destname);
359 new_output_find->partnum=stralloc("--");
360 new_output_find->filenum=0;
361 new_output_find->status=stralloc("OK");
362 *output_find=new_output_find;
368 free_sl(holding_list);
382 find_result_t **i = (find_result_t **)i1;
383 find_result_t **j = (find_result_t **)j1;
385 size_t nb_compare=strlen(find_sort_order);
388 for(k=0;k<nb_compare;k++) {
389 switch (find_sort_order[k]) {
390 case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
392 case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
394 case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
396 case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
398 case 'd' : compare=strcmp((*i)->timestamp,(*j)->timestamp);
400 case 'D' : compare=strcmp((*j)->timestamp,(*i)->timestamp);
402 case 'l' : compare=(*j)->level - (*i)->level;
404 case 'f' : compare=((*i)->filenum == (*j)->filenum) ? 0 :
405 (((*i)->filenum < (*j)->filenum) ? -1 : 1);
407 case 'F' : compare=((*j)->filenum == (*i)->filenum) ? 0 :
408 (((*j)->filenum < (*i)->filenum) ? -1 : 1);
410 case 'L' : compare=(*i)->level - (*j)->level;
412 case 'b' : compare=strcmp((*i)->label,(*j)->label);
414 case 'B' : compare=strcmp((*j)->label,(*i)->label);
417 if(strcmp((*i)->partnum, "--") != 0 &&
418 strcmp((*j)->partnum, "--") != 0){
419 compare = atoi((*i)->partnum) - atoi((*j)->partnum);
421 else compare=strcmp((*i)->partnum,(*j)->partnum);
424 if(strcmp((*i)->partnum, "--") != 0 &&
425 strcmp((*j)->partnum, "--") != 0){
426 compare = atoi((*j)->partnum) - atoi((*i)->partnum);
428 else compare=strcmp((*j)->partnum,(*i)->partnum);
440 find_result_t **output_find)
442 find_result_t *output_find_result;
443 find_result_t **array_find_result = NULL;
447 find_sort_order = sort_order;
448 /* qsort core dump if nothing to sort */
449 if(*output_find==NULL)
452 /* How many result */
453 for(output_find_result=*output_find;
455 output_find_result=output_find_result->next) {
459 /* put the list in an array */
460 array_find_result=alloc(nb_result * SIZEOF(find_result_t *));
461 for(output_find_result=*output_find,no_result=0;
463 output_find_result=output_find_result->next,no_result++) {
464 array_find_result[no_result]=output_find_result;
468 qsort(array_find_result,nb_result,SIZEOF(find_result_t *),
471 /* put the sorted result in the list */
473 no_result<nb_result-1; no_result++) {
474 array_find_result[no_result]->next = array_find_result[no_result+1];
476 array_find_result[nb_result-1]->next=NULL;
477 *output_find=array_find_result[0];
478 amfree(array_find_result);
483 find_result_t *output_find)
485 find_result_t *output_find_result;
486 int max_len_datestamp = 4;
487 int max_len_hostname = 4;
488 int max_len_diskname = 4;
489 int max_len_level = 2;
490 int max_len_label =12;
491 int max_len_filenum = 4;
492 int max_len_part = 4;
493 int max_len_status = 6;
496 for(output_find_result=output_find;
498 output_find_result=output_find_result->next) {
500 len=strlen(find_nicedate(output_find_result->timestamp));
501 if((int)len > max_len_datestamp)
502 max_len_datestamp=(int)len;
504 len=strlen(output_find_result->hostname);
505 if((int)len > max_len_hostname)
506 max_len_hostname = (int)len;
508 len=strlen(output_find_result->diskname);
509 if((int)len > max_len_diskname)
510 max_len_diskname = (int)len;
512 len=strlen(output_find_result->label);
513 if((int)len > max_len_label)
514 max_len_label = (int)len;
516 len=strlen(output_find_result->status);
517 if((int)len > max_len_status)
518 max_len_status = (int)len;
520 len=strlen(output_find_result->partnum);
521 if((int)len > max_len_part)
522 max_len_part = (int)len;
526 * Since status is the rightmost field, we zap the maximum length
527 * because it is not needed. The code is left in place in case
528 * another column is added later.
532 if(output_find==NULL) {
533 printf("\nNo dump to list\n");
536 printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n",
537 max_len_datestamp-4,"",
538 max_len_hostname-4 ,"",
539 max_len_diskname-4 ,"",
541 max_len_label-12 ,"",
542 max_len_filenum-4 ,"",
544 for(output_find_result=output_find;
546 output_find_result=output_find_result->next) {
549 qdiskname = quote_string(output_find_result->diskname);
551 printf("%-*s %-*s %-*s %*d %-*s %*" OFF_T_RFMT " %*s %-*s\n",
553 find_nicedate(output_find_result->timestamp),
554 max_len_hostname, output_find_result->hostname,
555 max_len_diskname, qdiskname,
556 max_len_level, output_find_result->level,
557 max_len_label, output_find_result->label,
558 max_len_filenum, (OFF_T_FMT_TYPE)output_find_result->filenum,
559 max_len_part, output_find_result->partnum,
560 max_len_status, output_find_result->status
570 find_result_t **output_find)
572 find_result_t *output_find_result, *prev;
575 for(output_find_result=*output_find;
577 output_find_result=output_find_result->next) {
579 amfree(output_find_result->timestamp);
580 amfree(output_find_result->hostname);
581 amfree(output_find_result->diskname);
582 amfree(output_find_result->label);
583 amfree(output_find_result->partnum);
584 amfree(output_find_result->status);
585 amfree(output_find_result->timestamp);
586 prev = output_find_result;
597 disk_t *dp = lookup_disk(host,disk);
598 return (dp && dp->todo);
605 static char nice[20];
606 int year, month, day;
607 int hours, minutes, seconds;
608 char date[9], atime[7];
609 int numdate, numtime;
611 strncpy(date, datestamp, 8);
613 numdate = atoi(date);
614 year = numdate / 10000;
615 month = (numdate / 100) % 100;
618 if(strlen(datestamp) <= 8) {
619 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
623 strncpy(atime, &(datestamp[8]), 6);
625 numtime = atoi(atime);
626 hours = numtime / 10000;
627 minutes = (numtime / 100) % 100;
628 seconds = numtime % 100;
630 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d %02d:%02d:%02d",
631 year, month, day, hours, minutes, seconds);
638 parse_taper_datestamp_log(
649 skip_whitespace(s, ch);
653 #define sc "datestamp"
654 if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
661 skip_whitespace(s, ch);
666 skip_non_whitespace(s, ch);
669 skip_whitespace(s, ch);
674 if(strncmp(s - 1, sc, SIZEOF(sc)-1) != 0) {
681 skip_whitespace(s, ch);
686 skip_non_whitespace(s, ch);
693 * Check whether we've already seen a CHUNK log entry for the given dump.
694 * This is so we can interpret the final SUCCESS entry for a split dump as
695 * 'list its parts' instead. Return 1 if we have, 0 if not.
699 find_result_t *output_find,
707 if(!host || !disk) return(0);
709 for(cur=output_find; cur; cur=cur->next) {
710 if(atoi(cur->partnum) < 1 || !cur->hostname || !cur->diskname) continue;
712 if(strcmp(cur->timestamp, date) == 0 && strcmp(cur->hostname, host) == 0 &&
713 strcmp(cur->diskname, disk) == 0 && cur->level == level){
720 /* if output_find is NULL */
721 /* return 1 if this is the logfile for this label */
722 /* return 0 if this is not the logfile for this label */
724 /* add to output_find all the dump for this label */
725 /* return the number of dump added. */
728 find_result_t **output_find,
734 char *host, *host_undo;
735 char *disk, *qdisk, *disk_undo;
736 char *date, *date_undo;
737 char *partnum=NULL, *partnum_undo;
744 char *ck_datestamp, *ck_datestamp2;
749 if((logf = fopen(logfile, "r")) == NULL) {
750 error("could not open logfile %s: %s", logfile, strerror(errno));
754 /* check that this log file corresponds to the right tape */
756 while(!tapematch && get_logline(logf)) {
757 if(curlog == L_START && curprog == P_TAPER) {
758 if(parse_taper_datestamp_log(curstr,
759 &ck_datestamp, &ck_label) == 0) {
760 printf("strange log line \"start taper %s\" curstr='%s'\n",
762 } else if(strcmp(ck_datestamp, datestamp) == 0
763 && strcmp(ck_label, label) == 0) {
769 if(output_find == NULL) {
784 while(get_logline(logf) && passlabel) {
785 if((curlog == L_SUCCESS || curlog == L_CHUNK) &&
786 curprog == P_TAPER && passlabel){
789 if(curlog == L_START && curprog == P_TAPER) {
790 if(parse_taper_datestamp_log(curstr,
791 &ck_datestamp2, &ck_label) == 0) {
792 printf("strange log line in %s \"start taper %s\"\n",
794 } else if (strcmp(ck_label, label)) {
795 passlabel = !passlabel;
799 if(curlog == L_SUCCESS || curlog == L_FAIL || curlog == L_CHUNK) {
803 skip_whitespace(s, ch);
805 printf("strange log line in %s \"%s\"\n",
810 skip_non_whitespace(s, ch);
814 skip_whitespace(s, ch);
816 printf("strange log line in %s \"%s\"\n",
821 skip_quoted_string(s, ch);
824 disk = unquote_string(qdisk);
826 skip_whitespace(s, ch);
828 printf("strange log line in %s \"%s\"\n",
833 skip_non_whitespace(s, ch);
837 if(strlen(date) < 3) { /* old log didn't have datestamp */
839 date = stralloc(datestamp);
842 if(curlog == L_CHUNK){
843 skip_whitespace(s, ch);
845 skip_non_whitespace(s, ch);
846 partnum_undo = s - 1;
847 *partnum_undo = '\0';
849 skip_whitespace(s, ch);
850 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
851 printf("strange log line in %s \"%s\"\n",
858 skip_whitespace(s, ch);
860 printf("strange log line in %s \"%s\"\n",
865 if((s = strchr(s, '\n')) != NULL) {
869 dp = lookup_disk(host,disk);
871 if (dynamic_disklist == 0) {
874 dp = add_disk(find_diskqp, host, disk);
875 enqueue_disk(find_diskqp, dp);
877 if(find_match(host, disk) && (curlog != L_SUCCESS ||
878 !seen_chunk_of(*output_find, date, host, disk, level))) {
879 if(curprog == P_TAPER) {
880 find_result_t *new_output_find =
881 (find_result_t *)alloc(SIZEOF(find_result_t));
882 new_output_find->next=*output_find;
883 new_output_find->timestamp = stralloc(date);
884 new_output_find->hostname=stralloc(host);
885 new_output_find->diskname=stralloc(disk);
886 new_output_find->level=level;
887 new_output_find->partnum = stralloc(partnum);
888 new_output_find->label=stralloc(label);
889 new_output_find->filenum=filenum;
890 if(curlog == L_SUCCESS || curlog == L_CHUNK)
891 new_output_find->status=stralloc("OK");
893 new_output_find->status=stralloc(rest);
894 *output_find=new_output_find;
896 else if(curlog == L_FAIL) { /* print other failures too */
897 find_result_t *new_output_find =
898 (find_result_t *)alloc(SIZEOF(find_result_t));
899 new_output_find->next=*output_find;
900 new_output_find->timestamp = stralloc(date);
901 new_output_find->hostname=stralloc(host);
902 new_output_find->diskname=stralloc(disk);
903 new_output_find->level=level;
904 new_output_find->label=stralloc(label);
905 new_output_find->partnum=stralloc(partnum);
906 new_output_find->filenum=0;
907 new_output_find->status=vstralloc(
909 program_str[(int)curprog],
913 *output_find=new_output_find;
925 * Return the set of dumps that match *all* of the given patterns (we consider
926 * an empty pattern to match .*, though). If 'ok' is true, will only match
927 * dumps with SUCCESS status.
931 find_result_t *output_find,
938 find_result_t *cur_result;
939 find_result_t *matches = NULL;
941 for(cur_result=output_find;
943 cur_result=cur_result->next) {
944 char level_str[NUM_STR_SIZE];
945 snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
946 if((*hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
947 (*diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
948 (*datestamp== '\0' || match_datestamp(datestamp, cur_result->timestamp)) &&
949 (*level== '\0' || match_level(level, level_str)) &&
950 (!ok || !strcmp(cur_result->status, "OK"))){
952 find_result_t *curmatch = alloc(SIZEOF(find_result_t));
953 memcpy(curmatch, cur_result, SIZEOF(find_result_t));
956 curmatch->hostname = stralloc(cur_result->hostname);
957 curmatch->diskname = stralloc(cur_result->diskname);
958 curmatch->datestamp = stralloc(cur_result->datestamp);
959 curmatch->partnum = stralloc(cur_result->partnum);
960 curmatch->status = stralloc(cur_result->status);
961 curmatch->level = stralloc(cur_result->level);
963 curmatch->next = matches;
973 find_result_t *output_find,
979 find_result_t *output_find_result;
981 for(output_find_result=output_find;
983 output_find_result=output_find_result->next) {
984 if( !strcmp(output_find_result->hostname, hostname) &&
985 !strcmp(output_find_result->diskname, diskname) &&
986 !strcmp(output_find_result->timestamp, datestamp) &&
987 output_find_result->level == level) {
989 return output_find_result;