Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / bucoord / vol_sets.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 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <afs/budb_client.h>
17 #include <afs/cmd.h>
18 #include <afs/com_err.h>
19 #include <afs/bubasics.h>
20
21 #include "bc.h"
22 #include "error_macros.h"
23 #include "bucoord_internal.h"
24 #include "bucoord_prototypes.h"
25
26 /* code to manage volumesets
27 * specific to the ubik database implementation
28 */
29
30 extern struct bc_config *bc_globalConfig;
31 extern struct udbHandleS udbHandle;
32 extern char *whoami;
33
34 static int ListVolSet(struct bc_volumeSet *aset);
35
36 /* ------------------------------------
37 * command level routines
38 * ------------------------------------
39 */
40
41
42 /* bc_AddVolEntryCmd
43 * add a volume (or volume expression) to a volume set
44 * params:
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
49 */
50
51 int
52 bc_AddVolEntryCmd(struct cmd_syndesc *as, void *arock)
53 {
54 int code;
55 char *volSetName, *serverName, *partitionName, *volRegExp;
56 udbClientTextP ctPtr;
57 struct bc_volumeSet *tset;
58
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;
63
64 code = bc_UpdateVolumeSet();
65 if (code) {
66 afs_com_err(whoami, code, "; Can't retrieve volume sets");
67 return (code);
68 }
69
70 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
71
72 tset = bc_FindVolumeSet(bc_globalConfig, volSetName);
73 if (!tset) {
74 afs_com_err(whoami, code, "; Volume entry not added");
75 ERROR(code);
76 }
77
78 if (!(tset->flags & VSFLAG_TEMPORARY)) {
79 code = bc_LockText(ctPtr);
80 if (code)
81 ERROR(code);
82 }
83
84 code = bc_UpdateVolumeSet();
85 if (code) {
86 afs_com_err(whoami, code, "; Can't retrieve volume sets");
87 return (code);
88 }
89
90 code =
91 bc_AddVolumeItem(bc_globalConfig, volSetName, serverName,
92 partitionName, volRegExp);
93 if (code) {
94 afs_com_err(whoami, code, "; Volume entry not added");
95 ERROR(code);
96 }
97
98 if (!(tset->flags & VSFLAG_TEMPORARY)) {
99 code = bc_SaveVolumeSet();
100 if (code) {
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");
104 }
105 }
106
107 error_exit:
108 if (ctPtr->lockHandle)
109 bc_UnlockText(ctPtr);
110 return (code);
111 }
112
113
114
115 /* bc_AddVolSetCmd
116 * create a new volume set, writing out the new volumeset
117 * file in a safe manner
118 * params:
119 * name of new volume set
120 */
121
122 int
123 bc_AddVolSetCmd(struct cmd_syndesc *as, void *arock)
124 {
125 /* parm 0 is vol set name */
126 int code;
127 struct cmd_item *ti;
128 udbClientTextP ctPtr;
129 afs_int32 flags;
130
131 /* lock schedules and check validity */
132 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
133
134 flags = (as->parms[1].items ? VSFLAG_TEMPORARY : 0);
135
136 /* Don't lock if adding a temporary volumeset */
137 if (!(flags & VSFLAG_TEMPORARY)) {
138 code = bc_LockText(ctPtr);
139 if (code)
140 ERROR(code);
141 }
142
143 code = bc_UpdateVolumeSet();
144 if (code) {
145 afs_com_err(whoami, code, "; Can't retrieve volume sets");
146 return (code);
147 }
148
149 /* validate size of volumeset name */
150 code =
151 bc_CreateVolumeSet(bc_globalConfig, (ti = as->parms[0].items)->data,
152 flags);
153 if (code) {
154 if (code == -1)
155 afs_com_err(whoami, 0, "Volume set '%s' already exists", ti->data);
156 else
157 afs_com_err(whoami, 0, "Unknown problem");
158 } else if (!(flags & VSFLAG_TEMPORARY)) {
159 code = bc_SaveVolumeSet();
160 if (code) {
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");
164 }
165 }
166
167 error_exit:
168 if (ctPtr->lockHandle != 0)
169 bc_UnlockText(ctPtr);
170 return (code);
171 }
172
173
174 /* bc_DeleteVolEntryCmd
175 * delete a volume specification from a volume set
176 * params:
177 * parm 0 is vol set name
178 * parm 1 is entry # (integer, 1 based)
179 */
180
181 int
182 bc_DeleteVolEntryCmd(struct cmd_syndesc *as, void *arock)
183 {
184 int code;
185 afs_int32 entry;
186 char *vsname;
187 udbClientTextP ctPtr;
188 struct bc_volumeSet *tset;
189
190 vsname = as->parms[0].items->data;
191
192 code = bc_UpdateVolumeSet();
193 if (code) {
194 afs_com_err(whoami, code, "; Can't retrieve volume sets");
195 return (code);
196 }
197
198 /* lock schedules and check validity */
199 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
200
201 tset = bc_FindVolumeSet(bc_globalConfig, vsname);
202 if (!tset) {
203 afs_com_err(whoami, 0, "No such volume set as '%s'", vsname);
204 ERROR(code);
205 }
206
207 if (!(tset->flags & VSFLAG_TEMPORARY)) {
208 code = bc_LockText(ctPtr);
209 if (code)
210 ERROR(code);
211 }
212
213 code = bc_UpdateVolumeSet();
214 if (code) {
215 afs_com_err(whoami, code, "; Can't retrieve volume sets");
216 return (code);
217 }
218
219 entry = bc_SafeATOI(as->parms[1].items->data);
220 if (entry < 0) {
221 afs_com_err(whoami, 0, "Can't parse entry number '%s' as decimal integer",
222 as->parms[1].items->data);
223 ERROR(BC_BADARG);
224 }
225
226 code = bc_DeleteVolumeItem(bc_globalConfig, vsname, entry);
227 if (code) {
228 if (code == -1) {
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",
233 entry);
234 afs_com_err(whoami, 0,
235 "Use the 'listvolsets' command to examine the volume set");
236 }
237 ERROR(code);
238 }
239
240 if (!(tset->flags & VSFLAG_TEMPORARY)) {
241 code = bc_SaveVolumeSet();
242 if (code == 0) {
243 printf("backup: deleted volume entry %d from volume set %s\n",
244 entry, vsname);
245 } else {
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");
249 }
250 }
251
252 error_exit:
253 if (ctPtr->lockHandle != 0)
254 bc_UnlockText(ctPtr);
255 return (code);
256 }
257
258
259
260
261 /* bc_DeleteVolSetCmd
262 * delete a volume set, writing out a new configuration file when
263 * completed
264 * params:
265 * name of volumeset to delete
266 */
267
268 int
269 bc_DeleteVolSetCmd(struct cmd_syndesc *as, void *arock)
270 {
271 /* parm 0 is vol set name */
272 int code;
273 struct cmd_item *ti;
274 udbClientTextP ctPtr;
275 afs_int32 c;
276 afs_int32 flags, tosave = 0;
277
278 /* lock schedules and check validity */
279 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
280
281 code = bc_LockText(ctPtr);
282 if (code)
283 ERROR(code);
284
285 code = bc_UpdateVolumeSet();
286 if (code) {
287 afs_com_err(whoami, code, "; Can't retrieve volume sets");
288 return (code);
289 }
290
291 for (ti = as->parms[0].items; ti; ti = ti->next) {
292 code = bc_DeleteVolumeSet(bc_globalConfig, ti->data, &flags);
293 if (code) {
294 if (code == -1)
295 afs_com_err(whoami, 0, "Can't find volume set '%s'", ti->data);
296 else
297 afs_com_err(whoami, code,
298 "; Unknown problem deleting volume set '%s'",
299 ti->data);
300 } else {
301 if (!(flags & VSFLAG_TEMPORARY))
302 tosave = 1;
303 printf("backup: deleted volume set '%s'\n", ti->data);
304 }
305 }
306
307 /* now write out the file */
308 if (tosave) {
309 c = bc_SaveVolumeSet();
310 if (c) {
311 if (!code)
312 code = c;
313 afs_com_err(whoami, c, "Cannot save updated volume set file");
314 afs_com_err(whoami, 0, "Deletion effective for this session only");
315 }
316
317 }
318
319 error_exit:
320 if (ctPtr->lockHandle)
321 bc_UnlockText(ctPtr);
322 return (code);
323 }
324
325
326 static int
327 ListVolSet(struct bc_volumeSet *aset)
328 {
329 struct bc_volumeEntry *tentry;
330 int i;
331
332 printf("Volume set %s", aset->name);
333 if (aset->flags & VSFLAG_TEMPORARY)
334 printf(" (temporary)");
335 printf(":\n");
336 i = 1;
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);
340 }
341 return 0;
342 }
343
344 /* bc_ListVolSetCmd
345 * list out all the information (?) about a volumeset or about all
346 * volumesets
347 * entry:
348 * optional parameter specifies a volumeset name
349 */
350
351 int
352 bc_ListVolSetCmd(struct cmd_syndesc *as, void *arock)
353 {
354 /* parm 0 is optional volume set to display */
355 struct bc_volumeSet *tset;
356 struct cmd_item *ti;
357 int code = 0;
358
359 code = bc_UpdateVolumeSet();
360 if (code) {
361 afs_com_err(whoami, code, "; Can't retrieve volume sets");
362 return (code);
363 }
364
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);
370 if (tset) {
371 ListVolSet(tset);
372 printf("\n");
373 } else {
374 afs_com_err(whoami, 0, "Can't find volume set '%s'", ti->data);
375 code = 1;
376 }
377 }
378 } else {
379 /* no command parameters specified, show entire list */
380 for (tset = bc_globalConfig->vset; tset; tset = tset->next) {
381 ListVolSet(tset);
382 printf("\n");
383 }
384 }
385
386 return code;
387 }
388
389
390
391 /* ------------------------------------
392 * support routines
393 * ------------------------------------
394 */
395
396 int
397 bc_ClearVolumeSets(void)
398 {
399 struct bc_volumeSet *vsPtr, *vsNextPtr, **vsPrev;
400
401 extern struct bc_config *bc_globalConfig;
402
403 vsPrev = &(bc_globalConfig->vset);
404 for (vsPtr = bc_globalConfig->vset; vsPtr; vsPtr = vsNextPtr) {
405 vsNextPtr = vsPtr->next;
406
407 if (vsPtr->flags & VSFLAG_TEMPORARY) { /* Skip temporary volumeSet */
408 vsPrev = &(vsPtr->next);
409 continue;
410 }
411
412 *vsPrev = vsPtr->next; /* Remove volumeSet from the chain */
413 FreeVolumeSet(vsPtr);
414 }
415 return (0);
416 }
417
418 /* bc_ParseVolumeSet
419 * Open up the volume set configuration file as specified in our argument,
420 * then parse the file to set up our internal representation.
421 * exit:
422 * 0 on successful processing,
423 * -1 otherwise.
424 */
425
426 int
427 bc_ParseVolumeSet(void)
428 {
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? */
440
441 udbClientTextP ctPtr;
442 FILE *stream;
443
444 extern struct bc_config *bc_globalConfig;
445
446 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
447 stream = ctPtr->textStream;
448
449 /*
450 * Open up the volume set configuration file, fail if it can't be done.
451 */
452
453 if (ctPtr->textSize == 0) /* empty is ok */
454 return (0);
455
456 /* stream checks and initialization */
457 if (stream == NULL)
458 return (BC_INTERNALERROR);
459
460 rewind(stream);
461
462 readHeader = 1;
463 while (1) {
464 /* Read in and process the next line of the volume set description
465 * file.
466 */
467 tp = fgets(tbuffer, sizeof(tbuffer), stream);
468 if (!tp)
469 break;
470 if (readHeader) { /*r */
471 /*
472 * Scan a header entry.
473 */
474 readHeader = 0;
475 code = sscanf(tbuffer, "%s %s", serverName, vsname);
476 if ((code != 2)
477 || (strcmp(serverName, "volumeset") != 0)
478 ) {
479 afs_com_err(whoami, 0, "Bad volume header line: '%s'", tbuffer);
480 return (-1);
481 }
482
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.
486 */
487 tvs = calloc(1, sizeof(struct bc_volumeSet));
488 tvs->name = strdup(vsname);
489
490 /* append to the end */
491 for (ppvs = &bc_globalConfig->vset, pvs = *ppvs; pvs;
492 ppvs = &pvs->next, pvs = *ppvs);
493 *ppvs = tvs;
494 tvs->next = (struct bc_volumeSet *)0;
495 } /*r */
496 else { /*e */
497 /* Scan a volume name entry, which contains the server name,
498 * partition pattern, and volume pattern.
499 */
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.
504 */
505 readHeader = 1;
506 continue;
507 }
508
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.
512 */
513 tve = calloc(1, sizeof(struct bc_volumeEntry));
514 if (!tve) {
515 afs_com_err(whoami, 0,
516 "Can't malloc() a new volume spec record!");
517 return (-1);
518 }
519 if (bc_ParseHost(serverName, &(tve->server)))
520 afs_com_err(whoami, 0, "Can't get required info on host '%s'",
521 serverName);
522
523 /* The above code has filled in the server sockaddr, now fill in
524 * the rest of the fields.
525 */
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!");
530 return (-1);
531 }
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!");
536 return (-1);
537 }
538 code = bc_GetPartitionID(partName, &tve->partition);
539 if (code) {
540 afs_com_err(whoami, 0, "Can't parse partition '%s'", partName);
541 return -1;
542 }
543 tp = strdup(vsname);
544 if (!tp) {
545 afs_com_err(whoami, 0,
546 "Can't malloc() a new volume spec volume pattern field!");
547 return (-1);
548 }
549 tve->name = tp;
550
551 /* Now, thread it onto the list of other volume spec entries for
552 * the current volume set.
553 */
554
555 for (ppve = &tvs->ventries, pve = *ppve; pve;
556 ppve = &pve->next, pve = *ppve);
557 *ppve = tve;
558 tve->next = (struct bc_volumeEntry *)0;
559 }
560 } /*forever loop */
561
562 /* If we hit an EOF in the middle of a volume set record, we bitch and
563 * moan.
564 */
565 if (!readHeader)
566 return (-1);
567
568 /*
569 * Well, we did it. Return successfully.
570 */
571 return (0);
572
573 } /*bc_ParseVolumeSet */
574
575 /* bc_SaveVolumeSet
576 * save the current volume set information to disk
577 */
578
579 int
580 bc_SaveVolumeSet(void)
581 {
582 afs_int32 code = 0;
583 struct bc_volumeSet *tset;
584 struct bc_volumeEntry *tentry;
585
586 udbClientTextP ctPtr;
587 FILE *stream;
588
589 extern struct bc_config *bc_globalConfig;
590
591 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
592 stream = ctPtr->textStream;
593
594 /* must be locked */
595 if (ctPtr->lockHandle == 0)
596 return (BC_INTERNALERROR);
597
598 /* truncate the file */
599 code = ftruncate(fileno(stream), 0);
600 if (code)
601 ERROR(errno);
602
603 rewind(stream);
604
605 /* now write the volumeset information */
606
607 for (tset = bc_globalConfig->vset; tset != 0; tset = tset->next) {
608 if (tset->flags & VSFLAG_TEMPORARY)
609 continue; /* skip temporary entries */
610
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);
615 }
616 fprintf(stream, "end\n");
617 }
618
619 if (ferror(stream))
620 return (BC_INTERNALERROR);
621
622 /* send to server */
623 code = bcdb_SaveTextFile(ctPtr);
624 if (code)
625 ERROR(code);
626
627 /* do this on bcdb_SaveTextFile */
628 /* increment local version number */
629 ctPtr->textVersion++;
630
631 /* update locally stored file size */
632 ctPtr->textSize = filesize(ctPtr->textStream);
633
634 error_exit:
635 return (code);
636 }
637
638 afs_int32
639 bc_UpdateVolumeSet(void)
640 {
641 struct udbHandleS *uhptr = &udbHandle;
642 udbClientTextP ctPtr;
643 afs_int32 code;
644 int lock = 0;
645
646 /* lock schedules and check validity */
647 ctPtr = &bc_globalConfig->configText[TB_VOLUMESET];
648
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 */
653 }
654
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);
659 if (code)
660 ERROR(code);
661 lock = 1;
662 }
663
664 if (ctPtr->textVersion != -1) {
665 printf("backup: obsolete volumesets - updating\n");
666 bc_ClearVolumeSets();
667 }
668
669 /* open a temp file to store the config text received from buserver *
670 * The open file stream is stored in ctPtr->textStream */
671 code =
672 bc_openTextFile(ctPtr,
673 &bc_globalConfig->tmpTextFileNames[TB_VOLUMESET][0]);
674 if (code)
675 ERROR(code);
676 /* now get a fresh set of information from the database */
677 code = bcdb_GetTextFile(ctPtr);
678 if (code)
679 ERROR(code);
680
681 /* fetch the version number */
682 code =
683 ubik_BUDB_GetTextVersion(uhptr->uh_client, 0, ctPtr->textType,
684 &ctPtr->textVersion);
685 if (code)
686 ERROR(code);
687
688 /* parse the file */
689 code = bc_ParseVolumeSet();
690 if (code)
691 ERROR(code);
692
693 error_exit:
694 if (lock && ctPtr->lockHandle)
695 bc_UnlockText(ctPtr);
696 return (code);
697 }