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