1 /*
2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
4 * Copyright (c) 2007-2013 Zmanda, Inc. All Rights Reserved.
5 * All Rights Reserved.
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of U.M. not be used in advertising or
12 * publicity pertaining to distribution of the software without specific,
13 * written prior permission. U.M. makes no representations about the
14 * suitability of this software for any purpose. It is provided "as is"
15 * without express or implied warranty.
16 *
17 * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19 * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23 *
24 * Author: James da Silva, Systems Design and Analysis Group
25 * Computer Science Department
26 * University of Maryland at College Park
27 */
28 /*
29 * $Id: find.c,v 1.33 2006/07/06 13:13:15 martinea Exp $
30 *
31 * controlling process for the Amanda backup system
32 */
33 #include "amanda.h"
34 #include "match.h"
35 #include "conffile.h"
36 #include "tapefile.h"
37 #include "logfile.h"
38 #include "holding.h"
39 #include "find.h"
40 #include <regex.h>
41 #include "cmdline.h"
42
43 int find_match(char *host, char *disk);
44 static char *find_nicedate(char *datestamp);
45 static int len_find_nicedate(char *datestamp);
46 static int find_compare(const void *, const void *);
47 static int parse_taper_datestamp_log(char *logline, char **datestamp, char **level);
48 static gboolean logfile_has_tape(char * label, char * datestamp,
49 char * logfile);
50
51 static char *find_sort_order = NULL;
52 static GStringChunk *string_chunk = NULL;
53
find_dump(disklist_t * diskqp)54 find_result_t * find_dump(disklist_t* diskqp) {
55 char *conf_logdir, *logfile = NULL;
56 int tape, maxtape, logs;
57 unsigned seq;
58 tape_t *tp;
59 find_result_t *output_find = NULL;
60 GHashTable *tape_seen = g_hash_table_new(g_str_hash, g_str_equal);
61
62 if (string_chunk == NULL) {
63 string_chunk = g_string_chunk_new(32768);
64 }
65 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
66 maxtape = lookup_nb_tape();
67
68 for(tape = 1; tape <= maxtape; tape++) {
69
70 tp = lookup_tapepos(tape);
71 if(tp == NULL) continue;
72
73 /* Do not search the log file if we already searched that datestamp */
74 if (g_hash_table_lookup(tape_seen, tp->datestamp)) {
75 continue;
76 }
77 g_hash_table_insert(tape_seen, tp->datestamp, GINT_TO_POINTER(1));
78
79
80 /* search log files */
81
82 logs = 0;
83
84 /* new-style log.<date>.<seq> */
85
86 for(seq = 0; 1; seq++) {
87 char seq_str[NUM_STR_SIZE];
88
89 g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
90 logfile = newvstralloc(logfile,
91 conf_logdir, "/log.", tp->datestamp, ".", seq_str, NULL);
92 if(access(logfile, R_OK) != 0) break;
93 if (search_logfile(&output_find, NULL, tp->datestamp,
94 logfile, diskqp)) {
95 logs ++;
96 }
97 }
98
99 /* search old-style amflush log, if any */
100
101 logfile = newvstralloc(logfile, conf_logdir, "/log.",
102 tp->datestamp, ".amflush", NULL);
103 if(access(logfile,R_OK) == 0) {
104 if (search_logfile(&output_find, NULL, tp->datestamp,
105 logfile, diskqp)) {
106 logs ++;
107 }
108 }
109
110 /* search old-style main log, if any */
111
112 logfile = newvstralloc(logfile, conf_logdir, "/log.", tp->datestamp,
113 NULL);
114 if(access(logfile,R_OK) == 0) {
115 if (search_logfile(&output_find, NULL, tp->datestamp,
116 logfile, diskqp)) {
117 logs ++;
118 }
119 }
120 }
121 g_hash_table_destroy(tape_seen);
122 amfree(logfile);
123 amfree(conf_logdir);
124
125 search_holding_disk(&output_find, diskqp);
126
127 return(output_find);
128 }
129
130 char **
find_log(void)131 find_log(void)
132 {
133 char *conf_logdir, *logfile = NULL;
134 char *pathlogfile = NULL;
135 int tape, maxtape, logs;
136 unsigned seq;
137 tape_t *tp;
138 char **output_find_log = NULL;
139 char **current_log;
140
141 conf_logdir = config_dir_relative(getconf_str(CNF_LOGDIR));
142 maxtape = lookup_nb_tape();
143
144 output_find_log = alloc((maxtape*5+10) * SIZEOF(char *));
145 current_log = output_find_log;
146
147 for(tape = 1; tape <= maxtape; tape++) {
148
149 tp = lookup_tapepos(tape);
150 if(tp == NULL) continue;
151
152 /* search log files */
153
154 logs = 0;
155
156 /* new-style log.<date>.<seq> */
157
158 for(seq = 0; 1; seq++) {
159 char seq_str[NUM_STR_SIZE];
160
161 g_snprintf(seq_str, SIZEOF(seq_str), "%u", seq);
162 logfile = newvstralloc(logfile, "log.", tp->datestamp, ".", seq_str, NULL);
163 pathlogfile = newvstralloc(pathlogfile, conf_logdir, "/", logfile, NULL);
164 if (access(pathlogfile, R_OK) != 0) break;
165 if (logfile_has_tape(tp->label, tp->datestamp, pathlogfile)) {
166 if (current_log == output_find_log || strcmp(*(current_log-1), logfile)) {
167 *current_log = stralloc(logfile);
168 current_log++;
169 }
170 logs++;
171 break;
172 }
173 }
174
175 /* search old-style amflush log, if any */
176
177 logfile = newvstralloc(logfile, "log.", tp->datestamp, ".amflush", NULL);
178 pathlogfile = newvstralloc(pathlogfile, conf_logdir, "/", logfile, NULL);
179 if (access(pathlogfile, R_OK) == 0) {
180 if (logfile_has_tape(tp->label, tp->datestamp, pathlogfile)) {
181 if (current_log == output_find_log || strcmp(*(current_log-1), logfile)) {
182 *current_log = stralloc(logfile);
183 current_log++;
184 }
185 logs++;
186 }
187 }
188
189 /* search old-style main log, if any */
190
191 logfile = newvstralloc(logfile, "log.", tp->datestamp, NULL);
192 pathlogfile = newvstralloc(pathlogfile, conf_logdir, "/", logfile, NULL);
193 if (access(pathlogfile, R_OK) == 0) {
194 if (logfile_has_tape(tp->label, tp->datestamp, pathlogfile)) {
195 if (current_log == output_find_log || strcmp(*(current_log-1), logfile)) {
196 *current_log = stralloc(logfile);
197 current_log++;
198 }
199 logs++;
200 }
201 }
202
203 if(logs == 0 && strcmp(tp->datestamp,"0") != 0)
204 g_fprintf(stderr, _("Warning: no log files found for tape %s written %s\n"),
205 tp->label, find_nicedate(tp->datestamp));
206 }
207 amfree(logfile);
208 amfree(pathlogfile);
209 amfree(conf_logdir);
210 *current_log = NULL;
211 return(output_find_log);
212 }
213
214 void
search_holding_disk(find_result_t ** output_find,disklist_t * dynamic_disklist)215 search_holding_disk(
216 find_result_t **output_find,
217 disklist_t * dynamic_disklist)
218 {
219 GSList *holding_file_list;
220 GSList *e;
221 char *holding_file;
222 disk_t *dp;
223 char *orig_name;
224
225 holding_file_list = holding_get_files(NULL, 1);
226
227 if (string_chunk == NULL) {
228 string_chunk = g_string_chunk_new(32768);
229 }
230
231 for(e = holding_file_list; e != NULL; e = e->next) {
232 dumpfile_t file;
233
234 holding_file = (char *)e->data;
235
236 if (!holding_file_get_dumpfile(holding_file, &file))
237 continue;
238
239 if (file.dumplevel < 0 || file.dumplevel >= DUMP_LEVELS) {
240 dumpfile_free_data(&file);
241 continue;
242 }
243
244 dp = NULL;
245 orig_name = g_strdup(file.name);
246 for(;;) {
247 char *s;
248 if((dp = lookup_disk(file.name, file.disk)))
249 break;
250 if((s = strrchr(file.name,'.')) == NULL)
251 break;
252 *s = '\0';
253 }
254 strcpy(file.name, orig_name); /* restore munged string */
255 g_free(orig_name);
256
257 if ( dp == NULL ) {
258 if (dynamic_disklist == NULL) {
259 dumpfile_free_data(&file);
260 continue;
261 }
262 dp = add_disk(dynamic_disklist, file.name, file.disk);
263 }
264
265 if(find_match(file.name,file.disk)) {
266 find_result_t *new_output_find = g_new0(find_result_t, 1);
267 new_output_find->next=*output_find;
268 new_output_find->timestamp = g_string_chunk_insert_const(string_chunk, file.datestamp);
269 new_output_find->write_timestamp = g_string_chunk_insert_const(string_chunk, "00000000000000");
270 new_output_find->hostname = g_string_chunk_insert_const(string_chunk, file.name);
271 new_output_find->diskname = g_string_chunk_insert_const(string_chunk, file.disk);
272 new_output_find->level=file.dumplevel;
273 new_output_find->label=g_string_chunk_insert_const(string_chunk, holding_file);
274 new_output_find->partnum = -1;
275 new_output_find->totalparts = -1;
276 new_output_find->filenum=0;
277 if (file.is_partial) {
278 new_output_find->status="PARTIAL";
279 new_output_find->dump_status="PARTIAL";
280 } else {
281 new_output_find->status="OK";
282 new_output_find->dump_status="OK";
283 }
284 new_output_find->message="";
285 new_output_find->kb = holding_file_size(holding_file, 1);
286 new_output_find->bytes = 0;
287
288 new_output_find->orig_kb = file.orig_size;
289
290 *output_find=new_output_find;
291 }
292 dumpfile_free_data(&file);
293 }
294
295 slist_free_full(holding_file_list, g_free);
296 }
297
298 static int
find_compare(const void * i1,const void * j1)299 find_compare(
300 const void *i1,
301 const void *j1)
302 {
303 int compare=0;
304 find_result_t *i, *j;
305
306 size_t nb_compare=strlen(find_sort_order);
307 size_t k;
308
309 for(k=0;k<nb_compare;k++) {
310 char sort_key = find_sort_order[k];
311 if (isupper((int)sort_key)) {
312 /* swap */
313 sort_key = tolower(sort_key);
314 j = *(find_result_t **)i1;
315 i = *(find_result_t **)j1;
316 } else {
317 i = *(find_result_t **)i1;
318 j = *(find_result_t **)j1;
319 }
320
321 switch (sort_key) {
322 case 'h' : compare=strcmp(i->hostname,j->hostname);
323 break;
324 case 'k' : compare=strcmp(i->diskname,j->diskname);
325 break;
326 case 'd' : compare=strcmp(i->timestamp,j->timestamp);
327 break;
328 case 'l' : compare=j->level - i->level;
329 break;
330 case 'f' : compare=(i->filenum == j->filenum) ? 0 :
331 ((i->filenum < j->filenum) ? -1 : 1);
332 break;
333 case 'b' : compare=compare_possibly_null_strings(i->label,
334 j->label);
335 break;
336 case 'w': compare=strcmp(i->write_timestamp, j->write_timestamp);
337 break;
338 case 'p' :
339 compare=i->partnum - j->partnum;
340 break;
341 }
342 if(compare != 0)
343 return compare;
344 }
345 return 0;
346 }
347
348 void
sort_find_result(char * sort_order,find_result_t ** output_find)349 sort_find_result(
350 char *sort_order,
351 find_result_t **output_find)
352 {
353 find_result_t *output_find_result;
354 find_result_t **array_find_result = NULL;
355 size_t nb_result=0;
356 size_t no_result;
357
358 find_sort_order = sort_order;
359 /* qsort core dump if nothing to sort */
360 if(*output_find==NULL)
361 return;
362
363 /* How many result */
364 for(output_find_result=*output_find;
365 output_find_result;
366 output_find_result=output_find_result->next) {
367 nb_result++;
368 }
369
370 /* put the list in an array */
371 array_find_result=alloc(nb_result * SIZEOF(find_result_t *));
372 for(output_find_result=*output_find,no_result=0;
373 output_find_result;
374 output_find_result=output_find_result->next,no_result++) {
375 array_find_result[no_result]=output_find_result;
376 }
377
378 /* sort the array */
379 qsort(array_find_result,nb_result,SIZEOF(find_result_t *),
380 find_compare);
381
382 /* put the sorted result in the list */
383 for(no_result=0;
384 no_result<nb_result-1; no_result++) {
385 array_find_result[no_result]->next = array_find_result[no_result+1];
386 }
387 array_find_result[nb_result-1]->next=NULL;
388 *output_find=array_find_result[0];
389 amfree(array_find_result);
390 }
391
392 void
print_find_result(find_result_t * output_find)393 print_find_result(
394 find_result_t *output_find)
395 {
396 find_result_t *output_find_result;
397 int max_len_datestamp = 4;
398 int max_len_hostname = 4;
399 int max_len_diskname = 4;
400 int max_len_level = 2;
401 int max_len_label =12;
402 int max_len_filenum = 4;
403 int max_len_part = 4;
404 int max_len_status = 6;
405 size_t len;
406
407 for(output_find_result=output_find;
408 output_find_result;
409 output_find_result=output_find_result->next) {
410 char *s;
411
412 len=len_find_nicedate(output_find_result->timestamp);
413 if((int)len > max_len_datestamp)
414 max_len_datestamp=(int)len;
415
416 len=strlen(output_find_result->hostname);
417 if((int)len > max_len_hostname)
418 max_len_hostname = (int)len;
419
420 len = len_quote_string(output_find_result->diskname);
421 if((int)len > max_len_diskname)
422 max_len_diskname = (int)len;
423
424 if (output_find_result->label != NULL) {
425 len = len_quote_string(output_find_result->label);
426 if((int)len > max_len_label)
427 max_len_label = (int)len;
428 }
429
430 len=strlen(output_find_result->status) + 1 + strlen(output_find_result->dump_status);
431 if((int)len > max_len_status)
432 max_len_status = (int)len;
433
434 s = g_strdup_printf("%d/%d", output_find_result->partnum,
435 output_find_result->totalparts);
436 len=strlen(s);
437 if((int)len > max_len_part)
438 max_len_part = (int)len;
439 amfree(s);
440 }
441
442 /*
443 * Since status is the rightmost field, we zap the maximum length
444 * because it is not needed. The code is left in place in case
445 * another column is added later.
446 */
447 max_len_status = 1;
448
449 if(output_find==NULL) {
450 g_printf(_("\nNo dump to list\n"));
451 }
452 else {
453 g_printf(_("\ndate%*s host%*s disk%*s lv%*s tape or file%*s file%*s part%*s status\n"),
454 max_len_datestamp-4,"",
455 max_len_hostname-4 ,"",
456 max_len_diskname-4 ,"",
457 max_len_level-2 ,"",
458 max_len_label-12 ,"",
459 max_len_filenum-4 ,"",
460 max_len_part-4 ,"");
461 for(output_find_result=output_find;
462 output_find_result;
463 output_find_result=output_find_result->next) {
464 char *qdiskname;
465 char * formatted_label;
466 char *s;
467 char *status;
468
469 qdiskname = quote_string(output_find_result->diskname);
470 if (output_find_result->label == NULL)
471 formatted_label = stralloc("");
472 else
473 formatted_label = quote_string(output_find_result->label);
474
475 if (strcmp(output_find_result->status, "OK") != 0 ||
476 strcmp(output_find_result->dump_status, "OK") != 0) {
477 status = vstralloc(output_find_result->status, " ",
478 output_find_result->dump_status, NULL);
479 } else {
480 status = stralloc(output_find_result->status);
481 }
482
483 /*@ignore@*/
484 /* sec and kb are omitted here, for compatibility with the existing
485 * output from 'amadmin' */
486 s = g_strdup_printf("%d/%d", output_find_result->partnum,
487 output_find_result->totalparts);
488 g_printf("%-*s %-*s %-*s %*d %-*s %*lld %*s %s %s\n",
489 max_len_datestamp,
490 find_nicedate(output_find_result->timestamp),
491 max_len_hostname, output_find_result->hostname,
492 max_len_diskname, qdiskname,
493 max_len_level, output_find_result->level,
494 max_len_label, formatted_label,
495 max_len_filenum, (long long)output_find_result->filenum,
496 max_len_part, s,
497 status,
498 output_find_result->message
499 );
500 amfree(status);
501 amfree(s);
502 /*@end@*/
503 amfree(qdiskname);
504 amfree(formatted_label);
505 }
506 }
507 }
508
509 void
free_find_result(find_result_t ** output_find)510 free_find_result(
511 find_result_t **output_find)
512 {
513 find_result_t *output_find_result, *prev;
514
515 prev=NULL;
516 for(output_find_result=*output_find;
517 output_find_result;
518 output_find_result=output_find_result->next) {
519 amfree(prev);
520 prev = output_find_result;
521 }
522 amfree(prev);
523 *output_find = NULL;
524 }
525
526 int
find_match(char * host,char * disk)527 find_match(
528 char *host,
529 char *disk)
530 {
531 disk_t *dp = lookup_disk(host,disk);
532 return (dp && dp->todo);
533 }
534
535 static char *
find_nicedate(char * datestamp)536 find_nicedate(
537 char *datestamp)
538 {
539 static char nice[20];
540 int year, month, day;
541 int hours, minutes, seconds;
542 char date[9], atime[7];
543 int numdate, numtime;
544
545 strncpy(date, datestamp, 8);
546 date[8] = '\0';
547 numdate = atoi(date);
548 year = numdate / 10000;
549 month = (numdate / 100) % 100;
550 day = numdate % 100;
551
552 if(strlen(datestamp) <= 8) {
553 g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
554 year, month, day);
555 }
556 else {
557 strncpy(atime, &(datestamp[8]), 6);
558 atime[6] = '\0';
559 numtime = atoi(atime);
560 hours = numtime / 10000;
561 minutes = (numtime / 100) % 100;
562 seconds = numtime % 100;
563
564 g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d %02d:%02d:%02d",
565 year, month, day, hours, minutes, seconds);
566 }
567
568 return nice;
569 }
570
571 static int
len_find_nicedate(char * datestamp)572 len_find_nicedate(
573 char *datestamp)
574 {
575 if(strlen(datestamp) <= 8) {
576 return 10;
577 } else {
578 return 19;
579 }
580 }
581
582 static int
parse_taper_datestamp_log(char * logline,char ** datestamp,char ** label)583 parse_taper_datestamp_log(
584 char *logline,
585 char **datestamp,
586 char **label)
587 {
588 char *s;
589 int ch;
590
591 s = logline;
592 ch = *s++;
593
594 skip_whitespace(s, ch);
595 if(ch == '\0') {
596 return 0;
597 }
598 if(strncmp_const_skip(s - 1, "datestamp", s, ch) != 0) {
599 return 0;
600 }
601
602 skip_whitespace(s, ch);
603 if(ch == '\0') {
604 return 0;
605 }
606 *datestamp = s - 1;
607 skip_non_whitespace(s, ch);
608 s[-1] = '\0';
609
610 skip_whitespace(s, ch);
611 if(ch == '\0') {
612 return 0;
613 }
614 if(strncmp_const_skip(s - 1, "label", s, ch) != 0) {
615 return 0;
616 }
617
618 skip_whitespace(s, ch);
619 if(ch == '\0') {
620 return 0;
621 }
622 *label = s - 1;
623 skip_quoted_string(s, ch);
624 s[-1] = '\0';
625
626 *label = unquote_string(*label);
627 return 1;
628 }
629
630 /* Returns TRUE if the given logfile mentions the given tape. */
logfile_has_tape(char * label,char * datestamp,char * logfile)631 static gboolean logfile_has_tape(char * label, char * datestamp,
632 char * logfile) {
633 FILE * logf;
634 char * ck_datestamp, *ck_label = NULL;
635 if((logf = fopen(logfile, "r")) == NULL) {
636 error(_("could not open logfile %s: %s"), logfile, strerror(errno));
637 /*NOTREACHED*/
638 }
639
640 while(get_logline(logf)) {
641 if(curlog == L_START && curprog == P_TAPER) {
642 if(parse_taper_datestamp_log(curstr,
643 &ck_datestamp, &ck_label) == 0) {
644 g_printf(_("strange log line \"start taper %s\" curstr='%s'\n"),
645 logfile, curstr);
646 } else if(strcmp(ck_datestamp, datestamp) == 0
647 && strcmp(ck_label, label) == 0) {
648 amfree(ck_label);
649 afclose(logf);
650 return TRUE;
651 }
652 amfree(ck_label);
653 }
654 }
655
656 afclose(logf);
657 return FALSE;
658 }
659
660 static gboolean
volume_matches(const char * label1,const char * label2,const char * datestamp)661 volume_matches(
662 const char *label1,
663 const char *label2,
664 const char *datestamp)
665 {
666 tape_t *tp;
667
668 if (!label2)
669 return TRUE;
670
671 if (label1)
672 return (strcmp(label1, label2) == 0);
673
674 /* check in tapelist */
675 if (!(tp = lookup_tapelabel(label2)))
676 return FALSE;
677
678 if (strcmp(tp->datestamp, datestamp) != 0)
679 return FALSE;
680
681 return TRUE;
682 }
683
684 /* WARNING: Function accesses globals find_diskqp, curlog, curlog, curstr,
685 * dynamic_disklist */
686 gboolean
search_logfile(find_result_t ** output_find,const char * label,const char * passed_datestamp,const char * logfile,disklist_t * dynamic_disklist)687 search_logfile(
688 find_result_t **output_find,
689 const char *label,
690 const char *passed_datestamp,
691 const char *logfile,
692 disklist_t * dynamic_disklist)
693 {
694 FILE *logf;
695 char *host, *host_undo;
696 char *disk, *qdisk, *disk_undo;
697 char *date, *date_undo;
698 int partnum;
699 int totalparts;
700 int maxparts = -1;
701 char *number;
702 int fileno;
703 char *current_label = stralloc("");
704 char *rest, *rest_undo;
705 char *ck_label=NULL;
706 int level = 0;
707 off_t filenum;
708 char *ck_datestamp=NULL;
709 char *datestamp;
710 char *s;
711 int ch;
712 disk_t *dp;
713 GHashTable* valid_label;
714 GHashTable* part_by_dle;
715 find_result_t *part_find;
716 find_result_t *a_part_find;
717 gboolean right_label = FALSE;
718 gboolean found_something = FALSE;
719 double sec;
720 off_t kb;
721 off_t bytes;
722 off_t orig_kb;
723 int taper_part = 0;
724
725 g_return_val_if_fail(output_find != NULL, 0);
726 g_return_val_if_fail(logfile != NULL, 0);
727
728 if (string_chunk == NULL) {
729 string_chunk = g_string_chunk_new(32768);
730 }
731 valid_label = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
732 part_by_dle = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
733 datestamp = g_strdup(passed_datestamp);
734
735 if((logf = fopen(logfile, "r")) == NULL) {
736 error(_("could not open logfile %s: %s"), logfile, strerror(errno));
737 /*NOTREACHED*/
738 }
739
740 filenum = (off_t)0;
741 while(get_logline(logf)) {
742 if (curlog == L_START && curprog == P_TAPER) {
743 amfree(ck_label);
744 ck_datestamp = NULL;
745 if(parse_taper_datestamp_log(curstr, &ck_datestamp,
746 &ck_label) == 0) {
747 g_printf(_("strange log line in %s \"start taper %s\"\n"),
748 logfile, curstr);
749 continue;
750 }
751 if (datestamp != NULL) {
752 if (strcmp(datestamp, ck_datestamp) != 0) {
753 g_printf(_("Log file %s stamped %s, expecting %s!\n"),
754 logfile, ck_datestamp, datestamp);
755 amfree(ck_label);
756 break;
757 }
758 }
759
760 right_label = volume_matches(label, ck_label, ck_datestamp);
761 if (right_label && ck_label) {
762 g_hash_table_insert(valid_label, g_strdup(ck_label),
763 GINT_TO_POINTER(1));
764 }
765 if (label && datestamp && right_label) {
766 found_something = TRUE;
767 }
768 amfree(current_label);
769 current_label = ck_label;
770 ck_label = NULL;
771 if (datestamp == NULL) {
772 datestamp = g_strdup(ck_datestamp);
773 }
774 filenum = (off_t)0;
775 }
776 if (!datestamp)
777 continue;
778 if (right_label &&
779 (curlog == L_SUCCESS ||
780 curlog == L_CHUNK || curlog == L_PART || curlog == L_PARTPARTIAL) &&
781 curprog == P_TAPER) {
782 filenum++;
783 } else if (right_label && curlog == L_PARTIAL && curprog == P_TAPER &&
784 taper_part == 0) {
785 filenum++;
786 }
787 partnum = -1;
788 totalparts = -1;
789 if (curlog == L_SUCCESS || curlog == L_CHUNKSUCCESS ||
790 curlog == L_DONE || curlog == L_FAIL ||
791 curlog == L_CHUNK || curlog == L_PART || curlog == L_PARTIAL ||
792 curlog == L_PARTPARTIAL ) {
793 s = curstr;
794 ch = *s++;
795
796 skip_whitespace(s, ch);
797 if(ch == '\0') {
798 g_printf(_("strange log line in %s \"%s\"\n"),
799 logfile, curstr);
800 continue;
801 }
802
803 if (curlog == L_PART || curlog == L_PARTPARTIAL) {
804 char *part_label;
805 char *qpart_label = s - 1;
806 taper_part++;
807 skip_quoted_string(s, ch);
808 s[-1] = '\0';
809
810 part_label = unquote_string(qpart_label);
811 if (!g_hash_table_lookup(valid_label, part_label)) {
812 amfree(part_label);
813 continue;
814 }
815 amfree(current_label);
816 current_label = part_label;
817
818 skip_whitespace(s, ch);
819 if(ch == '\0') {
820 g_printf("strange log line in %s \"%s\"\n",
821 logfile, curstr);
822 continue;
823 }
824
825 number = s - 1;
826 skip_non_whitespace(s, ch);
827 s[-1] = '\0';
828 fileno = atoi(number);
829 filenum = fileno;
830 if (filenum == 0)
831 continue;
832
833 skip_whitespace(s, ch);
834 if(ch == '\0') {
835 g_printf("strange log line in %s \"%s\"\n",
836 logfile, curstr);
837 continue;
838 }
839 } else {
840 taper_part = 0;
841 }
842
843 host = s - 1;
844 skip_non_whitespace(s, ch);
845 host_undo = s - 1;
846 *host_undo = '\0';
847
848 skip_whitespace(s, ch);
849 if(ch == '\0') {
850 g_printf(_("strange log line in %s \"%s\"\n"),
851 logfile, curstr);
852 continue;
853 }
854 qdisk = s - 1;
855 skip_quoted_string(s, ch);
856 disk_undo = s - 1;
857 *disk_undo = '\0';
858 disk = unquote_string(qdisk);
859
860 skip_whitespace(s, ch);
861 if(ch == '\0') {
862 g_printf(_("strange log line in %s \"%s\"\n"),
863 logfile, curstr);
864 continue;
865 }
866 date = s - 1;
867 skip_non_whitespace(s, ch);
868 date_undo = s - 1;
869 *date_undo = '\0';
870
871 if(strlen(date) < 3) { /* old log didn't have datestamp */
872 level = atoi(date);
873 date = stralloc(datestamp);
874 partnum = 1;
875 totalparts = 1;
876 } else {
877 if (curprog == P_TAPER &&
878 (curlog == L_CHUNK || curlog == L_PART ||
879 curlog == L_PARTPARTIAL || curlog == L_PARTIAL ||
880 curlog == L_DONE)) {
881 char *s1, ch1;
882 skip_whitespace(s, ch);
883 number = s - 1;
884 skip_non_whitespace(s, ch);
885 s1 = &s[-1];
886 ch1 = *s1;
887 skip_whitespace(s, ch);
888 if (*(s-1) != '[') {
889 *s1 = ch1;
890 sscanf(number, "%d/%d", &partnum, &totalparts);
891 if (partnum > maxparts)
892 maxparts = partnum;
893 if (totalparts > maxparts)
894 maxparts = totalparts;
895 } else { /* nparts is not in all PARTIAL lines */
896 partnum = 1;
897 totalparts = 1;
898 s = number + 1;
899 }
900 } else {
901 skip_whitespace(s, ch);
902 }
903 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
904 g_printf(_("Fstrange log line in %s \"%s\"\n"),
905 logfile, s-1);
906 continue;
907 }
908 skip_integer(s, ch);
909 }
910
911 skip_whitespace(s, ch);
912 if(ch == '\0') {
913 g_printf(_("strange log line in %s \"%s\"\n"),
914 logfile, curstr);
915 continue;
916 }
917 rest = s - 1;
918 skip_non_whitespace(s, ch);
919 rest_undo = s - 1;
920 *rest_undo = '\0';
921 if (strcmp(rest, "[sec") == 0) {
922 skip_whitespace(s, ch);
923 if(ch == '\0') {
924 g_printf(_("strange log line in %s \"%s\"\n"),
925 logfile, curstr);
926 continue;
927 }
928 sec = atof(s - 1);
929 skip_non_whitespace(s, ch);
930 skip_whitespace(s, ch);
931 rest = s - 1;
932 skip_non_whitespace(s, ch);
933 rest_undo = s - 1;
934 *rest_undo = '\0';
935 if (strcmp(rest, "kb") != 0 &&
936 strcmp(rest, "bytes") != 0) {
937 g_printf(_("Bstrange log line in %s \"%s\"\n"),
938 logfile, curstr);
939 continue;
940 }
941
942 skip_whitespace(s, ch);
943 if (ch == '\0') {
944 g_printf(_("strange log line in %s \"%s\"\n"),
945 logfile, curstr);
946 continue;
947 }
948 if (strcmp(rest, "kb") == 0) {
949 kb = atof(s - 1);
950 bytes = 0;
951 } else {
952 bytes = atof(s - 1);
953 kb = bytes / 1024;
954 }
955 skip_non_whitespace(s, ch);
956 skip_whitespace(s, ch);
957 rest = s - 1;
958 skip_non_whitespace(s, ch);
959 rest_undo = s - 1;
960 *rest_undo = '\0';
961 if (strcmp(rest, "kps") != 0) {
962 g_printf(_("Cstrange log line in %s \"%s\"\n"),
963 logfile, curstr);
964 continue;
965 }
966
967 skip_whitespace(s, ch);
968 if (ch == '\0') {
969 g_printf(_("strange log line in %s \"%s\"\n"),
970 logfile, curstr);
971 continue;
972 }
973 /* kps = atof(s - 1); */
974 skip_non_whitespace(s, ch);
975 skip_whitespace(s, ch);
976 rest = s - 1;
977 skip_non_whitespace(s, ch);
978 rest_undo = s - 1;
979 *rest_undo = '\0';
980 if (strcmp(rest, "orig-kb") != 0) {
981 orig_kb = 0;
982 } else {
983
984 skip_whitespace(s, ch);
985 if(ch == '\0') {
986 g_printf(_("strange log line in %s \"%s\"\n"),
987 logfile, curstr);
988 continue;
989 }
990 orig_kb = atof(s - 1);
991 }
992 } else {
993 sec = 0;
994 kb = 0;
995 bytes = 0;
996 orig_kb = 0;
997 *rest_undo = ' ';
998 }
999
1000 if (strncmp(rest, "error", 5) == 0) rest += 6;
1001 if (strncmp(rest, "config", 6) == 0) rest += 7;
1002
1003 dp = lookup_disk(host,disk);
1004 if ( dp == NULL ) {
1005 if (dynamic_disklist == NULL) {
1006 continue;
1007 }
1008 dp = add_disk(dynamic_disklist, host, disk);
1009 }
1010 if (find_match(host, disk)) {
1011 if(curprog == P_TAPER) {
1012 char *key = g_strdup_printf(
1013 "HOST:%s DISK:%s: DATE:%s LEVEL:%d",
1014 host, disk, date, level);
1015 find_result_t *new_output_find = g_new0(find_result_t, 1);
1016 part_find = g_hash_table_lookup(part_by_dle, key);
1017 maxparts = partnum;
1018 if (maxparts < totalparts)
1019 maxparts = totalparts;
1020 for (a_part_find = part_find;
1021 a_part_find;
1022 a_part_find = a_part_find->next) {
1023 if (maxparts < a_part_find->partnum)
1024 maxparts = a_part_find->partnum;
1025 if (maxparts < a_part_find->totalparts)
1026 maxparts = a_part_find->totalparts;
1027 }
1028 new_output_find->timestamp = g_string_chunk_insert_const(string_chunk, date);
1029 new_output_find->write_timestamp = g_string_chunk_insert_const(string_chunk, datestamp);
1030 new_output_find->hostname=g_string_chunk_insert_const(string_chunk, host);
1031 new_output_find->diskname=g_string_chunk_insert_const(string_chunk, disk);
1032 new_output_find->level=level;
1033 new_output_find->partnum = partnum;
1034 new_output_find->totalparts = totalparts;
1035 new_output_find->label=g_string_chunk_insert_const(string_chunk, current_label);
1036 new_output_find->status=NULL;
1037 new_output_find->dump_status=NULL;
1038 new_output_find->message="";
1039 new_output_find->filenum=filenum;
1040 new_output_find->sec=sec;
1041 new_output_find->kb=kb;
1042 new_output_find->bytes=bytes;
1043 new_output_find->orig_kb=orig_kb;
1044 new_output_find->next=NULL;
1045 if (curlog == L_SUCCESS) {
1046 new_output_find->status = "OK";
1047 new_output_find->dump_status = "OK";
1048 new_output_find->next = *output_find;
1049 new_output_find->partnum = 1; /* L_SUCCESS is pre-splitting */
1050 *output_find = new_output_find;
1051 found_something = TRUE;
1052 } else if (curlog == L_CHUNKSUCCESS || curlog == L_DONE ||
1053 curlog == L_PARTIAL || curlog == L_FAIL) {
1054 /* result line */
1055 if (curlog == L_PARTIAL || curlog == L_FAIL) {
1056 /* set dump_status of each part */
1057 for (a_part_find = part_find;
1058 a_part_find;
1059 a_part_find = a_part_find->next) {
1060 if (curlog == L_PARTIAL)
1061 a_part_find->dump_status = "PARTIAL";
1062 else {
1063 a_part_find->dump_status = "FAIL";
1064 a_part_find->message = g_string_chunk_insert_const(string_chunk, rest);
1065 }
1066 }
1067 } else {
1068 if (maxparts > -1) { /* format with part */
1069 /* must check if all part are there */
1070 int num_part = maxparts;
1071 for (a_part_find = part_find;
1072 a_part_find;
1073 a_part_find = a_part_find->next) {
1074 if (a_part_find->partnum == num_part &&
1075 strcmp(a_part_find->status, "OK") == 0)
1076 num_part--;
1077 }
1078 /* set dump_status of each part */
1079 for (a_part_find = part_find;
1080 a_part_find;
1081 a_part_find = a_part_find->next) {
1082 if (num_part == 0) {
1083 a_part_find->dump_status = "OK";
1084 } else {
1085 a_part_find->dump_status = "FAIL";
1086 a_part_find->message =
1087 g_string_chunk_insert_const(string_chunk, "Missing part");
1088 }
1089 }
1090 }
1091 }
1092 if (curlog == L_DONE) {
1093 for (a_part_find = part_find;
1094 a_part_find;
1095 a_part_find = a_part_find->next) {
1096 if (a_part_find->totalparts == -1) {
1097 a_part_find->totalparts = maxparts;
1098 }
1099 if (a_part_find->orig_kb == 0) {
1100 a_part_find->orig_kb = orig_kb;
1101 }
1102 }
1103 }
1104 if (part_find) { /* find last element */
1105 for (a_part_find = part_find;
1106 a_part_find->next != NULL;
1107 a_part_find=a_part_find->next) {
1108 }
1109 /* merge part_find to *output_find */
1110 a_part_find->next = *output_find;
1111 *output_find = part_find;
1112 part_find = NULL;
1113 maxparts = -1;
1114 found_something = TRUE;
1115 g_hash_table_remove(part_by_dle, key);
1116 }
1117 free_find_result(&new_output_find);
1118 } else { /* part line */
1119 if (curlog == L_PART || curlog == L_CHUNK) {
1120 new_output_find->status = "OK";
1121 new_output_find->dump_status = "OK";
1122 } else { /* PARTPARTIAL */
1123 new_output_find->status = "PARTIAL";
1124 new_output_find->dump_status = "PARTIAL";
1125 }
1126 /* Add to part_find list */
1127 if (part_find) {
1128 new_output_find->next = part_find;
1129 part_find = new_output_find;
1130 } else {
1131 new_output_find->next = NULL;
1132 part_find = new_output_find;
1133 }
1134 g_hash_table_insert(part_by_dle, g_strdup(key),
1135 part_find);
1136 found_something = TRUE;
1137 }
1138 amfree(key);
1139 }
1140 else if(curlog == L_FAIL) {
1141 char *status_failed;
1142 /* print other failures too -- this is a hack to ensure that failures which
1143 * did not make it to tape are also listed in the output of 'amadmin x find';
1144 * users that do not want this information (e.g., Amanda::DB::Catalog) should
1145 * filter dumps with a NULL label. */
1146 find_result_t *new_output_find = g_new0(find_result_t, 1);
1147 new_output_find->next = *output_find;
1148 new_output_find->timestamp = g_string_chunk_insert_const(string_chunk, date);
1149 new_output_find->write_timestamp = g_strdup("00000000000000"); /* dump was not written.. */
1150 new_output_find->hostname=g_string_chunk_insert_const(string_chunk, host);
1151 new_output_find->diskname=g_string_chunk_insert_const(string_chunk, disk);
1152 new_output_find->level=level;
1153 new_output_find->label=NULL;
1154 new_output_find->partnum=partnum;
1155 new_output_find->totalparts=totalparts;
1156 new_output_find->filenum=0;
1157 new_output_find->sec=sec;
1158 new_output_find->kb=kb;
1159 new_output_find->bytes=bytes;
1160 new_output_find->orig_kb=orig_kb;
1161 status_failed = vstralloc(
1162 "FAILED (",
1163 program_str[(int)curprog],
1164 ") ",
1165 rest,
1166 NULL);
1167 new_output_find->status = g_string_chunk_insert_const(string_chunk, status_failed);
1168 amfree(status_failed);
1169 new_output_find->dump_status="";
1170 new_output_find->message="";
1171 *output_find=new_output_find;
1172 found_something = TRUE;
1173 maxparts = -1;
1174 }
1175 }
1176 amfree(disk);
1177 }
1178 }
1179
1180 g_hash_table_destroy(valid_label);
1181 afclose(logf);
1182 amfree(datestamp);
1183 amfree(current_label);
1184
1185 return found_something;
1186 }
1187
1188
1189 /*
1190 * Return the set of dumps that match *all* of the given patterns (we consider
1191 * an empty pattern to match .*, though). If 'ok' is true, will only match
1192 * dumps with SUCCESS status.
1193 *
1194 * Returns a newly allocated list of results, where all strings are also newly
1195 * allocated. Apparently some part of Amanda leaks under this condition.
1196 */
1197 find_result_t *
dumps_match(find_result_t * output_find,char * hostname,char * diskname,char * datestamp,char * level,int ok)1198 dumps_match(
1199 find_result_t *output_find,
1200 char *hostname,
1201 char *diskname,
1202 char *datestamp,
1203 char *level,
1204 int ok)
1205 {
1206 find_result_t *cur_result;
1207 find_result_t *matches = NULL;
1208
1209 for(cur_result=output_find;
1210 cur_result;
1211 cur_result=cur_result->next) {
1212 char level_str[NUM_STR_SIZE];
1213 g_snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
1214 if((!hostname || *hostname == '\0' || match_host(hostname, cur_result->hostname)) &&
1215 (!diskname || *diskname == '\0' || match_disk(diskname, cur_result->diskname)) &&
1216 (!datestamp || *datestamp== '\0' || match_datestamp(datestamp, cur_result->timestamp)) &&
1217 (!level || *level== '\0' || match_level(level, level_str)) &&
1218 (!ok || !strcmp(cur_result->status, "OK")) &&
1219 (!ok || !strcmp(cur_result->dump_status, "OK"))){
1220
1221 find_result_t *curmatch = g_new0(find_result_t, 1);
1222 memcpy(curmatch, cur_result, SIZEOF(find_result_t));
1223
1224 curmatch->timestamp = cur_result->timestamp;
1225 curmatch->write_timestamp = cur_result->write_timestamp;
1226 curmatch->hostname = cur_result->hostname;
1227 curmatch->diskname = cur_result->diskname;
1228 curmatch->level = cur_result->level;
1229 curmatch->label = cur_result->label? cur_result->label : NULL;
1230 curmatch->filenum = cur_result->filenum;
1231 curmatch->sec = cur_result->sec;
1232 curmatch->kb = cur_result->kb;
1233 curmatch->bytes = cur_result->bytes;
1234 curmatch->orig_kb = cur_result->orig_kb;
1235 curmatch->status = cur_result->status;
1236 curmatch->dump_status = cur_result->dump_status;
1237 curmatch->message = cur_result->message;
1238 curmatch->partnum = cur_result->partnum;
1239 curmatch->totalparts = cur_result->totalparts;
1240 curmatch->next = matches;
1241 matches = curmatch;
1242 }
1243 }
1244
1245 return(matches);
1246 }
1247
1248 /*
1249 * Return the set of dumps that match one or more of the given dumpspecs,
1250 * If 'ok' is true, only dumps with a SUCCESS status will be matched.
1251 *
1252 * Returns a newly allocated list of results, where all strings are also newly
1253 * allocated. Apparently some part of Amanda leaks under this condition.
1254 */
1255 find_result_t *
dumps_match_dumpspecs(find_result_t * output_find,GSList * dumpspecs,int ok)1256 dumps_match_dumpspecs(
1257 find_result_t *output_find,
1258 GSList *dumpspecs,
1259 int ok)
1260 {
1261 find_result_t *cur_result;
1262 find_result_t *matches = NULL;
1263 GSList *dumpspec;
1264 dumpspec_t *ds;
1265
1266 for(cur_result=output_find;
1267 cur_result;
1268 cur_result=cur_result->next) {
1269 char level_str[NUM_STR_SIZE];
1270 char *zeropad_ts = NULL;
1271 char *zeropad_w_ts = NULL;
1272 g_snprintf(level_str, SIZEOF(level_str), "%d", cur_result->level);
1273
1274 /* get the timestamp padded to full width */
1275 if (strlen(cur_result->timestamp) < 14) {
1276 zeropad_ts = g_new0(char, 15);
1277 memset(zeropad_ts, '0', 14);
1278 memcpy(zeropad_ts, cur_result->timestamp, strlen(cur_result->timestamp));
1279 }
1280 if (strlen(cur_result->write_timestamp) < 14) {
1281 zeropad_w_ts = g_new0(char, 15);
1282 memset(zeropad_w_ts, '0', 14);
1283 memcpy(zeropad_w_ts, cur_result->write_timestamp, strlen(cur_result->write_timestamp));
1284 }
1285
1286 for (dumpspec = dumpspecs; dumpspec; dumpspec = dumpspec->next) {
1287 ds = (dumpspec_t *)dumpspec->data;
1288 if((!ds->host || *ds->host == '\0' || match_host(ds->host, cur_result->hostname)) &&
1289 (!ds->disk || *ds->disk == '\0' || match_disk(ds->disk, cur_result->diskname)) &&
1290 (!ds->datestamp || *ds->datestamp== '\0'
1291 || match_datestamp(ds->datestamp, cur_result->timestamp)
1292 || (zeropad_ts && match_datestamp(ds->datestamp, zeropad_ts))) &&
1293 (!ds->write_timestamp || *ds->write_timestamp== '\0'
1294 || match_datestamp(ds->write_timestamp, cur_result->write_timestamp)
1295 || (zeropad_w_ts && match_datestamp(ds->write_timestamp, zeropad_w_ts))) &&
1296 (!ds->level || *ds->level== '\0' || match_level(ds->level, level_str)) &&
1297 (!ok || !strcmp(cur_result->status, "OK")) &&
1298 (!ok || !strcmp(cur_result->dump_status, "OK"))) {
1299
1300 find_result_t *curmatch = alloc(SIZEOF(find_result_t));
1301 memcpy(curmatch, cur_result, SIZEOF(find_result_t));
1302
1303 curmatch->timestamp = cur_result->timestamp;
1304 curmatch->write_timestamp = cur_result->write_timestamp;
1305 curmatch->hostname = cur_result->hostname;
1306 curmatch->diskname = cur_result->diskname;
1307 curmatch->level = cur_result->level;
1308 curmatch->label = cur_result->label? cur_result->label : NULL;
1309 curmatch->filenum = cur_result->filenum;
1310 curmatch->status = cur_result->status;
1311 curmatch->dump_status = cur_result->dump_status;
1312 curmatch->message = cur_result->message;
1313 curmatch->partnum = cur_result->partnum;
1314 curmatch->totalparts = cur_result->totalparts;
1315
1316 curmatch->next = matches;
1317 matches = curmatch;
1318 break;
1319 }
1320 }
1321
1322 amfree(zeropad_ts);
1323 }
1324
1325 return(matches);
1326 }
1327
1328 find_result_t *
dump_exist(find_result_t * output_find,char * hostname,char * diskname,char * datestamp,int level)1329 dump_exist(
1330 find_result_t *output_find,
1331 char *hostname,
1332 char *diskname,
1333 char *datestamp,
1334 int level)
1335 {
1336 find_result_t *output_find_result;
1337
1338 for(output_find_result=output_find;
1339 output_find_result;
1340 output_find_result=output_find_result->next) {
1341 if( !strcmp(output_find_result->hostname, hostname) &&
1342 !strcmp(output_find_result->diskname, diskname) &&
1343 !strcmp(output_find_result->timestamp, datestamp) &&
1344 output_find_result->level == level) {
1345
1346 return output_find_result;
1347 }
1348 }
1349 return(NULL);
1350 }
1351