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