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