Imported Upstream version 3.3.3
[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  * Copyright (c) 2007-2012 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
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
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
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 *
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 *
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 *
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
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 *
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 *
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
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
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 *
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
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 *
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
442     return tp;
443 }
444
445
446 /* insert in reversed datestamp order */
447 /*@ignore@*/
448 static tape_t *
449 insert(
450     tape_t *list,
451     tape_t *tp)
452 {
453     tape_t *prev, *cur;
454
455     prev = NULL;
456     cur = list;
457
458     while(cur != NULL && strcmp(cur->datestamp, tp->datestamp) >= 0) {
459         prev = cur;
460         cur = cur->next;
461     }
462     tp->prev = prev;
463     tp->next = cur;
464     if(prev == NULL) {
465         list = tp;
466 #ifndef __lint
467     } else {
468         prev->next = tp;
469 #endif
470     }
471     if(cur !=NULL)
472         cur->prev = tp;
473
474     return list;
475 }
476 /*@end@*/
477
478 /*
479  * Converts datestamp (an char of the form YYYYMMDD or YYYYMMDDHHMMSS) into a real
480  * time_t value.
481  * Since the datestamp contains no timezone or hh/mm/ss information, the
482  * value is approximate.  This is ok for our purposes, since we round off
483  * scheduling calculations to the nearest day.
484  */
485
486 static time_t
487 stamp2time(
488     char *datestamp)
489 {
490     struct tm *tm;
491     time_t now;
492     char date[9];
493     int dateint;
494
495     strncpy(date, datestamp, 8);
496     date[8] = '\0';
497     dateint = atoi(date);
498     now = time(0);
499     tm = localtime(&now);       /* initialize sec/min/hour & gmtoff */
500
501     if (!tm) {
502         tm = alloc(SIZEOF(struct tm));
503         tm->tm_sec   = 0;
504         tm->tm_min   = 0;
505         tm->tm_hour  = 0;
506         tm->tm_wday  = 0;
507         tm->tm_yday  = 0;
508         tm->tm_isdst = 0;
509     }
510
511
512     tm->tm_year = ( dateint          / 10000) - 1900;
513     tm->tm_mon  = ((dateint % 10000) /   100) - 1;
514     tm->tm_mday = ((dateint %   100)        );
515
516     return mktime(tm);
517 }
518
519 char *
520 list_new_tapes(
521     int nb)
522 {
523     tape_t *lasttp, *iter;
524     char *result = NULL;
525
526     /* Find latest reusable new tape */
527     lasttp = lookup_tapepos(lookup_nb_tape());
528     while (lasttp && lasttp->reuse == 0)
529         lasttp = lasttp->prev;
530
531     if(lasttp && nb > 0 && strcmp(lasttp->datestamp,"0") == 0) {
532         int c = 0;
533         iter = lasttp;
534         /* count the number of tapes we *actually* used */
535         while(iter && nb > 0 && strcmp(iter->datestamp,"0") == 0) {
536             if (iter->reuse) {
537                 c++;
538                 nb--;
539             }
540             iter = iter->prev;
541         }
542
543         if(c == 1) {
544             result = g_strdup_printf(
545                         _("The next new tape already labelled is: %s."),
546                         lasttp->label);
547         } else {
548             result = g_strdup_printf(
549                         _("The next %d new tapes already labelled are: %s"),
550                         c, lasttp->label);
551             iter = lasttp->prev;
552             c--;
553             while(iter && c > 0 && strcmp(iter->datestamp,"0") == 0) {
554                 if (iter->reuse) {
555                     result = vstrextend(&result, ", ", iter->label, NULL);
556                     c--;
557                 }
558                 iter = iter->prev;
559             }
560         }
561     }
562     return result;
563 }
564
565 void
566 print_new_tapes(
567     FILE *output,
568     int   nb)
569 {
570     char *result = list_new_tapes(nb);
571
572     if (result) {
573         g_fprintf(output,"%s\n", result);
574         amfree(result);
575     }
576 }