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