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