2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
16 #include <afs/budb_client.h>
18 #include <afs/com_err.h>
19 #include <afs/bubasics.h>
22 #include "error_macros.h"
23 #include "bucoord_internal.h"
24 #include "bucoord_prototypes.h"
26 /* code to manage volumesets
27 * specific to the ubik database implementation
30 extern struct bc_config
*bc_globalConfig
;
31 extern struct udbHandleS udbHandle
;
34 static int ListVolSet(struct bc_volumeSet
*aset
);
36 /* ------------------------------------
37 * command level routines
38 * ------------------------------------
43 * add a volume (or volume expression) to a volume set
45 * parm 0 is vol set name.
46 * parm 1 is server name
47 * parm 2 is partition name
48 * parm 3 is volume regexp
52 bc_AddVolEntryCmd(struct cmd_syndesc
*as
, void *arock
)
55 char *volSetName
, *serverName
, *partitionName
, *volRegExp
;
57 struct bc_volumeSet
*tset
;
59 volSetName
= as
->parms
[0].items
->data
;
60 serverName
= as
->parms
[1].items
->data
;
61 partitionName
= as
->parms
[2].items
->data
;
62 volRegExp
= as
->parms
[3].items
->data
;
64 code
= bc_UpdateVolumeSet();
66 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
70 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
72 tset
= bc_FindVolumeSet(bc_globalConfig
, volSetName
);
74 afs_com_err(whoami
, code
, "; Volume entry not added");
78 if (!(tset
->flags
& VSFLAG_TEMPORARY
)) {
79 code
= bc_LockText(ctPtr
);
84 code
= bc_UpdateVolumeSet();
86 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
91 bc_AddVolumeItem(bc_globalConfig
, volSetName
, serverName
,
92 partitionName
, volRegExp
);
94 afs_com_err(whoami
, code
, "; Volume entry not added");
98 if (!(tset
->flags
& VSFLAG_TEMPORARY
)) {
99 code
= bc_SaveVolumeSet();
101 afs_com_err(whoami
, code
, "Cannot save volume set file");
102 afs_com_err(whoami
, 0,
103 "Changes are temporary - for this session only");
108 if (ctPtr
->lockHandle
)
109 bc_UnlockText(ctPtr
);
116 * create a new volume set, writing out the new volumeset
117 * file in a safe manner
119 * name of new volume set
123 bc_AddVolSetCmd(struct cmd_syndesc
*as
, void *arock
)
125 /* parm 0 is vol set name */
128 udbClientTextP ctPtr
;
131 /* lock schedules and check validity */
132 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
134 flags
= (as
->parms
[1].items
? VSFLAG_TEMPORARY
: 0);
136 /* Don't lock if adding a temporary volumeset */
137 if (!(flags
& VSFLAG_TEMPORARY
)) {
138 code
= bc_LockText(ctPtr
);
143 code
= bc_UpdateVolumeSet();
145 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
149 /* validate size of volumeset name */
151 bc_CreateVolumeSet(bc_globalConfig
, (ti
= as
->parms
[0].items
)->data
,
155 afs_com_err(whoami
, 0, "Volume set '%s' already exists", ti
->data
);
157 afs_com_err(whoami
, 0, "Unknown problem");
158 } else if (!(flags
& VSFLAG_TEMPORARY
)) {
159 code
= bc_SaveVolumeSet();
161 afs_com_err(whoami
, code
, "Cannot save new volume set file");
162 afs_com_err(whoami
, 0,
163 "Changes are temporary - for this session only");
168 if (ctPtr
->lockHandle
!= 0)
169 bc_UnlockText(ctPtr
);
174 /* bc_DeleteVolEntryCmd
175 * delete a volume specification from a volume set
177 * parm 0 is vol set name
178 * parm 1 is entry # (integer, 1 based)
182 bc_DeleteVolEntryCmd(struct cmd_syndesc
*as
, void *arock
)
187 udbClientTextP ctPtr
;
188 struct bc_volumeSet
*tset
;
190 vsname
= as
->parms
[0].items
->data
;
192 code
= bc_UpdateVolumeSet();
194 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
198 /* lock schedules and check validity */
199 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
201 tset
= bc_FindVolumeSet(bc_globalConfig
, vsname
);
203 afs_com_err(whoami
, 0, "No such volume set as '%s'", vsname
);
207 if (!(tset
->flags
& VSFLAG_TEMPORARY
)) {
208 code
= bc_LockText(ctPtr
);
213 code
= bc_UpdateVolumeSet();
215 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
219 entry
= bc_SafeATOI(as
->parms
[1].items
->data
);
221 afs_com_err(whoami
, 0, "Can't parse entry number '%s' as decimal integer",
222 as
->parms
[1].items
->data
);
226 code
= bc_DeleteVolumeItem(bc_globalConfig
, vsname
, entry
);
229 afs_com_err(whoami
, 0, "No such volume set as '%s'", vsname
);
230 } else if (code
== -2) {
231 afs_com_err(whoami
, 0,
232 "There aren't %d volume items for this volume set",
234 afs_com_err(whoami
, 0,
235 "Use the 'listvolsets' command to examine the volume set");
240 if (!(tset
->flags
& VSFLAG_TEMPORARY
)) {
241 code
= bc_SaveVolumeSet();
243 printf("backup: deleted volume entry %d from volume set %s\n",
246 afs_com_err(whoami
, code
, "Cannot save volume set file");
247 afs_com_err(whoami
, 0,
248 "Deletion is temporary - for this session only");
253 if (ctPtr
->lockHandle
!= 0)
254 bc_UnlockText(ctPtr
);
261 /* bc_DeleteVolSetCmd
262 * delete a volume set, writing out a new configuration file when
265 * name of volumeset to delete
269 bc_DeleteVolSetCmd(struct cmd_syndesc
*as
, void *arock
)
271 /* parm 0 is vol set name */
274 udbClientTextP ctPtr
;
276 afs_int32 flags
, tosave
= 0;
278 /* lock schedules and check validity */
279 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
281 code
= bc_LockText(ctPtr
);
285 code
= bc_UpdateVolumeSet();
287 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
291 for (ti
= as
->parms
[0].items
; ti
; ti
= ti
->next
) {
292 code
= bc_DeleteVolumeSet(bc_globalConfig
, ti
->data
, &flags
);
295 afs_com_err(whoami
, 0, "Can't find volume set '%s'", ti
->data
);
297 afs_com_err(whoami
, code
,
298 "; Unknown problem deleting volume set '%s'",
301 if (!(flags
& VSFLAG_TEMPORARY
))
303 printf("backup: deleted volume set '%s'\n", ti
->data
);
307 /* now write out the file */
309 c
= bc_SaveVolumeSet();
313 afs_com_err(whoami
, c
, "Cannot save updated volume set file");
314 afs_com_err(whoami
, 0, "Deletion effective for this session only");
320 if (ctPtr
->lockHandle
)
321 bc_UnlockText(ctPtr
);
327 ListVolSet(struct bc_volumeSet
*aset
)
329 struct bc_volumeEntry
*tentry
;
332 printf("Volume set %s", aset
->name
);
333 if (aset
->flags
& VSFLAG_TEMPORARY
)
334 printf(" (temporary)");
337 for (tentry
= aset
->ventries
; tentry
; tentry
= tentry
->next
, i
++) {
338 printf(" Entry %3d: server %s, partition %s, volumes: %s\n", i
,
339 tentry
->serverName
, tentry
->partname
, tentry
->name
);
345 * list out all the information (?) about a volumeset or about all
348 * optional parameter specifies a volumeset name
352 bc_ListVolSetCmd(struct cmd_syndesc
*as
, void *arock
)
354 /* parm 0 is optional volume set to display */
355 struct bc_volumeSet
*tset
;
359 code
= bc_UpdateVolumeSet();
361 afs_com_err(whoami
, code
, "; Can't retrieve volume sets");
365 /* figure out volume set to list */
366 if ((ti
= as
->parms
[0].items
)) {
367 /* for each volume set in the command item list */
368 for (; ti
; ti
= ti
->next
) {
369 tset
= bc_FindVolumeSet(bc_globalConfig
, ti
->data
);
374 afs_com_err(whoami
, 0, "Can't find volume set '%s'", ti
->data
);
379 /* no command parameters specified, show entire list */
380 for (tset
= bc_globalConfig
->vset
; tset
; tset
= tset
->next
) {
391 /* ------------------------------------
393 * ------------------------------------
397 bc_ClearVolumeSets(void)
399 struct bc_volumeSet
*vsPtr
, *vsNextPtr
, **vsPrev
;
401 extern struct bc_config
*bc_globalConfig
;
403 vsPrev
= &(bc_globalConfig
->vset
);
404 for (vsPtr
= bc_globalConfig
->vset
; vsPtr
; vsPtr
= vsNextPtr
) {
405 vsNextPtr
= vsPtr
->next
;
407 if (vsPtr
->flags
& VSFLAG_TEMPORARY
) { /* Skip temporary volumeSet */
408 vsPrev
= &(vsPtr
->next
);
412 *vsPrev
= vsPtr
->next
; /* Remove volumeSet from the chain */
413 FreeVolumeSet(vsPtr
);
419 * Open up the volume set configuration file as specified in our argument,
420 * then parse the file to set up our internal representation.
422 * 0 on successful processing,
427 bc_ParseVolumeSet(void)
429 char tbuffer
[1024]; /*Buffer for reading config file */
430 char vsname
[256]; /*Volume set name */
431 char serverName
[256]; /*Server name */
432 char partName
[256]; /*Partition name */
433 struct bc_volumeEntry
*tve
; /*Ptr to generated volume spec struct */
434 struct bc_volumeSet
*tvs
= NULL
; /*Ptr to volume set struct */
435 struct bc_volumeEntry
**ppve
, *pve
;
436 struct bc_volumeSet
**ppvs
, *pvs
;
437 afs_int32 code
; /*Generalized return code */
438 char *tp
; /*Result of fgets(), malloc() */
439 int readHeader
; /*Is next thing to read a volume set hdr? */
441 udbClientTextP ctPtr
;
444 extern struct bc_config
*bc_globalConfig
;
446 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
447 stream
= ctPtr
->textStream
;
450 * Open up the volume set configuration file, fail if it can't be done.
453 if (ctPtr
->textSize
== 0) /* empty is ok */
456 /* stream checks and initialization */
458 return (BC_INTERNALERROR
);
464 /* Read in and process the next line of the volume set description
467 tp
= fgets(tbuffer
, sizeof(tbuffer
), stream
);
470 if (readHeader
) { /*r */
472 * Scan a header entry.
475 code
= sscanf(tbuffer
, "%s %s", serverName
, vsname
);
477 || (strcmp(serverName
, "volumeset") != 0)
479 afs_com_err(whoami
, 0, "Bad volume header line: '%s'", tbuffer
);
483 /* Create and fill in the volume set descriptor structure from
484 * the info just read placing it at the head of its queue in the
485 * global configuration structure.
487 tvs
= calloc(1, sizeof(struct bc_volumeSet
));
488 tvs
->name
= strdup(vsname
);
490 /* append to the end */
491 for (ppvs
= &bc_globalConfig
->vset
, pvs
= *ppvs
; pvs
;
492 ppvs
= &pvs
->next
, pvs
= *ppvs
);
494 tvs
->next
= (struct bc_volumeSet
*)0;
497 /* Scan a volume name entry, which contains the server name,
498 * partition pattern, and volume pattern.
500 code
= sscanf(tbuffer
, "%s %s %s", serverName
, partName
, vsname
);
501 if (code
== 1 && strcmp(serverName
, "end") == 0) {
502 /* This was really a line delimiting the current volume set.
503 * Next time around, we should try to read a header.
509 /* The line just read in is a volume spec. Create a new volume
510 * spec record, then get the rest of the information regarding
511 * the host, and stuff everything into place.
513 tve
= calloc(1, sizeof(struct bc_volumeEntry
));
515 afs_com_err(whoami
, 0,
516 "Can't malloc() a new volume spec record!");
519 if (bc_ParseHost(serverName
, &(tve
->server
)))
520 afs_com_err(whoami
, 0, "Can't get required info on host '%s'",
523 /* The above code has filled in the server sockaddr, now fill in
524 * the rest of the fields.
526 tve
->serverName
= strdup(serverName
);
527 if (!tve
->serverName
) {
528 afs_com_err(whoami
, 0,
529 "Can't malloc() a new volume spec server name field!");
532 tve
->partname
= strdup(partName
);
533 if (!tve
->partname
) {
534 afs_com_err(whoami
, 0,
535 "Can't malloc() a new volume spec partition pattern field!");
538 code
= bc_GetPartitionID(partName
, &tve
->partition
);
540 afs_com_err(whoami
, 0, "Can't parse partition '%s'", partName
);
545 afs_com_err(whoami
, 0,
546 "Can't malloc() a new volume spec volume pattern field!");
551 /* Now, thread it onto the list of other volume spec entries for
552 * the current volume set.
555 for (ppve
= &tvs
->ventries
, pve
= *ppve
; pve
;
556 ppve
= &pve
->next
, pve
= *ppve
);
558 tve
->next
= (struct bc_volumeEntry
*)0;
562 /* If we hit an EOF in the middle of a volume set record, we bitch and
569 * Well, we did it. Return successfully.
573 } /*bc_ParseVolumeSet */
576 * save the current volume set information to disk
580 bc_SaveVolumeSet(void)
583 struct bc_volumeSet
*tset
;
584 struct bc_volumeEntry
*tentry
;
586 udbClientTextP ctPtr
;
589 extern struct bc_config
*bc_globalConfig
;
591 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
592 stream
= ctPtr
->textStream
;
595 if (ctPtr
->lockHandle
== 0)
596 return (BC_INTERNALERROR
);
598 /* truncate the file */
599 code
= ftruncate(fileno(stream
), 0);
605 /* now write the volumeset information */
607 for (tset
= bc_globalConfig
->vset
; tset
!= 0; tset
= tset
->next
) {
608 if (tset
->flags
& VSFLAG_TEMPORARY
)
609 continue; /* skip temporary entries */
611 fprintf(stream
, "volumeset %s\n", tset
->name
);
612 for (tentry
= tset
->ventries
; tentry
; tentry
= tentry
->next
) {
613 fprintf(stream
, "%s %s %s\n", tentry
->serverName
,
614 tentry
->partname
, tentry
->name
);
616 fprintf(stream
, "end\n");
620 return (BC_INTERNALERROR
);
623 code
= bcdb_SaveTextFile(ctPtr
);
627 /* do this on bcdb_SaveTextFile */
628 /* increment local version number */
629 ctPtr
->textVersion
++;
631 /* update locally stored file size */
632 ctPtr
->textSize
= filesize(ctPtr
->textStream
);
639 bc_UpdateVolumeSet(void)
641 struct udbHandleS
*uhptr
= &udbHandle
;
642 udbClientTextP ctPtr
;
646 /* lock schedules and check validity */
647 ctPtr
= &bc_globalConfig
->configText
[TB_VOLUMESET
];
649 /* Don't need a lock to check the version */
650 code
= bc_CheckTextVersion(ctPtr
);
651 if (code
!= BC_VERSIONMISMATCH
) {
652 ERROR(code
); /* version matches or some other error */
655 /* Must update the volume sets */
656 /* If no lock alredy taken, then lock it */
657 if (ctPtr
->lockHandle
== 0) {
658 code
= bc_LockText(ctPtr
);
664 if (ctPtr
->textVersion
!= -1) {
665 printf("backup: obsolete volumesets - updating\n");
666 bc_ClearVolumeSets();
669 /* open a temp file to store the config text received from buserver *
670 * The open file stream is stored in ctPtr->textStream */
672 bc_openTextFile(ctPtr
,
673 &bc_globalConfig
->tmpTextFileNames
[TB_VOLUMESET
][0]);
676 /* now get a fresh set of information from the database */
677 code
= bcdb_GetTextFile(ctPtr
);
681 /* fetch the version number */
683 ubik_BUDB_GetTextVersion(uhptr
->uh_client
, 0, ctPtr
->textType
,
684 &ctPtr
->textVersion
);
689 code
= bc_ParseVolumeSet();
694 if (lock
&& ctPtr
->lockHandle
)
695 bc_UnlockText(ctPtr
);