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 * Authors: the Amanda Development Team. Its members are listed in a
25 * file named AUTHORS, in the root directory of this distribution.
26 */
27 /*
28 * $Id: tapefile.c,v 1.37 2006/07/21 00:25:52 martinea Exp $
29 *
30 * routines to read and write the amanda active tape list
31 */
32 #include "amanda.h"
33 #include "match.h"
34 #include "tapefile.h"
35 #include "conffile.h"
36
37 static tape_t *tape_list = NULL;
38
39 /* local functions */
40 static tape_t *parse_tapeline(int *status, char *line);
41 static tape_t *insert(tape_t *list, tape_t *tp);
42 static time_t stamp2time(char *datestamp);
43
44 int
read_tapelist(char * tapefile)45 read_tapelist(
46 char *tapefile)
47 {
48 tape_t *tp;
49 FILE *tapef;
50 int pos;
51 char *line = NULL;
52 int status = 0;
53
54 clear_tapelist();
55 if((tapef = fopen(tapefile,"r")) == NULL) {
56 if (errno == ENOENT) {
57 /* no tapelist is equivalent to an empty tapelist */
58 return 0;
59 } else {
60 g_debug("Error opening '%s': %s", tapefile, strerror(errno));
61 return 1;
62 }
63 }
64
65 while((line = agets(tapef)) != NULL) {
66 if (line[0] == '\0') {
67 amfree(line);
68 continue;
69 }
70 tp = parse_tapeline(&status, line);
71 amfree(line);
72 if(tp == NULL && status != 0)
73 return 1;
74 if(tp != NULL)
75 tape_list = insert(tape_list, tp);
76 }
77 afclose(tapef);
78
79 for(pos=1,tp=tape_list; tp != NULL; pos++,tp=tp->next) {
80 tp->position = pos;
81 }
82
83 return 0;
84 }
85
86 int
write_tapelist(char * tapefile)87 write_tapelist(
88 char *tapefile)
89 {
90 tape_t *tp;
91 FILE *tapef;
92 char *newtapefile;
93 int rc;
94
95 newtapefile = stralloc2(tapefile, ".new");
96
97 if((tapef = fopen(newtapefile,"w")) == NULL) {
98 amfree(newtapefile);
99 return 1;
100 }
101
102 for(tp = tape_list; tp != NULL; tp = tp->next) {
103 g_fprintf(tapef, "%s %s", tp->datestamp, tp->label);
104 if(tp->reuse) g_fprintf(tapef, " reuse");
105 else g_fprintf(tapef, " no-reuse");
106 if (tp->barcode)
107 g_fprintf(tapef, " BARCODE:%s", tp->barcode);
108 if (tp->meta)
109 g_fprintf(tapef, " META:%s", tp->meta);
110 if (tp->blocksize)
111 g_fprintf(tapef, " BLOCKSIZE:%jd", (intmax_t)tp->blocksize);
112 if (tp->comment)
113 g_fprintf(tapef, " #%s", tp->comment);
114 g_fprintf(tapef, "\n");
115 }
116
117 if (fclose(tapef) == EOF) {
118 g_fprintf(stderr,_("error [closing %s: %s]"), newtapefile, strerror(errno));
119 amfree(newtapefile);
120 return 1;
121 }
122 rc = rename(newtapefile, tapefile);
123 amfree(newtapefile);
124
125 return(rc != 0);
126 }
127
128 void
clear_tapelist(void)129 clear_tapelist(void)
130 {
131 tape_t *tp, *next;
132
133 for(tp = tape_list; tp; tp = next) {
134 amfree(tp->label);
135 amfree(tp->datestamp);
136 amfree(tp->barcode);
137 amfree(tp->meta);
138 amfree(tp->comment);
139 next = tp->next;
140 amfree(tp);
141 }
142 tape_list = NULL;
143 }
144
145 tape_t *
lookup_tapelabel(const char * label)146 lookup_tapelabel(
147 const char *label)
148 {
149 tape_t *tp;
150
151 for(tp = tape_list; tp != NULL; tp = tp->next) {
152 if(strcmp(label, tp->label) == 0) return tp;
153 }
154 return NULL;
155 }
156
157
158
159 tape_t *
lookup_tapepos(int pos)160 lookup_tapepos(
161 int pos)
162 {
163 tape_t *tp;
164
165 for(tp = tape_list; tp != NULL; tp = tp->next) {
166 if(tp->position == pos) return tp;
167 }
168 return NULL;
169 }
170
171
172 tape_t *
lookup_tapedate(char * datestamp)173 lookup_tapedate(
174 char *datestamp)
175 {
176 tape_t *tp;
177
178 for(tp = tape_list; tp != NULL; tp = tp->next) {
179 if(strcmp(tp->datestamp, datestamp) == 0) return tp;
180 }
181 return NULL;
182 }
183
184 int
lookup_nb_tape(void)185 lookup_nb_tape(void)
186 {
187 tape_t *tp;
188 int pos=0;
189
190 for(tp = tape_list; tp != NULL; tp = tp->next) {
191 pos=tp->position;
192 }
193 return pos;
194 }
195
196
197 char *
get_last_reusable_tape_label(int skip)198 get_last_reusable_tape_label(
199 int skip)
200 {
201 tape_t *tp = lookup_last_reusable_tape(skip);
202 return (tp != NULL) ? tp->label : NULL;
203 }
204
205 tape_t *
lookup_last_reusable_tape(int skip)206 lookup_last_reusable_tape(
207 int skip)
208 {
209 tape_t *tp, **tpsave;
210 int count=0;
211 int s;
212 int tapecycle = getconf_int(CNF_TAPECYCLE);
213 char *labelstr = getconf_str (CNF_LABELSTR);
214
215 /*
216 * The idea here is we keep the last "several" reusable tapes we
217 * find in a stack and then return the n-th oldest one to the
218 * caller. If skip is zero, the oldest is returned, if it is
219 * one, the next oldest, two, the next to next oldest and so on.
220 */
221 tpsave = alloc((skip + 1) * SIZEOF(*tpsave));
222 for(s = 0; s <= skip; s++) {
223 tpsave[s] = NULL;
224 }
225 for(tp = tape_list; tp != NULL; tp = tp->next) {
226 if(tp->reuse == 1 && strcmp(tp->datestamp,"0") != 0 && match (labelstr, tp->label)) {
227 count++;
228 for(s = skip; s > 0; s--) {
229 tpsave[s] = tpsave[s - 1];
230 }
231 tpsave[0] = tp;
232 }
233 }
234 s = tapecycle - count;
235 if(s < 0) s = 0;
236 if(count < tapecycle - skip) tp = NULL;
237 else tp = tpsave[skip - s];
238 amfree(tpsave);
239 return tp;
240 }
241
242 int
reusable_tape(tape_t * tp)243 reusable_tape(
244 tape_t *tp)
245 {
246 int count = 0;
247
248 if(tp == NULL) return 0;
249 if(tp->reuse == 0) return 0;
250 if( strcmp(tp->datestamp,"0") == 0) return 1;
251 while(tp != NULL) {
252 if(tp->reuse == 1) count++;
253 tp = tp->prev;
254 }
255 return (count >= getconf_int(CNF_TAPECYCLE));
256 }
257
258 void
remove_tapelabel(char * label)259 remove_tapelabel(
260 char *label)
261 {
262 tape_t *tp, *prev, *next;
263
264 tp = lookup_tapelabel(label);
265 if(tp != NULL) {
266 prev = tp->prev;
267 next = tp->next;
268 /*@ignore@*/
269 if(prev != NULL)
270 prev->next = next;
271 else /* begin of list */
272 tape_list = next;
273 if(next != NULL)
274 next->prev = prev;
275 /*@end@*/
276 while (next != NULL) {
277 next->position--;
278 next = next->next;
279 }
280 amfree(tp->datestamp);
281 amfree(tp->label);
282 amfree(tp->meta);
283 amfree(tp->comment);
284 amfree(tp->barcode);
285 amfree(tp);
286 }
287 }
288
289 tape_t *
add_tapelabel(char * datestamp,char * label,char * comment)290 add_tapelabel(
291 char *datestamp,
292 char *label,
293 char *comment)
294 {
295 tape_t *cur, *new;
296
297 /* insert a new record to the front of the list */
298
299 new = g_new0(tape_t, 1);
300
301 new->datestamp = stralloc(datestamp);
302 new->position = 0;
303 new->reuse = 1;
304 new->label = stralloc(label);
305 new->comment = comment? stralloc(comment) : NULL;
306
307 new->prev = NULL;
308 if(tape_list != NULL) tape_list->prev = new;
309 new->next = tape_list;
310 tape_list = new;
311
312 /* scan list, updating positions */
313 cur = tape_list;
314 while(cur != NULL) {
315 cur->position++;
316 cur = cur->next;
317 }
318
319 return new;
320 }
321
322 int
guess_runs_from_tapelist(void)323 guess_runs_from_tapelist(void)
324 {
325 tape_t *tp;
326 int i, ntapes, tape_ndays, dumpcycle, runtapes, runs;
327 time_t tape_time, today;
328
329 today = time(0);
330 dumpcycle = getconf_int(CNF_DUMPCYCLE);
331 runtapes = getconf_int(CNF_RUNTAPES);
332 if(runtapes == 0) runtapes = 1; /* just in case */
333
334 ntapes = 0;
335 tape_ndays = 0;
336 for(i = 1; i < getconf_int(CNF_TAPECYCLE); i++) {
337 if((tp = lookup_tapepos(i)) == NULL) break;
338
339 tape_time = stamp2time(tp->datestamp);
340 tape_ndays = (int)days_diff(tape_time, today);
341
342 if(tape_ndays < dumpcycle) ntapes++;
343 else break;
344 }
345
346 if(tape_ndays < dumpcycle) {
347 /* scale for best guess */
348 if(tape_ndays == 0) ntapes = dumpcycle * runtapes;
349 else ntapes = ntapes * dumpcycle / tape_ndays;
350 }
351 else if(ntapes == 0) {
352 /* no dumps within the last dumpcycle, guess as above */
353 ntapes = dumpcycle * runtapes;
354 }
355
356 runs = (ntapes + runtapes - 1) / runtapes;
357 if (runs <= 0)
358 runs = 1;
359 return runs;
360 }
361
362 static tape_t *
parse_tapeline(int * status,char * line)363 parse_tapeline(
364 int *status,
365 char *line)
366 {
367 tape_t *tp = NULL;
368 char *s, *s1;
369 int ch;
370 char *cline;
371
372 *status = 0;
373
374 s = line;
375 ch = *s++;
376
377 skip_whitespace(s, ch);
378 if(ch == '\0') {
379 return NULL;
380 }
381
382 cline = g_strdup(line);
383 tp = g_new0(tape_t, 1);
384
385 s1 = s - 1;
386 skip_non_whitespace(s, ch);
387 s[-1] = '\0';
388 tp->datestamp = stralloc(s1);
389
390 skip_whitespace(s, ch);
391 s1 = s - 1;
392 skip_non_whitespace(s, ch);
393 s[-1] = '\0';
394 tp->label = stralloc(s1);
395
396 skip_whitespace(s, ch);
397 tp->reuse = 1;
398 if(strncmp_const(s - 1, "reuse") == 0) {
399 tp->reuse = 1;
400 s1 = s - 1;
401 skip_non_whitespace(s, ch);
402 s[-1] = '\0';
403 skip_whitespace(s, ch);
404 }
405 if(strncmp_const(s - 1, "no-reuse") == 0) {
406 tp->reuse = 0;
407 s1 = s - 1;
408 skip_non_whitespace(s, ch);
409 s[-1] = '\0';
410 skip_whitespace(s, ch);
411 }
412
413 if (strncmp_const(s - 1, "BARCODE:") == 0) {
414 s1 = s - 1 + 8;
415 skip_non_whitespace(s, ch);
416 s[-1] = '\0';
417 skip_whitespace(s, ch);
418 tp->barcode = stralloc(s1);
419 }
420
421 if (strncmp_const(s - 1, "META:") == 0) {
422 s1 = s - 1 + 5;
423 skip_non_whitespace(s, ch);
424 s[-1] = '\0';
425 skip_whitespace(s, ch);
426 tp->meta = stralloc(s1);
427 }
428
429 if (strncmp_const(s - 1, "BLOCKSIZE:") == 0) {
430 s1 = s - 1 + 10;
431 skip_non_whitespace(s, ch);
432 s[-1] = '\0';
433 skip_whitespace(s, ch);
434 tp->blocksize = atol(s1);
435 }
436 if (*(s - 1) == '#') {
437 tp->comment = stralloc(s); /* skip leading '#' */
438 } else if (*(s-1)) {
439 g_critical("Bogus line in the tapelist file: %s", cline);
440 }
441 g_free(cline);
442
443 return tp;
444 }
445
446
447 /* insert in reversed datestamp order */
448 /*@ignore@*/
449 static tape_t *
insert(tape_t * list,tape_t * tp)450 insert(
451 tape_t *list,
452 tape_t *tp)
453 {
454 tape_t *prev, *cur;
455
456 prev = NULL;
457 cur = list;
458
459 while(cur != NULL && strcmp(cur->datestamp, tp->datestamp) >= 0) {
460 prev = cur;
461 cur = cur->next;
462 }
463 tp->prev = prev;
464 tp->next = cur;
465 if(prev == NULL) {
466 list = tp;
467 #ifndef __lint
468 } else {
469 prev->next = tp;
470 #endif
471 }
472 if(cur !=NULL)
473 cur->prev = tp;
474
475 return list;
476 }
477 /*@end@*/
478
479 /*
480 * Converts datestamp (an char of the form YYYYMMDD or YYYYMMDDHHMMSS) into a real
481 * time_t value.
482 * Since the datestamp contains no timezone or hh/mm/ss information, the
483 * value is approximate. This is ok for our purposes, since we round off
484 * scheduling calculations to the nearest day.
485 */
486
487 static time_t
stamp2time(char * datestamp)488 stamp2time(
489 char *datestamp)
490 {
491 struct tm *tm;
492 time_t now;
493 char date[9];
494 int dateint;
495
496 strncpy(date, datestamp, 8);
497 date[8] = '\0';
498 dateint = atoi(date);
499 now = time(0);
500 tm = localtime(&now); /* initialize sec/min/hour & gmtoff */
501
502 if (!tm) {
503 tm = alloc(SIZEOF(struct tm));
504 tm->tm_sec = 0;
505 tm->tm_min = 0;
506 tm->tm_hour = 0;
507 tm->tm_wday = 0;
508 tm->tm_yday = 0;
509 tm->tm_isdst = 0;
510 }
511
512
513 tm->tm_year = ( dateint / 10000) - 1900;
514 tm->tm_mon = ((dateint % 10000) / 100) - 1;
515 tm->tm_mday = ((dateint % 100) );
516
517 return mktime(tm);
518 }
519
520 char *
list_new_tapes(int nb)521 list_new_tapes(
522 int nb)
523 {
524 tape_t *lasttp, *iter;
525 char *result = NULL;
526
527 /* Find latest reusable new tape */
528 lasttp = lookup_tapepos(lookup_nb_tape());
529 while (lasttp && lasttp->reuse == 0)
530 lasttp = lasttp->prev;
531
532 if(lasttp && nb > 0 && strcmp(lasttp->datestamp,"0") == 0) {
533 int c = 0;
534 iter = lasttp;
535 /* count the number of tapes we *actually* used */
536 while(iter && nb > 0 && strcmp(iter->datestamp,"0") == 0) {
537 if (iter->reuse) {
538 c++;
539 nb--;
540 }
541 iter = iter->prev;
542 }
543
544 if(c == 1) {
545 result = g_strdup_printf(
546 _("The next new tape already labelled is: %s."),
547 lasttp->label);
548 } else {
549 result = g_strdup_printf(
550 _("The next %d new tapes already labelled are: %s"),
551 c, lasttp->label);
552 iter = lasttp->prev;
553 c--;
554 while(iter && c > 0 && strcmp(iter->datestamp,"0") == 0) {
555 if (iter->reuse) {
556 result = vstrextend(&result, ", ", iter->label, NULL);
557 c--;
558 }
559 iter = iter->prev;
560 }
561 }
562 }
563 return result;
564 }
565
566 void
print_new_tapes(FILE * output,int nb)567 print_new_tapes(
568 FILE *output,
569 int nb)
570 {
571 char *result = list_new_tapes(nb);
572
573 if (result) {
574 g_fprintf(output,"%s\n", result);
575 amfree(result);
576 }
577 }
578