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