95146f3572f9a171bc9af53e921eb989ef2c13f9
[openafs.git] / src / bucoord / dump_sched.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * ALL RIGHTS RESERVED
12  */
13
14 #include <sys/types.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <afsconfig.h>
18 #include <afs/param.h>
19
20 #include <roken.h>
21
22 #ifdef AFS_NT40_ENV
23 #include <winsock2.h>
24 #else
25 #include <sys/socket.h>
26 #include <netinet/in.h>
27 #include <netdb.h>
28 #endif
29 #include <errno.h>
30 #include <afs/ktime.h>
31 #include <afs/budb_client.h>
32 #include <afs/cmd.h>
33 #include <afs/com_err.h>
34 #include <afs/bubasics.h>
35 #include "bc.h"
36 #include "error_macros.h"
37 #include "bucoord_internal.h"
38 #include "bucoord_prototypes.h"
39
40 /* code to manage dump schedules
41  * specific to the ubik database implementation
42  */
43
44 extern struct bc_config *bc_globalConfig;
45 extern struct udbHandleS udbHandle;
46 extern char *whoami;
47
48 static int ListDumpSchedule(struct bc_dumpSchedule *adump, int alevel);
49
50 /* ------------------------------------
51  * command level routines
52  * ------------------------------------
53  */
54
55 /* bc_AddDumpCmd
56  *      add a dump schedule
57  * params:
58  *      parm 0: list of dump names
59  *      parm 1: expiration date (list)
60  */
61
62 int
63 bc_AddDumpCmd(struct cmd_syndesc *as, void *arock)
64 {
65     char *dname;        /* dump schedule name */
66     int code;
67     afs_int32 expType, expDate;
68     struct cmd_item *ti;
69     udbClientTextP ctPtr;
70
71     /* if an expiration date has been specified */
72     if (as->parms[1].items) {
73         code = bc_ParseExpiration(&as->parms[1], &expType, &expDate);
74         if (code) {
75             printf("Invalid expiration date syntax\n");
76             return (1);
77         }
78     } else {
79         /* no expiration date specified */
80         expDate = 0;
81         expType = BC_NO_EXPDATE;
82     }
83
84     /* lock schedules and check validity */
85     ctPtr = &bc_globalConfig->configText[TB_DUMPSCHEDULE];
86
87     code = bc_LockText(ctPtr);
88     if (code)
89         ERROR(code);
90
91     code = bc_UpdateDumpSchedule();
92     if (code) {
93         afs_com_err(whoami, code, "; Can't retrieve dump schedule");
94         return (code);
95     }
96
97     /* process each dump name using the expiration date computed above */
98     for (ti = as->parms[0].items; ti != 0; ti = ti->next) {
99         /* get next dump name to process */
100         dname = ti->data;
101
102         /* validate the name dump name length */
103         if (strlen(dname) >= BU_MAX_DUMP_PATH) {
104             afs_com_err(whoami, 0, "Dump names must be < %d characters",
105                     BU_MAX_DUMP_PATH);
106             afs_com_err(whoami, 0, "Dump %s not added", dname);
107             code = -1;
108             continue;
109         }
110
111         code =
112             bc_CreateDumpSchedule(bc_globalConfig, dname, expDate, expType);
113         if (code) {
114             if (code == -1)
115                 afs_com_err(whoami, 0, "Dump already exists");
116             else if (code == -2)
117                 afs_com_err(whoami, 0, "Invalid path name '%s'", dname);
118             else if (code == -3)
119                 afs_com_err(whoami, 0, "Name specification error");
120             else
121                 afs_com_err(whoami, code, "; Failed to create dump schedule");
122             continue;
123         }
124
125         /* save the new schedule item */
126         code = bc_SaveDumpSchedule();
127         if (code) {
128             afs_com_err(whoami, code, "Cannot save dump schedule");
129             afs_com_err(whoami, 0,
130                     "Changes are temporary - for this session only");
131             break;
132         }
133
134         afs_com_err(whoami, 0, "Created new dump schedule %s", dname);
135     }
136
137   error_exit:
138     if (ctPtr->lockHandle)
139         bc_UnlockText(ctPtr);
140     return (code);
141 }
142
143
144 /* bc_DeleteDumpCmd
145  *      delete a dump schedule
146  */
147
148 int
149 bc_DeleteDumpCmd(struct cmd_syndesc *as, void *arock)
150 {
151     /* parm 0 is vol set name
152      * parm 1 is dump schedule name
153      */
154     char *dname;
155     int code;
156     udbClientTextP ctPtr;
157
158     /* lock schedules and check validity */
159     ctPtr = &bc_globalConfig->configText[TB_DUMPSCHEDULE];
160
161     code = bc_LockText(ctPtr);
162     if (code)
163         ERROR(code);
164
165     code = bc_UpdateDumpSchedule();
166     if (code) {
167         afs_com_err(whoami, code, "; Can't retrieve dump schedule");
168         return (code);
169     }
170
171     dname = as->parms[0].items->data;
172
173     code = bc_DeleteDumpSchedule(bc_globalConfig, dname);
174     if (code) {
175         if (code == -1)
176             afs_com_err(whoami, 0, "No such dump as %s", dname);
177         else
178             afs_com_err(whoami, code, "; Failed to delete dump schedule");
179         goto error_exit;
180     }
181
182     code = bc_SaveDumpSchedule();
183     if (code == 0)
184         printf("backup: deleted dump schedule %s\n", dname);
185     else {
186         afs_com_err(whoami, code, "Cannot save dump schedule file");
187         afs_com_err(whoami, 0, "Deletion is temporary - for this session only");
188     }
189
190   error_exit:
191     if (ctPtr->lockHandle != 0)
192         bc_UnlockText(ctPtr);
193     return code;
194 }
195
196 /* ListDumpSchedule
197  *      Print out the dump schedule tree whose root is adump. Alevel should
198  *      be passed in as 0, and is incremented for the recursive calls
199  * entry:
200  *      adump - ptr to the root node of a dump schedule
201  *      alevel - 0
202  */
203
204 static int
205 ListDumpSchedule(struct bc_dumpSchedule *adump, int alevel)
206 {
207     int i;
208     struct bc_dumpSchedule *child;
209
210     /* sanity check for loops */
211     if (alevel > 100) {
212         printf("backup: recursing listing dump schedule\n");
213         return -1;
214     }
215
216     /* move to appropriate indentation level */
217     for (i = 0; i < alevel; i++)
218         printf("    ");
219
220     /* name is a pathname style name, determine trailing name and only print
221      * it
222      */
223
224     printf("/%s ", tailCompPtr(adump->name));
225
226
227     /* list expiration time */
228     switch (adump->expType) {
229     case BC_ABS_EXPDATE:
230         /* absolute expiration date. Never expires if date is 0 */
231         if (adump->expDate) {
232             time_t t = adump->expDate;
233             printf("expires at %.24s", cTIME(&t));
234         }
235         break;
236
237     case BC_REL_EXPDATE:
238         {
239             struct ktime_date kt;
240
241             /* expiration date relative to the time that the dump is done */
242             LongTo_ktimeRelDate(adump->expDate, &kt);
243             printf(" expires in %s", RelDatetoString(&kt));
244         }
245         break;
246
247     default:
248         break;
249     }
250     printf("\n");
251     for (child = adump->firstChild; child; child = child->nextSibling)
252         ListDumpSchedule(child, alevel + 1);
253
254     return 0;
255 }
256
257 /* bc_ListDumpScheduleCmd
258  *      list the (internally held) dump schedule tree
259  * parameters:
260  *      ignored
261  */
262
263 int
264 bc_ListDumpScheduleCmd(struct cmd_syndesc *as, void *arock)
265 {
266     /* no parms */
267     int code;
268     struct bc_dumpSchedule *tdump;
269
270     /* first check to see if schedules must be updated */
271     code = bc_UpdateDumpSchedule();
272     if (code) {
273         afs_com_err(whoami, code, "; Can't retrieve dump schedule");
274         return (code);
275     }
276
277     /* go through entire list, displaying trees for root-level dump
278      * schedules
279      */
280     for (tdump = bc_globalConfig->dsched; tdump; tdump = tdump->next) {
281         /* if this is a root-level dump, show it and its kids */
282         if (!tdump->parent)
283             ListDumpSchedule(tdump, 0);
284     }
285     return 0;
286 }
287
288
289 /* bc_SetExpCmd
290  *      Set/clear expiration date on existing dump node
291  * params:
292  *      parm 0: list of dump names
293  *      parm 1: expiration date (list)
294  */
295
296 int
297 bc_SetExpCmd(struct cmd_syndesc *as, void *arock)
298 {
299     char *dname;        /* dump schedule name */
300     struct cmd_item *ti;
301     struct bc_dumpSchedule *node, *parent;
302     afs_int32 expType, expDate;
303     udbClientTextP ctPtr;
304     int code;
305
306     /* if an expiration date has been specified */
307     if (as->parms[1].items) {
308         code = bc_ParseExpiration(&as->parms[1], &expType, &expDate);
309         if (code) {
310             printf("Invalid expiration date syntax\n");
311             return (1);
312         }
313     } else {
314         /* no expiration date specified */
315         expDate = 0;
316         expType = BC_NO_EXPDATE;
317     }
318
319     /* lock schedules and check validity */
320     ctPtr = &bc_globalConfig->configText[TB_DUMPSCHEDULE];
321
322     code = bc_LockText(ctPtr);
323     if (code)
324         ERROR(code);
325
326     code = bc_UpdateDumpSchedule();
327     if (code) {
328         afs_com_err(whoami, code, "; Can't retrieve dump schedule");
329         return (code);
330     }
331
332     /* process each dump name using the expiration date computed above */
333     for (ti = as->parms[0].items; ti != 0; ti = ti->next) {
334         /* get next dump name to process */
335         dname = ti->data;
336
337         /* validate the name dump name length */
338         if (strlen(dname) >= BU_MAX_DUMP_PATH) {
339             code = -1;
340             afs_com_err(whoami, 0, "Dump names must be < %d characters",
341                     BU_MAX_DUMP_PATH);
342             afs_com_err(whoami, 0, "Dump %s not added", dname);
343             continue;
344         }
345
346         code = FindDump(bc_globalConfig, dname, &parent, &node);
347         if (code) {
348             afs_com_err(whoami, 0, "Dump level %s not found", dname);
349             continue;
350         }
351
352         node->expDate = expDate;
353         node->expType = expType;
354     }
355
356     code = bc_SaveDumpSchedule();
357     if (code) {
358         afs_com_err(whoami, code, "Cannot save dump schedule");
359         afs_com_err(whoami, 0,
360                 "Expiration changes effective for this session only");
361     }
362
363   error_exit:
364     if (ctPtr->lockHandle)
365         bc_UnlockText(ctPtr);
366     return (code);
367 }
368
369
370
371 /* ------------------------------------
372  * general dump schedule handling routines
373  * ------------------------------------
374  */
375
376 int
377 bc_ParseDumpSchedule(void)
378 {
379     char tbuffer[1024];
380     char dsname[256], period[64];
381     char *tp;
382     afs_int32 code;
383     udbClientTextP ctPtr;
384     struct bc_dumpSchedule *tds;
385     struct bc_dumpSchedule **ppds, *pds;
386     afs_int32 expDate, expType;
387
388     FILE *stream;
389
390     /* initialize locally used variables */
391     ctPtr = &bc_globalConfig->configText[TB_DUMPSCHEDULE];
392     stream = ctPtr->textStream;
393
394     if (ctPtr->textSize == 0)   /* nothing defined yet */
395         return (0);
396
397     if (stream == NULL)
398         return (BC_INTERNALERROR);
399
400     rewind(stream);
401
402     /* check the magic number and version */
403     tp = fgets(tbuffer, sizeof(tbuffer), stream);
404     if (tp == 0)
405         /* can't read first line - error */
406         return (BC_INTERNALERROR);
407     else {
408         afs_int32 dsmagic, dsversion;
409
410         /* read the first line, and then check magic # and version */
411
412         code = sscanf(tbuffer, "%d %d", &dsmagic, &dsversion);
413         if ((code != 2)
414             || (dsmagic != BC_SCHEDULE_MAGIC)
415             || (dsversion != BC_SCHEDULE_VERSION)
416             ) {
417             /* invalid or unexpected header - error */
418             afs_com_err(whoami, 0, "Unable to understand dump schedule file");
419             return (BC_INTERNALERROR);
420         }
421     }
422
423     while (1) {
424         /* read all of the lines out */
425         tp = fgets(tbuffer, sizeof(tbuffer), stream);
426         if (tp == 0)
427             break;              /* hit eof? */
428         code =
429             sscanf(tbuffer, "%s %s %d %d", dsname, period, &expDate,
430                    &expType);
431         if (code != 4) {
432             afs_com_err(whoami, 0,
433                     "Syntax error in dump schedule file, line is: %s",
434                     tbuffer);
435             return (BC_INTERNALERROR);
436         }
437         tds =
438             (struct bc_dumpSchedule *)malloc(sizeof(struct bc_dumpSchedule));
439         memset(tds, 0, sizeof(*tds));
440
441         tds->next = (struct bc_dumpSchedule *)0;
442         tds->name = (char *)malloc(strlen(dsname) + 1);
443         strcpy(tds->name, dsname);
444
445         tds->expDate = expDate;
446         tds->expType = expType;
447
448         /* find the end of the schedule list, and append the new item to it */
449         ppds = &bc_globalConfig->dsched;
450         pds = *ppds;
451         while (pds != 0) {
452             ppds = &pds->next;
453             pds = *ppds;
454         }
455         *ppds = tds;
456     }
457     return 0;
458 }
459
460 int
461 bc_SaveDumpSchedule(void)
462 {
463     struct bc_dumpSchedule *tdump;
464     udbClientTextP ctPtr;
465     afs_int32 code = 0;
466
467     extern struct bc_config *bc_globalConfig;
468
469     /* setup the right ptr */
470     ctPtr = &bc_globalConfig->configText[TB_DUMPSCHEDULE];
471
472     /* must be locked */
473     if (ctPtr->lockHandle == 0)
474         return (BC_INTERNALERROR);
475
476     /* truncate the file */
477     code = ftruncate(fileno(ctPtr->textStream), 0);
478     if (code)
479         ERROR(errno);
480
481     rewind(ctPtr->textStream);
482
483     /* write the new information */
484     fprintf(ctPtr->textStream, "%d %d\n", BC_SCHEDULE_MAGIC,
485             BC_SCHEDULE_VERSION);
486
487     for (tdump = bc_globalConfig->dsched; tdump; tdump = tdump->next) {
488         fprintf(ctPtr->textStream, "%s %s %d %d\n", tdump->name, "any",
489                 tdump->expDate, tdump->expType);
490     }
491
492     if (ferror(ctPtr->textStream))
493         return (BC_INTERNALERROR);
494
495     fflush(ctPtr->textStream);  /* debug */
496
497     /* send to server */
498     code = bcdb_SaveTextFile(ctPtr);
499     if (code)
500         ERROR(code);
501
502     /* increment local version number */
503     ctPtr->textVersion++;
504
505     /* update locally stored file size */
506     ctPtr->textSize = filesize(ctPtr->textStream);
507   error_exit:
508     return (code);
509 }
510
511
512 /* ------------------------------------
513  * misc. support routines - specific to dump schedules
514  * ------------------------------------
515  */
516
517 afs_int32
518 bc_UpdateDumpSchedule(void)
519 {
520     struct bc_dumpSchedule *dumpPtr, *nextDumpPtr;
521     struct udbHandleS *uhptr = &udbHandle;
522     udbClientTextP ctPtr;
523     afs_int32 code;
524     int lock = 0;
525
526     /* lock schedules and check validity */
527     ctPtr = &bc_globalConfig->configText[TB_DUMPSCHEDULE];
528
529     code = bc_CheckTextVersion(ctPtr);
530     if (code != BC_VERSIONMISMATCH) {
531         ERROR(code);            /* Version matches or some other error */
532     }
533
534     /* Must update the dump schedules */
535     /* If we are not already locked, then lock it now */
536     if (!ctPtr->lockHandle) {
537         code = bc_LockText(ctPtr);
538         if (code)
539             ERROR(code);
540         lock = 1;
541     }
542
543     if (ctPtr->textVersion != -1) {
544         printf("backup: obsolete dump schedule - updating\n");
545
546         /* clear all old schedule information */
547         dumpPtr = bc_globalConfig->dsched;
548         while (dumpPtr) {
549             nextDumpPtr = dumpPtr->next;
550             free(dumpPtr);
551             dumpPtr = nextDumpPtr;
552         }
553         bc_globalConfig->dsched = 0;;
554     }
555
556     /* open a temp file to store the config text received from buserver *
557      * The open file stream is stored in ctPtr->textStream */
558     code =
559         bc_openTextFile(ctPtr,
560                         &bc_globalConfig->
561                         tmpTextFileNames[TB_DUMPSCHEDULE][0]);
562     if (code)
563         ERROR(code);
564     /* now get a fresh set of information from the database */
565     code = bcdb_GetTextFile(ctPtr);
566     if (code)
567         ERROR(code);
568
569     /* fetch the version number */
570     code =
571         ubik_BUDB_GetTextVersion(uhptr->uh_client, 0, ctPtr->textType,
572                   &ctPtr->textVersion);
573     if (code)
574         ERROR(code);
575
576     /* parse the file */
577     code = bc_ParseDumpSchedule();
578     if (code)
579         ERROR(code);
580
581     /* rebuild the tree */
582     code = bc_ProcessDumpSchedule(bc_globalConfig);
583     if (code)
584         ERROR(code);
585
586   error_exit:
587     if (lock && ctPtr->lockHandle)
588         bc_UnlockText(ctPtr);
589     return (code);
590 }
591