build: compile_et rules for parallel make
[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 }