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