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