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