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.23 2006/01/15 21:01:00 martinea Exp $
30 * controlling process for the Amanda backup system
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 void strip_failed_chunks P((find_result_t *output_find));
44 char *find_nicedate P((int datestamp));
45 static int find_compare P((const void *, const void *));
46 static int parse_taper_datestamp_log P((char *, int *, char **));
48 static char *find_sort_order = NULL;
49 int dynamic_disklist = 0;
50 disklist_t* find_diskqp = NULL;
52 find_result_t *find_dump(dyna_disklist, diskqp)
56 char *conf_logdir, *logfile = NULL;
57 int tape, maxtape, seq, logs;
59 find_result_t *output_find = NULL;
61 dynamic_disklist = dyna_disklist;
63 conf_logdir = getconf_str(CNF_LOGDIR);
64 if (*conf_logdir == '/') {
65 conf_logdir = stralloc(conf_logdir);
67 conf_logdir = stralloc2(config_dir, conf_logdir);
69 maxtape = lookup_nb_tape();
71 for(tape = 1; tape <= maxtape; tape++) {
72 char ds_str[NUM_STR_SIZE];
74 tp = lookup_tapepos(tape);
75 if(tp == NULL) continue;
76 snprintf(ds_str, sizeof(ds_str), "%d", tp->datestamp);
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), "%d", seq);
88 logfile = newvstralloc(logfile,
89 conf_logdir, "/log.", ds_str, ".", seq_str, NULL);
90 if(access(logfile, R_OK) != 0) break;
91 logs += search_logfile(&output_find, tp->label, tp->datestamp, seq, logfile);
94 /* search old-style amflush log, if any */
96 logfile = newvstralloc(logfile,
97 conf_logdir, "/log.", ds_str, ".amflush", NULL);
98 if(access(logfile,R_OK) == 0) {
99 logs += search_logfile(&output_find, tp->label, tp->datestamp, 1000, logfile);
102 /* search old-style main log, if any */
104 logfile = newvstralloc(logfile, conf_logdir, "/log.", ds_str, NULL);
105 if(access(logfile,R_OK) == 0) {
106 logs += search_logfile(&output_find, tp->label, tp->datestamp, -1, logfile);
108 if(logs == 0 && tp->datestamp != 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);
124 char *conf_logdir, *logfile = NULL;
125 int tape, maxtape, seq, logs;
127 char **output_find_log = NULL;
130 conf_logdir = getconf_str(CNF_LOGDIR);
131 if (*conf_logdir == '/') {
132 conf_logdir = stralloc(conf_logdir);
134 conf_logdir = stralloc2(config_dir, conf_logdir);
136 maxtape = lookup_nb_tape();
138 output_find_log = alloc((maxtape*5+10) * sizeof(char *));
139 current_log = output_find_log;
141 for(tape = 1; tape <= maxtape; tape++) {
142 char ds_str[NUM_STR_SIZE];
144 tp = lookup_tapepos(tape);
145 if(tp == NULL) continue;
146 snprintf(ds_str, sizeof(ds_str), "%d", tp->datestamp);
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), "%d", seq);
158 logfile = newvstralloc(logfile,
159 conf_logdir, "/log.", ds_str, ".", seq_str, NULL);
160 if(access(logfile, R_OK) != 0) break;
161 if( search_logfile(NULL, tp->label, tp->datestamp, seq, logfile)) {
162 *current_log = vstralloc("log.", ds_str, ".", seq_str, NULL);
169 /* search old-style amflush log, if any */
171 logfile = newvstralloc(logfile,
172 conf_logdir, "/log.", ds_str, ".amflush", NULL);
173 if(access(logfile,R_OK) == 0) {
174 if( search_logfile(NULL, tp->label, tp->datestamp, 1000, logfile)) {
175 *current_log = vstralloc("log.", ds_str, ".amflush", NULL);
181 /* search old-style main log, if any */
183 logfile = newvstralloc(logfile, conf_logdir, "/log.", ds_str, NULL);
184 if(access(logfile,R_OK) == 0) {
185 if(search_logfile(NULL, tp->label, tp->datestamp, -1, logfile)) {
186 *current_log = vstralloc("log.", ds_str, NULL);
191 if(logs == 0 && tp->datestamp != 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(output_find)
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) continue;
213 if(strcmp(cur->status, "OK")){
214 failed = alloc(sizeof(find_result_t));
215 memcpy(failed, cur, sizeof(find_result_t));
216 failed->next = failures;
217 failed->diskname = stralloc(cur->diskname);
218 failed->hostname = stralloc(cur->hostname);
223 /* Now if a CHUNK matches the parameters of a failed dump, remove it */
224 for(failed=failures; failed; failed=failed->next) {
226 for(cur=output_find; cur; cur=cur->next) {
227 if(!cur->hostname || !cur->diskname ||
228 !strcmp(cur->partnum, "--") || strcmp(cur->status, "OK")){
233 if(!strcmp(cur->hostname, failed->hostname) &&
234 !strcmp(cur->diskname, failed->diskname) &&
235 cur->datestamp == failed->datestamp &&
236 cur->datestamp_aux == failed->datestamp_aux &&
237 cur->level == failed->level){
238 find_result_t *next = cur->next;
239 amfree(cur->diskname);
240 amfree(cur->hostname);
247 else output_find = next;
253 for(failed=failures; failed;) {
254 find_result_t *fai = failed->next;
255 amfree(failed->diskname);
256 amfree(failed->hostname);
263 void search_holding_disk(output_find)
264 find_result_t **output_find;
266 holdingdisk_t *hdisk;
269 char *sdirname = NULL;
270 char *destname = NULL;
271 char *hostname = NULL;
272 char *diskname = NULL;
274 struct dirent *entry;
278 holding_list = pick_all_datestamp(1);
280 for(hdisk = getconf_holdingdisks(); hdisk != NULL; hdisk = hdisk->next) {
281 for(dir = holding_list->first; dir != NULL; dir = dir->next) {
282 sdirname = newvstralloc(sdirname,
283 hdisk->diskdir, "/", dir->name,
285 if((workdir = opendir(sdirname)) == NULL) {
289 while((entry = readdir(workdir)) != NULL) {
290 if(is_dot_or_dotdot(entry->d_name)) {
293 destname = newvstralloc(destname,
294 sdirname, "/", entry->d_name,
296 if(is_emptyfile(destname)) {
301 if(get_amanda_names(destname, &hostname, &diskname, &level) != F_DUMPFILE) {
304 if(level < 0 || level > 9)
310 if((dp = lookup_disk(hostname, diskname)))
312 if((s = strrchr(hostname,'.')) == NULL)
320 if(find_match(hostname,diskname)) {
321 find_result_t *new_output_find =
322 alloc(sizeof(find_result_t));
323 new_output_find->next=*output_find;
324 if(strlen(dir->name) == 8) {
325 new_output_find->datestamp=atoi(dir->name);
326 new_output_find->timestamp=stralloc2(dir->name, "000000");
328 else if(strlen(dir->name) == 14) {
329 char *name = stralloc(dir->name);
331 new_output_find->datestamp=atoi(name);
332 new_output_find->timestamp=stralloc(dir->name);
338 new_output_find->datestamp_aux=1001;
339 new_output_find->hostname=hostname;
341 new_output_find->diskname=diskname;
343 new_output_find->level=level;
344 new_output_find->label=stralloc(destname);
345 new_output_find->partnum=stralloc("--");
346 new_output_find->filenum=0;
347 new_output_find->status=stralloc("OK");
348 *output_find=new_output_find;
354 free_sl(holding_list);
362 static int find_compare(i1, j1)
367 find_result_t **i = (find_result_t **)i1;
368 find_result_t **j = (find_result_t **)j1;
370 int nb_compare=strlen(find_sort_order);
373 for(k=0;k<nb_compare;k++) {
374 switch (find_sort_order[k]) {
375 case 'h' : compare=strcmp((*i)->hostname,(*j)->hostname);
377 case 'H' : compare=strcmp((*j)->hostname,(*i)->hostname);
379 case 'k' : compare=strcmp((*i)->diskname,(*j)->diskname);
381 case 'K' : compare=strcmp((*j)->diskname,(*i)->diskname);
383 case 'd' : compare=(*i)->datestamp - (*j)->datestamp;
385 compare = (*i)->datestamp_aux - (*j)->datestamp_aux;
387 case 'D' : compare=(*j)->datestamp - (*i)->datestamp;
389 compare = (*j)->datestamp_aux - (*i)->datestamp_aux;
391 case 'l' : compare=(*j)->level - (*i)->level;
393 case 'f' : compare=(*i)->filenum - (*j)->filenum;
395 case 'F' : compare=(*j)->filenum - (*i)->filenum;
397 case 'L' : compare=(*i)->level - (*j)->level;
399 case 'b' : compare=strcmp((*i)->label,(*j)->label);
401 case 'B' : compare=strcmp((*j)->label,(*i)->label);
404 if(strcmp((*i)->partnum, "--") != 0 &&
405 strcmp((*j)->partnum, "--") != 0){
406 compare = atoi((*i)->partnum) - atoi((*j)->partnum);
408 else compare=strcmp((*i)->partnum,(*j)->partnum);
411 if(strcmp((*i)->partnum, "--") != 0 &&
412 strcmp((*j)->partnum, "--") != 0){
413 compare = atoi((*j)->partnum) - atoi((*i)->partnum);
415 else compare=strcmp((*j)->partnum,(*i)->partnum);
424 void sort_find_result(sort_order, output_find)
426 find_result_t **output_find;
428 find_result_t *output_find_result;
429 find_result_t **array_find_result = NULL;
433 find_sort_order = sort_order;
434 /* qsort core dump if nothing to sort */
435 if(*output_find==NULL)
438 /* How many result */
439 for(output_find_result=*output_find;
441 output_find_result=output_find_result->next) {
445 /* put the list in an array */
446 array_find_result=alloc(nb_result * sizeof(find_result_t *));
447 for(output_find_result=*output_find,no_result=0;
449 output_find_result=output_find_result->next,no_result++) {
450 array_find_result[no_result]=output_find_result;
454 qsort(array_find_result,nb_result,sizeof(find_result_t *),
457 /* put the sorted result in the list */
459 no_result<nb_result-1; no_result++) {
460 array_find_result[no_result]->next = array_find_result[no_result+1];
462 array_find_result[nb_result-1]->next=NULL;
463 *output_find=array_find_result[0];
464 amfree(array_find_result);
467 void print_find_result(output_find)
468 find_result_t *output_find;
470 find_result_t *output_find_result;
471 int max_len_datestamp = 4;
472 int max_len_hostname = 4;
473 int max_len_diskname = 4;
474 int max_len_level = 2;
475 int max_len_label =12;
476 int max_len_filenum = 4;
477 int max_len_part = 4;
478 int max_len_status = 6;
481 for(output_find_result=output_find;
483 output_find_result=output_find_result->next) {
485 len=strlen(find_nicedate(output_find_result->datestamp));
486 if(len>max_len_datestamp) max_len_datestamp=len;
488 len=strlen(output_find_result->hostname);
489 if(len>max_len_hostname) max_len_hostname=len;
491 len=strlen(output_find_result->diskname);
492 if(len>max_len_diskname) max_len_diskname=len;
494 len=strlen(output_find_result->label);
495 if(len>max_len_label) max_len_label=len;
497 len=strlen(output_find_result->status);
498 if(len>max_len_status) max_len_status=len;
500 len=strlen(output_find_result->partnum);
501 if(len>max_len_part) max_len_part=len;
505 * Since status is the rightmost field, we zap the maximum length
506 * because it is not needed. The code is left in place in case
507 * another column is added later.
511 if(output_find==NULL) {
512 printf("\nNo dump to list\n");
515 printf("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n",
516 max_len_datestamp-4,"",
517 max_len_hostname-4 ,"",
518 max_len_diskname-4 ,"",
520 max_len_label-12 ,"",
521 max_len_filenum-4 ,"",
523 for(output_find_result=output_find;
525 output_find_result=output_find_result->next) {
527 printf("%-*s %-*s %-*s %*d %-*s %*d %*s %-*s\n",
529 find_nicedate(output_find_result->datestamp),
530 max_len_hostname, output_find_result->hostname,
531 max_len_diskname, output_find_result->diskname,
532 max_len_level, output_find_result->level,
533 max_len_label, output_find_result->label,
534 max_len_filenum, output_find_result->filenum,
535 max_len_part, output_find_result->partnum,
536 max_len_status, output_find_result->status
542 void free_find_result(output_find)
543 find_result_t **output_find;
545 find_result_t *output_find_result, *prev;
548 for(output_find_result=*output_find;
550 output_find_result=output_find_result->next) {
551 if(prev != NULL) amfree(prev);
552 amfree(output_find_result->hostname);
553 amfree(output_find_result->diskname);
554 amfree(output_find_result->label);
555 amfree(output_find_result->partnum);
556 amfree(output_find_result->status);
557 amfree(output_find_result->timestamp);
558 prev = output_find_result;
560 if(prev != NULL) amfree(prev);
564 int find_match(host, disk)
567 disk_t *dp = lookup_disk(host,disk);
568 return (dp && dp->todo);
571 char *find_nicedate(datestamp)
574 static char nice[20];
575 int year, month, day;
577 year = datestamp / 10000;
578 month = (datestamp / 100) % 100;
579 day = datestamp % 100;
581 snprintf(nice, sizeof(nice), "%4d-%02d-%02d", year, month, day);
586 static int parse_taper_datestamp_log(logline, datestamp, label)
597 skip_whitespace(s, ch);
601 #define sc "datestamp"
602 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
609 skip_whitespace(s, ch);
610 if(ch == '\0' || sscanf(s - 1, "%d", datestamp) != 1) {
615 skip_whitespace(s, ch);
620 if(strncmp(s - 1, sc, sizeof(sc)-1) != 0) {
627 skip_whitespace(s, ch);
632 skip_non_whitespace(s, ch);
639 * Check whether we've already seen a CHUNK log entry for the given dump.
640 * This is so we can interpret the final SUCCESS entry for a split dump as
641 * 'list its parts' instead. Return 1 if we have, 0 if not.
643 int seen_chunk_of(output_find, date, host, disk, level)
644 find_result_t *output_find;
650 if(!host || !disk) return(0);
652 for(cur=output_find; cur; cur=cur->next) {
653 if(atoi(cur->partnum) < 1 || !cur->hostname || !cur->diskname) continue;
655 if(cur->datestamp == date && strcmp(cur->hostname, host) == 0 &&
656 strcmp(cur->diskname, disk) == 0 && cur->level == level){
663 /* if output_find is NULL */
664 /* return 1 if this is the logfile for this label */
665 /* return 0 if this is not the logfile for this label */
667 /* add to output_find all the dump for this label */
668 /* return the number of dump added. */
669 int search_logfile(output_find, label, datestamp, datestamp_aux, logfile)
670 find_result_t **output_find;
671 char *label, *logfile;
672 int datestamp, datestamp_aux;
675 char *host, *host_undo;
676 char *disk, *disk_undo;
677 char *partnum=NULL, *partnum_undo;
681 int level, filenum, ck_datestamp, tapematch;
682 int passlabel, ck_datestamp2;
687 if((logf = fopen(logfile, "r")) == NULL)
688 error("could not open logfile %s: %s", logfile, strerror(errno));
690 /* check that this log file corresponds to the right tape */
692 while(!tapematch && get_logline(logf)) {
693 if(curlog == L_START && curprog == P_TAPER) {
694 if(parse_taper_datestamp_log(curstr,
695 &ck_datestamp, &ck_label) == 0) {
696 printf("strange log line \"start taper %s\"\n", curstr);
697 } else if(ck_datestamp == datestamp
698 && strcmp(ck_label, label) == 0) {
704 if(output_find == NULL) {
719 while(get_logline(logf) && passlabel) {
720 if((curlog == L_SUCCESS || curlog == L_CHUNK) &&
721 curprog == P_TAPER && passlabel){
724 if(curlog == L_START && curprog == P_TAPER) {
725 if(parse_taper_datestamp_log(curstr,
726 &ck_datestamp2, &ck_label) == 0) {
727 printf("strange log line \"start taper %s\"\n", curstr);
728 } else if (strcmp(ck_label, label)) {
729 passlabel = !passlabel;
733 if(curlog == L_SUCCESS || curlog == L_FAIL || curlog == L_CHUNK) {
737 skip_whitespace(s, ch);
739 printf("strange log line \"%s\"\n", curstr);
743 skip_non_whitespace(s, ch);
747 skip_whitespace(s, ch);
749 printf("strange log line \"%s\"\n", curstr);
753 skip_non_whitespace(s, ch);
757 skip_whitespace(s, ch);
758 if(ch == '\0' || sscanf(s - 1, "%d", &datestampI) != 1) {
759 printf("strange log line \"%s\"\n", curstr);
764 if(datestampI < 100) { /* old log didn't have datestamp */
766 datestampI = datestamp;
769 if(curlog == L_CHUNK){
770 skip_whitespace(s, ch);
772 skip_non_whitespace(s, ch);
773 partnum_undo = s - 1;
774 *partnum_undo = '\0';
776 skip_whitespace(s, ch);
777 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
778 printf("strange log line \"%s\"\n", curstr);
784 skip_whitespace(s, ch);
786 printf("strange log line \"%s\"\n", curstr);
790 if((s = strchr(s, '\n')) != NULL) {
794 dp = lookup_disk(host,disk);
796 if (dynamic_disklist == 0) {
799 dp = add_disk(find_diskqp, host, disk);
800 enqueue_disk(find_diskqp, dp);
802 if(find_match(host, disk) && (curlog != L_SUCCESS ||
803 !seen_chunk_of(*output_find,datestampI,host,disk,level))){
804 if(curprog == P_TAPER) {
805 find_result_t *new_output_find =
806 (find_result_t *)alloc(sizeof(find_result_t));
807 new_output_find->next=*output_find;
808 new_output_find->datestamp=datestampI;
809 new_output_find->timestamp = alloc(15);
810 snprintf(new_output_find->timestamp, 15, "%d000000", datestampI);
811 new_output_find->datestamp_aux=datestamp_aux;
812 new_output_find->hostname=stralloc(host);
813 new_output_find->diskname=stralloc(disk);
814 new_output_find->level=level;
815 new_output_find->partnum = stralloc(partnum);
816 new_output_find->label=stralloc(label);
817 new_output_find->filenum=filenum;
818 if(curlog == L_SUCCESS || curlog == L_CHUNK)
819 new_output_find->status=stralloc("OK");
821 new_output_find->status=stralloc(rest);
822 *output_find=new_output_find;
824 else if(curlog == L_FAIL) { /* print other failures too */
825 find_result_t *new_output_find =
826 (find_result_t *)alloc(sizeof(find_result_t));
827 new_output_find->next=*output_find;
828 new_output_find->datestamp=datestamp;
829 new_output_find->datestamp_aux=datestamp_aux;
830 new_output_find->timestamp = alloc(15);
831 snprintf(new_output_find->timestamp, 15, "%d000000", datestamp);
832 new_output_find->hostname=stralloc(host);
833 new_output_find->diskname=stralloc(disk);
834 new_output_find->level=level;
835 new_output_find->label=stralloc(label);
836 new_output_find->partnum=stralloc(partnum);
837 new_output_find->filenum=0;
838 new_output_find->status=vstralloc(
840 program_str[(int)curprog],
844 *output_find=new_output_find;
855 * Return the set of dumps that match *all* of the given patterns (we consider
856 * an empty pattern to match .*, though). If 'ok' is true, will only match
857 * dumps with SUCCESS status.
859 find_result_t *dumps_match(output_find,hostname,diskname,datestamp,level,ok)
860 find_result_t *output_find;
867 find_result_t *cur_result;
868 find_result_t *matches = NULL;
870 for(cur_result=output_find;
872 cur_result=cur_result->next) {
873 char date_str[NUM_STR_SIZE];
874 char level_str[NUM_STR_SIZE];
875 snprintf(date_str, sizeof(date_str), "%d", cur_result->datestamp);
876 snprintf(level_str, sizeof(level_str), "%d", cur_result->level);
877 if((*hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
878 (*diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
879 (*datestamp== '\0' || match_datestamp(datestamp, date_str)) &&
880 (*level== '\0' || match_level(level, level_str)) &&
881 (!ok || !strcmp(cur_result->status, "OK"))){
883 find_result_t *curmatch = alloc(sizeof(find_result_t));
884 memcpy(curmatch, cur_result, sizeof(find_result_t));
887 curmatch->hostname = stralloc(cur_result->hostname);
888 curmatch->diskname = stralloc(cur_result->diskname);
889 curmatch->datestamp = stralloc(cur_result->datestamp);
890 curmatch->partnum = stralloc(cur_result->partnum);
891 curmatch->status = stralloc(cur_result->status);
892 curmatch->level = stralloc(cur_result->level);
894 curmatch->next = matches;
902 find_result_t *dump_exist(output_find, hostname, diskname, datestamp, level)
903 find_result_t *output_find;
909 find_result_t *output_find_result;
911 for(output_find_result=output_find;
913 output_find_result=output_find_result->next) {
914 if( !strcmp(output_find_result->hostname, hostname) &&
915 !strcmp(output_find_result->diskname, diskname) &&
916 output_find_result->datestamp == datestamp &&
917 output_find_result->level == level) {
919 return output_find_result;