we use /var/lib/dumpdates, *not* /etc/dumpdates
[debian/amanda] / client-src / amandates.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: amandates.c,v 1.21 2006/07/25 18:35:21 martinea Exp $
28  *
29  * manage amandates file, that mimics /var/lib/dumpdates, but stores
30  * GNUTAR dates
31  */
32
33 #include "amanda.h"
34 #include "getfsent.h"
35 #include "util.h"
36
37 #include "amandates.h"
38
39 static amandates_t *amandates_list = NULL;
40 static FILE *amdf = NULL;
41 static int updated, readonly;
42 static char *g_amandates_file = NULL;
43 static void import_dumpdates(amandates_t *);
44 static void enter_record(char *, int , time_t);
45 static amandates_t *lookup(char *name, int import);
46
47 int
48 start_amandates(
49     char *amandates_file,
50     int   open_readwrite)
51 {
52     int rc, level = 0;
53     long ldate = 0L;
54     char *line;
55     char *name;
56     char *s;
57     int ch;
58     char *qname;
59
60     if (amandates_file == NULL) {
61         errno = 0;
62         return 0;
63     }
64
65     /* clean up from previous invocation */
66
67     if(amdf != NULL)
68         finish_amandates();
69     if(amandates_list != NULL)
70         free_amandates();
71     amfree(g_amandates_file);
72
73     /* initialize state */
74
75     updated = 0;
76     readonly = !open_readwrite;
77     amdf = NULL;
78     amandates_list = NULL;
79     g_amandates_file = stralloc(amandates_file);
80     /* open the file */
81
82     if (access(amandates_file,F_OK))
83         /* not yet existing */
84         if ( (rc = open(amandates_file,(O_CREAT|O_RDWR),0644)) != -1 )
85             /* open/create successfull */
86             aclose(rc);
87
88     if(open_readwrite)
89         amdf = fopen(amandates_file, "r+");
90     else
91         amdf = fopen(amandates_file, "r");
92
93     /* create it if we need to */
94
95     if(amdf == NULL && (errno == EINTR || errno == ENOENT) && open_readwrite)
96         amdf = fopen(amandates_file, "w");
97
98     if(amdf == NULL)
99         return 0;
100
101     if(open_readwrite)
102         rc = amflock(fileno(amdf), amandates_file);
103     else
104         rc = amroflock(fileno(amdf), amandates_file);
105
106     if(rc == -1) {
107         error(_("could not lock %s: %s"), amandates_file, strerror(errno));
108         /*NOTREACHED*/
109     }
110
111     for(; (line = agets(amdf)) != NULL; free(line)) {
112         if (line[0] == '\0')
113             continue;
114         s = line;
115         ch = *s++;
116
117         skip_whitespace(s, ch);
118         if(ch == '\0') {
119             continue;                           /* no name field */
120         }
121         qname = s - 1;
122         skip_quoted_string(s, ch);
123         s[-1] = '\0';                           /* terminate the name */
124         name = unquote_string(qname);
125
126         skip_whitespace(s, ch);
127         if(ch == '\0' || sscanf(s - 1, "%d %ld", &level, &ldate) != 2) {
128             amfree(name);
129             continue;                           /* no more fields */
130         }
131
132         if(level < 0 || level >= DUMP_LEVELS) {
133             amfree(name);
134             continue;
135         }
136
137         enter_record(name, level, (time_t) ldate);
138         amfree(name);
139     }
140
141     if(ferror(amdf)) {
142         error(_("reading %s: %s"), amandates_file, strerror(errno));
143         /*NOTREACHED*/
144     }
145
146     updated = 0;        /* reset updated flag */
147     return 1;
148 }
149
150 void
151 finish_amandates(void)
152 {
153     amandates_t *amdp;
154     int level;
155     char *qname;
156
157     if(amdf == NULL)
158         return;
159
160     if(updated) {
161         if(readonly) {
162             error(_("updated amandates after opening readonly"));
163             /*NOTREACHED*/
164         }
165
166         rewind(amdf);
167         for(amdp = amandates_list; amdp != NULL; amdp = amdp->next) {
168             for(level = 0; level < DUMP_LEVELS; level++) {
169                 if(amdp->dates[level] == EPOCH) continue;
170                 qname = quote_string(amdp->name);
171                 g_fprintf(amdf, "%s %d %ld\n",
172                         qname, level, (long) amdp->dates[level]);
173                 amfree(qname);
174             }
175         }
176     }
177
178     if(amfunlock(fileno(amdf), g_amandates_file) == -1) {
179         error(_("could not unlock %s: %s"), g_amandates_file, strerror(errno));
180         /*NOTREACHED*/
181     }
182     if (fclose(amdf) == EOF) {
183         error(_("error [closing %s: %s]"), g_amandates_file, strerror(errno));
184         /*NOTREACHED*/
185     }
186     amdf = NULL;
187 }
188
189 void
190 free_amandates(void)
191 {
192     amandates_t *amdp, *nextp;
193
194     for(amdp = amandates_list; amdp != NULL; amdp = nextp) {
195         nextp = amdp->next;
196         amfree(amdp->name);
197         amfree(amdp);
198     }
199     amandates_list = NULL;
200 }
201
202 static amandates_t *
203 lookup(
204     char *      name,
205     int         import)
206 {
207     amandates_t *prevp, *amdp;
208     int rc, level;
209
210     (void)import;       /* Quiet unused parameter warning */
211     rc = 0;
212
213     prevp = NULL;
214     amdp = amandates_list;
215     while (amdp != NULL) {
216         if ((rc = strcmp(name, amdp->name)) <= 0)
217             break;
218         prevp = amdp;
219         amdp = amdp->next;
220     }
221     if (!(amdp && (rc == 0))) {
222         amandates_t *newp = alloc(SIZEOF(amandates_t));
223         newp->name = stralloc(name);
224         for (level = 0; level < DUMP_LEVELS; level++)
225             newp->dates[level] = EPOCH;
226         newp->next = amdp;
227         if (prevp != NULL) {
228 #ifndef __lint  /* Remove complaint about NULL pointer assignment */
229             prevp->next = newp;
230 #else
231             (void)prevp;
232 #endif
233         } else {
234             amandates_list = newp;
235         }
236         import_dumpdates(newp);
237         return newp;
238     }
239     return amdp;
240 }
241
242 amandates_t *
243 amandates_lookup(
244     char *      name)
245 {
246     return lookup(name, 1);
247 }
248
249 static void
250 enter_record(
251     char *      name,
252     int         level,
253     time_t      dumpdate)
254 {
255     amandates_t *amdp;
256     char *qname;
257
258     amdp = lookup(name, 0);
259
260     if(level < 0 || level >= DUMP_LEVELS || dumpdate < amdp->dates[level]) {
261         qname = quote_string(name);
262         /* this is not allowed, but we can ignore it */
263         dbprintf(_("amandates botch: %s lev %d: new dumpdate %ld old %ld\n"),
264                   qname, level, (long) dumpdate, (long) amdp->dates[level]);
265         amfree(qname);
266         return;
267     }
268
269     amdp->dates[level] = dumpdate;
270 }
271
272
273 void
274 amandates_updateone(
275     char *      name,
276     int         level,
277     time_t      dumpdate)
278 {
279     amandates_t *amdp;
280     char *qname;
281
282     assert(!readonly);
283
284     amdp = lookup(name, 1);
285
286     if(level < 0 || level >= DUMP_LEVELS || dumpdate < amdp->dates[level]) {
287         /* this is not allowed, but we can ignore it */
288         qname = quote_string(name);
289         dbprintf(_("amandates updateone: %s lev %d: new dumpdate %ld old %ld"),
290                   name, level, (long) dumpdate, (long) amdp->dates[level]);
291         amfree(qname);
292         return;
293     }
294
295     amdp->dates[level] = dumpdate;
296     updated = 1;
297 }
298
299
300 /* -------------------------- */
301
302 static void
303 import_dumpdates(
304     amandates_t *       amdp)
305 {
306     char *devname;
307     char *line;
308     char *fname;
309     int level = 0;
310     time_t dumpdate;
311     FILE *dumpdf;
312     char *s;
313     int ch;
314
315     devname = amname_to_devname(amdp->name);
316
317     if((dumpdf = fopen("/var/lib/dumpdates", "r")) == NULL) {
318         amfree(devname);
319         return;
320     }
321
322     for(; (line = agets(dumpdf)) != NULL; free(line)) {
323         if (line[0] == '\0')
324             continue;
325         s = line;
326         ch = *s++;
327
328         skip_whitespace(s, ch);
329         if(ch == '\0') {
330             continue;                           /* no fname field */
331         }
332         fname = s - 1;
333         skip_non_whitespace(s, ch);
334         s[-1] = '\0';                           /* terminate fname */
335
336         skip_whitespace(s, ch);
337         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
338             continue;                           /* no level field */
339         }
340         skip_integer(s, ch);
341
342         skip_whitespace(s, ch);
343         if(ch == '\0') {
344             continue;                           /* no dumpdate field */
345         }
346         dumpdate = unctime(s-1);
347
348         if(strcmp(fname, devname) != 0 || level < 0 || level >= DUMP_LEVELS) {
349             continue;
350         }
351
352         if(dumpdate != -1 && dumpdate > amdp->dates[level]) {
353             if(!readonly) updated = 1;
354             amdp->dates[level] = dumpdate;
355         }
356     }
357     afclose(dumpdf);
358     amfree(devname);
359 }