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