dcba50b820d94deb2a678315d3a1d76efc4124e6
[openafs.git] / src / bucoord / dsvs.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 /*
11  * ALL RIGHTS RESERVED
12  *
13  * (C) COPYRIGHT IBM CORPORATION 1987, 1998
14  * LICENSED MATERIALS - PROPERTY OF IBM
15  */
16
17
18 #include <afs/param.h>
19 #include <afsconfig.h>
20
21 RCSID("$Header$");
22
23 #include <sys/types.h>
24 #include <afs/cmd.h>
25 #ifdef AFS_NT40_ENV
26 #include <winsock2.h>
27 #else
28 #include <strings.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <sys/param.h>
33 #endif
34 #include <stdio.h>
35 #include <afs/com_err.h>
36
37 #include <afs/bubasics.h>
38 #include "bc.h"
39
40 extern struct bc_config *bc_globalConfig;
41 extern FILE *bc_open();
42 extern void bc_HandleMisc();
43 extern char *whoami;
44
45 static char db_dsvs = 0;    /*Assume debugging output turned off*/
46 static char mn[] = "dsvs";  /*Module name*/
47
48 struct ubik_client *cstructp;   /*Ptr to Ubik client structure*/
49
50 extern struct bc_volumeSet *bc_FindVolumeSet(struct bc_config *cf, char *name);
51
52
53 /* Code to maintain dump schedule and volume set abstractions.
54  * A volume set looks like this:
55  *      vsname: servername partition-name <volume list>*
56  * A dump schedule looks like this:
57  *      dsname: vsname period parent-ds
58  */
59
60 /* get partition id from a name */
61 afs_int32 bc_GetPartitionID(aname, aval)
62     afs_int32 *aval;
63     char *aname; {
64
65     /*bc_GetPartitionID */
66
67     register char tc;
68     char ascii[3];
69
70     /* special-case "anything" */
71     if (strcmp(aname, ".*") == 0) {
72         *aval = -1;
73         return 0;
74     }
75     tc = *aname;
76     if (tc == 0) return -1;     /* unknown */
77     /* numbers go straight through */
78     if (tc >= '0' && tc <= '9') {
79         *aval = bc_SafeATOI(aname);
80         return 0;
81     }
82     /* otherwise check for vicepa or /vicepa, or just plain "a" */
83     ascii[2] = 0;
84     if (strlen(aname) <= 2) {
85         strcpy(ascii, aname);
86     }
87     else if (!strncmp(aname, "/vicep", 6)) {
88         strncpy(ascii, aname+6, 2);
89     }
90     else if (!strncmp(aname, "vicep", 5)) {
91         strncpy(ascii, aname+5, 2);
92     }
93     else return(BC_NOPARTITION);        /* bad partition name */
94     /* now partitions are named /vicepa ... /vicepz, /vicepaa, /vicepab, .../vicepzz, and are numbered
95           from 0.  Do the appropriate conversion */
96     if (ascii[1] == 0) {
97         /* one char name, 0..25 */
98         if (ascii[0] <  'a' || ascii[0] > 'z')  return -1;  /* wrongo */
99         *aval = ascii[0] - 'a';
100         return 0;
101     }
102     else {
103         /* two char name, 26 .. <whatever> */
104         if (ascii[0] <  'a' || ascii[0] > 'z')  return -1;  /* wrongo */
105         if (ascii[1] <  'a' || ascii[1] > 'z')  return -1;  /* just as bad */
106         *aval = (ascii[0] - 'a') * 26 + (ascii[1] - 'a') + 26;
107         return 0;
108     }
109 } /*bc_GetPartitionID*/
110
111 /*----------------------------------------------------------------------------
112  * bc_ParseHost
113  *
114  * Description:
115  *      Given a string containing a host name or its IP address in dot notation, fill in
116  *      the given sockaddr with all the corresponding info.
117  *
118  * Arguments:
119  *      aname : Host name or dotted IP address.
120   *     asockaddr: Ptr to sockaddr to fill in for the above host.
121  *
122  * Returns:
123  *      0 if everything went well,
124  *      -1 if it couldn't be translated.
125  *
126  * Environment:
127  *      Nothing interesting.
128  *
129  * Side Effects:
130  *      None.
131  *----------------------------------------------------------------------------
132  */
133
134 int bc_ParseHost(aname, asockaddr)
135     char *aname;
136     struct sockaddr_in *asockaddr;
137
138 { /*bc_ParseHost*/
139
140     register struct hostent *th;    /*Host entry*/
141     afs_int32 addr;                         /*Converted address*/
142     afs_int32 b1, b2, b3, b4;       /*Byte-sized address chunks*/
143     register afs_int32 code;                /*Return code from sscanf()*/
144     afs_int32 tmp1,tmp2;
145
146     /*
147      * Try to parse the given name as a dot-notation IP address first.
148      */
149     code = sscanf(aname, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);
150     if (code == 4) {
151         /*
152          * Four chunks were read, so we assume success.  Construct the socket.
153          */
154         asockaddr->sin_family = AF_INET;
155         asockaddr->sin_port   = 0;
156         addr = (b1<<24) | (b2<<16) | (b3<<8) | b4;
157         bcopy(&addr,&tmp1,sizeof(afs_int32));
158         tmp2 = htonl(tmp1);
159         bcopy(&tmp2, &asockaddr->sin_addr.s_addr, sizeof(afs_int32));
160         return(0);
161     }
162
163     /*
164      * The given string isn't a dotted IP address.  Try to map it as a host
165      * name, or leave it as a wild-card.
166      */
167
168     if (strcmp(aname, ".*") == 0) {
169         bzero(asockaddr, sizeof(struct sockaddr_in));
170         return 0;
171     }
172
173     th = gethostbyname(aname);
174     if (!th)
175         /*
176          * No such luck, return failure.
177          */
178         return(BC_NOHOST);
179
180     /*
181      * We found a mapping; construct the socket.
182      */
183     asockaddr->sin_family = AF_INET;
184     asockaddr->sin_port   = 0;
185     bcopy(th->h_addr,&tmp1,sizeof(afs_int32));
186     tmp2 = htonl(tmp1);
187     bcopy(&tmp2,&(asockaddr->sin_addr.s_addr),
188            sizeof(asockaddr->sin_addr.s_addr));
189     return(0);
190
191 } /*bc_ParseHost*/
192
193
194 /* create an empty volume set, new items are added via bc_AddVolumeItem */
195 bc_CreateVolumeSet(aconfig, avolName, aflags)
196     struct bc_config *aconfig;
197     char *avolName;
198     afs_int32 aflags;
199 {
200     register struct bc_volumeSet **tlast, *tset, *nset;
201
202     if (bc_FindVolumeSet(aconfig, avolName)) return -1; /* already exists */
203     /* move to end of the list */
204
205     nset = (struct bc_volumeSet *) malloc(sizeof(struct bc_volumeSet));
206     bzero(nset, sizeof(*nset));
207     nset->flags = aflags;
208     nset->name  = (char *) malloc(strlen(avolName)+1);
209     strcpy(nset->name, avolName);
210     if (aflags & VSFLAG_TEMPORARY) {
211        /* Add to beginning of list */
212        nset->next = aconfig->vset;
213        aconfig->vset = nset;
214     } else {
215        /* Add to end of list */
216        for(tlast = &aconfig->vset, tset = *tlast; tset; tlast = &tset->next, tset = *tlast);
217        *tlast = nset;
218     }
219     return 0;
220 }
221
222
223
224 void FreeVolumeSet(avset)
225   struct bc_volumeSet *avset;
226 {
227   FreeVolumeEntryList(avset->ventries);
228   free(avset->name);
229   free(avset);
230 }
231
232 static FreeVolumeEntryList(aentry)
233 register struct bc_volumeEntry *aentry; {
234     register struct bc_volumeEntry *tnext;
235
236     while(aentry) {
237         tnext = aentry->next;
238         FreeVolumeEntry(aentry);
239         aentry = tnext;
240     }
241     return 0;
242 }
243
244 static FreeVolumeEntry(aentry)
245 register struct bc_volumeEntry *aentry; {
246     free(aentry->name);
247     free(aentry->serverName);
248     free(aentry->partname);
249     free(aentry);
250     return 0;
251 }
252
253 bc_DeleteVolumeSet(aconfig, avolName, flags)
254     struct bc_config *aconfig;
255     char *avolName; 
256     afs_int32 *flags;
257 {
258     register struct bc_volumeSet **tlast, *tset;
259
260     *flags = 0;
261     tlast = &aconfig->vset;
262     for(tset = *tlast; tset; tlast = &tset->next, tset = *tlast) {
263         if (strcmp(avolName, tset->name) == 0) {
264             *flags = tset->flags;    /* Remember flags */
265             *tlast = tset->next;     /* Remove from chain */
266             FreeVolumeSet(tset);     /* Free the volume set */
267             return 0;
268         }
269     }
270
271     /* if we get here, we didn't find the item */
272     return -1;
273 }
274
275 bc_DeleteVolumeItem(aconfig, avolName, anumber)
276     struct bc_config *aconfig;
277     char *avolName;
278     afs_int32 anumber; 
279 {
280     register afs_int32 i;
281     register struct bc_volumeSet *tset;
282     register struct bc_volumeEntry *tentry, **tlast;
283
284     tset = bc_FindVolumeSet(aconfig, avolName);
285     if (!tset) return -1;
286
287     tlast = &tset->ventries;
288     for(i=1, tentry = *tlast; tentry; tlast = &tentry->next, tentry = *tlast, i++) {
289         if (anumber == i) {
290             /* found entry we want */
291             *tlast = tentry->next;
292             FreeVolumeEntry(tentry);
293             return 0;
294         }
295     }
296     return -2;  /* not found */
297 }
298
299 bc_AddVolumeItem(aconfig, avolName, ahost, apart, avol)
300     struct bc_config *aconfig;
301     char *avolName;
302     char *ahost, *apart, *avol; 
303 {
304     struct bc_volumeSet *tset;
305     register struct bc_volumeEntry **tlast, *tentry;
306     register afs_int32 code;
307
308     tset = bc_FindVolumeSet(aconfig, avolName);
309     if (!tset) return(BC_NOVOLSET);
310
311     /* otherwise append this item to the end of the real list */
312     tlast = &tset->ventries;
313
314     /* move to end of the list */
315     for(tentry = *tlast; tentry; tlast = &tentry->next, tentry = *tlast);
316     tentry = (struct bc_volumeEntry *) malloc(sizeof(struct bc_volumeEntry));
317     bzero(tentry, sizeof(*tentry));
318     tentry->serverName = (char *) malloc(strlen(ahost)+1);
319     strcpy(tentry->serverName, ahost);
320     tentry->partname = (char *) malloc(strlen(apart)+1);
321     strcpy(tentry->partname, apart);
322     tentry->name = (char *) malloc(strlen(avol)+1);
323     strcpy(tentry->name, avol);
324
325     code = bc_ParseHost(tentry->serverName, &tentry->server);
326     if (code)
327         return(code);
328
329     code = bc_GetPartitionID(tentry->partname, &tentry->partition);
330     if (code)
331         return(code);
332
333     *tlast = tentry;    /* thread on the list */
334     return 0;
335 }
336
337 struct bc_volumeSet *bc_FindVolumeSet(struct bc_config *aconfig, char *aname)
338 { /*bc_FindVolumeSet*/
339
340     register struct bc_volumeSet *tvs;
341
342     for(tvs = aconfig->vset; tvs; tvs=tvs->next) {
343         if (!strcmp(tvs->name, aname))
344             return(tvs);
345     }
346     return(struct bc_volumeSet *)0;
347
348 }/*bc_FindVolumeSet*/
349
350 /* ------------------------------------
351  * dumpschedule management code
352  * ------------------------------------
353  */
354
355 /* bc_CreateDumpSchedule
356  *      Add another node to the dump schedule.
357  * entry:
358  *      aconfig - in core configuration structures
359  *      adumpName - name of new dump node
360  *      expDate - expiration date
361  *      expType - absolute or relative
362  */
363
364 bc_CreateDumpSchedule(aconfig,adumpName, expDate, expType)
365 struct bc_config *aconfig;
366 char *adumpName;
367 afs_int32       expDate;
368 afs_int32       expType;
369 {
370     register struct bc_dumpSchedule **tlast, *tdump;
371     struct bc_dumpSchedule *parent, *node;
372     afs_int32 code;
373
374     if(strcmp(adumpName, "none") == 0)
375         return -2;      /* invalid name */
376
377     code =  FindDump(aconfig, adumpName, &parent, &node);
378     if ( code == 0 )
379         return -1;                      /* node already exists */
380     else if ( code != -1 )
381         return -2;                      /* name specification error */
382
383     tdump = (struct bc_dumpSchedule *) malloc(sizeof(struct bc_dumpSchedule));
384     bzero(tdump, sizeof(*tdump));
385
386     /* prepend this node to the dump schedule list */
387     tdump->next = aconfig->dsched;
388     aconfig->dsched = tdump;
389
390     /* save the name of this dump node */
391     tdump->name = (char *) malloc(strlen(adumpName)+1);
392     strcpy(tdump->name, adumpName);
393
394     /* expiration information */
395     tdump->expDate = expDate;
396     tdump->expType = expType;
397
398     bc_ProcessDumpSchedule(aconfig);    /* redo tree */
399     return 0;
400 }
401
402
403 /* Recursively remove this node and all of its children from aconfig's
404  * list of dumps.  Note that this leaves the sibling pointers damaged (pointing
405  * to strange places), so we must call bc_ProcessDumpSchedule when we're done.
406  */
407
408 bc_DeleteDumpScheduleAddr(aconfig, adumpAddr)
409 struct bc_config *aconfig;
410 struct bc_dumpSchedule *adumpAddr; {
411     register struct bc_dumpSchedule **tlast, *tdump;
412     register struct bc_dumpSchedule *tnext;
413
414     /* knock off all children first */
415     for(tdump = adumpAddr->firstChild; tdump; tdump = tnext) {
416         /* extract next ptr now, since will be freed by recursive call below */
417         tnext = tdump->nextSibling;
418         bc_DeleteDumpScheduleAddr(aconfig, tdump);
419     }
420
421     /* finally, remove us from the list of good dudes */
422     for(tlast = &aconfig->dsched, tdump = *tlast; tdump;
423         tlast = &tdump->next, tdump = *tlast) {
424         if (tdump == adumpAddr) {
425             /* found the one we're looking for */
426             *tlast = tdump->next;       /* remove us from basic list */
427             free(tdump->name);
428             free(tdump);
429             return 0;
430         }
431     }
432     return 0;
433 }
434
435 /* bc_FindDumpSchedule
436  *      Finds dump schedule aname by doing a linear search
437  * entry:
438  *      aconfig - handle for incore configuration tables
439  *      aname - (path)name to match on
440  * exit:
441  *      0 for failure, ptr to dumpschedule for success
442  */
443
444 struct bc_dumpSchedule *bc_FindDumpSchedule(aconfig, aname)
445 char *aname;
446 struct bc_config *aconfig; {
447     register struct bc_dumpSchedule *tds;
448     for(tds=aconfig->dsched; tds; tds=tds->next) {
449         if (!strcmp(tds->name, aname))
450             return tds;
451     }
452     return (struct bc_dumpSchedule *) 0;
453 }
454
455 /* bc_DeleteDumpSchedule
456  *      Delete dump node adumpName from the dump schedule
457  */
458
459 bc_DeleteDumpSchedule(aconfig, adumpName)
460 struct bc_config *aconfig;
461 char *adumpName; {
462     register struct bc_dumpSchedule *tdump;
463
464     /* does a linear search of the dump schedules in order to find
465      * the one to delete
466      */
467     for(tdump = aconfig->dsched; tdump; tdump=tdump->next) {
468         if (strcmp(tdump->name, adumpName)==0) {
469             /* found it, we can zap recursively */
470             bc_DeleteDumpScheduleAddr(aconfig, tdump);
471             /* tree's been pruned, but we have to recompute the internal pointers
472                from first principles, since we didn't bother to maintain
473                the sibling and children pointers during the call to delete
474                the nodes */
475             bc_ProcessDumpSchedule(aconfig);
476             return 0;
477         }
478     }
479     /* if we make it here, there's no such dump schedule entry */
480     return -1;
481 }
482
483
484 /* bc_ProcessDumpSchedule
485  *      Walk over the dump schedule list, building it into a tree.  This
486  * algorithm is simple, but takes O(N*2) operations to run, with N=number
487  * of dump schedule nodes. It probably will never matter
488  */
489
490 bc_ProcessDumpSchedule(aconfig)
491 register struct bc_config *aconfig;
492 {
493     register struct bc_dumpSchedule *tds, *uds;
494     struct bc_dumpSchedule *parentptr, *nodeptr;
495     int retval;
496
497     /* first, clear all the links on all entries so that this function
498      * may be called any number of times with no ill effects
499      */
500     for(tds = aconfig->dsched; tds; tds=tds->next)
501     {
502         tds->parent = (struct bc_dumpSchedule *) 0;
503         tds->nextSibling = (struct bc_dumpSchedule *) 0;
504         tds->firstChild = (struct bc_dumpSchedule *) 0;
505     }
506
507     for(tds = aconfig->dsched; tds; tds=tds->next)
508     {
509         retval = FindDump(aconfig, tds->name, &parentptr, &nodeptr);
510         if ( retval != 0 )
511         {
512             printf("bc_processdumpschedule: finddump returns %d\n", retval);
513             exit(1);
514         }
515
516         /* only need to do work if it is not a root node */
517         if ( parentptr != 0 )
518         {
519             nodeptr->parent = parentptr;
520             nodeptr->nextSibling = parentptr->firstChild;
521             parentptr->firstChild = nodeptr;
522         }
523     }
524     return 0;
525 }
526
527
528 /* FindDump
529  * entry:
530  * exit:
531  *      parentptr - set to parent node, if one exists
532  *      nodeptr - set to node requested
533  *
534  *      return values are:
535  *      0 - success, parentptr and nodeptr set appropriately
536  *      -1 - node not found, parent exists if reqd. Will be 0 for root nodes.
537  *      -2 - path search error. Some node on the path does not exist.
538  *      -3 - name specification error
539  * notes:
540  *      pathname checking should be done externally. In particular, trailing
541  *      / symbols may return confusing error codes. (e.g on missing last
542  *      node returns -2 rather than -1)
543  */
544
545 int
546 FindDump(aconfig, nodeString, parentptr, nodeptr)
547      struct bc_config *aconfig;
548      char *nodeString;
549      struct bc_dumpSchedule **parentptr;
550      struct bc_dumpSchedule **nodeptr;
551 {
552     struct bc_dumpSchedule *dsptr;
553     char *separator;
554     int matchLength;
555     char *curptr;
556
557     *parentptr = 0;
558     *nodeptr = 0;
559
560     /* ensure first char is correct separator */
561     if ( (nodeString[0] != '/')
562     ||   (strlen(&nodeString[0]) <= 1)
563        )
564     {
565         printf("FindDump: %s, error in dump name specification\n", nodeString);
566         return(-3);
567     }
568
569     matchLength = 0;
570     curptr = &nodeString[1];                            /* past first / */
571     separator = index(curptr, '/');
572     if ( separator == 0 )
573         matchLength = strlen(curptr) + 1;               /* +1 for leading / */
574     else
575         matchLength = (separator-&nodeString[0]);
576    
577     /* printf("matchLength = %d\n", matchLength); */
578     while ( 1 )
579     {
580         /* now search all the nodes for this name */
581         for ( dsptr = aconfig->dsched; dsptr != 0; dsptr = dsptr->next)
582         {
583             /* printf("compare %s with %s for %d\n",
584                    dsptr->name, nodeString, matchLength); */
585             if ( strlen(dsptr->name) != matchLength )
586                 continue;
587
588             if ( strncmp(dsptr->name, nodeString, matchLength) == 0)
589             {
590                 *nodeptr = dsptr;
591                 break;
592             }
593         }
594
595         if ( nodeString[matchLength] == 0 )
596         {
597             /* last node in the path */
598             if ( *nodeptr )
599                 return(0);                              /* all ok */
600             else
601                 /* node not found; parent exists for non root nodes */
602                 return(-1);
603         }
604
605         if ( *nodeptr == 0 )
606                 /* failed to find a node in the path */
607                 return(-2);
608         
609         curptr = separator+1;
610         if ( *curptr == 0 )
611         {
612             printf("FindDump: trailing / in %s\n", nodeString);
613             return(-3);
614         }
615         
616         separator = index(curptr, '/');
617         if ( separator == 0 )
618                 matchLength = strlen(&nodeString[0]);
619         else
620                 matchLength = separator-&nodeString[0];
621         
622         *parentptr = *nodeptr;
623         *nodeptr = 0;
624     }
625 }