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