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