70839a35f27c6bf4805c61b82b05a4fd188b580c
[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 RCSID
22     ("$Header$");
23
24 #include <sys/types.h>
25 #include <afs/cmd.h>
26 #ifdef AFS_NT40_ENV
27 #include <winsock2.h>
28 #else
29 #include <sys/socket.h>
30 #include <netinet/in.h>
31 #include <netdb.h>
32 #include <sys/param.h>
33 #endif
34 #include <afs/com_err.h>
35
36 #include <afs/bubasics.h>
37 #include "bc.h"
38 #include "bucoord_prototypes.h"
39     
40 struct ubik_client *cstructp;   /*Ptr to Ubik client structure */
41
42 static int FreeVolumeEntryList(struct bc_volumeEntry *aentry);
43 static int FreeVolumeEntry(struct bc_volumeEntry *aentry);
44
45 /* Code to maintain dump schedule and volume set abstractions.
46  * A volume set looks like this:
47  *      vsname: servername partition-name <volume list>*
48  * A dump schedule looks like this:
49  *      dsname: vsname period parent-ds
50  */
51
52 /* get partition id from a name */
53 afs_int32
54 bc_GetPartitionID(char *aname, afs_int32 *aval)
55 {
56
57     /*bc_GetPartitionID */
58
59     register char tc;
60     char ascii[3];
61
62     /* special-case "anything" */
63     if (strcmp(aname, ".*") == 0) {
64         *aval = -1;
65         return 0;
66     }
67     tc = *aname;
68     if (tc == 0)
69         return -1;              /* unknown */
70     /* numbers go straight through */
71     if (tc >= '0' && tc <= '9') {
72         *aval = bc_SafeATOI(aname);
73         return 0;
74     }
75     /* otherwise check for vicepa or /vicepa, or just plain "a" */
76     ascii[2] = 0;
77     if (strlen(aname) <= 2) {
78         strcpy(ascii, aname);
79     } else if (!strncmp(aname, "/vicep", 6)) {
80         strncpy(ascii, aname + 6, 2);
81     } else if (!strncmp(aname, "vicep", 5)) {
82         strncpy(ascii, aname + 5, 2);
83     } else
84         return (BC_NOPARTITION);        /* bad partition name */
85     /* now partitions are named /vicepa ... /vicepz, /vicepaa, /vicepab, .../vicepzz, and are numbered
86      * from 0.  Do the appropriate conversion */
87     if (ascii[1] == 0) {
88         /* one char name, 0..25 */
89         if (ascii[0] < 'a' || ascii[0] > 'z')
90             return -1;          /* wrongo */
91         *aval = ascii[0] - 'a';
92         return 0;
93     } else {
94         /* two char name, 26 .. <whatever> */
95         if (ascii[0] < 'a' || ascii[0] > 'z')
96             return -1;          /* wrongo */
97         if (ascii[1] < 'a' || ascii[1] > 'z')
98             return -1;          /* just as bad */
99         *aval = (ascii[0] - 'a') * 26 + (ascii[1] - 'a') + 26;
100         return 0;
101     }
102 }                               /*bc_GetPartitionID */
103
104 /*----------------------------------------------------------------------------
105  * bc_ParseHost
106  *
107  * Description:
108  *      Given a string containing a host name or its IP address in dot notation, fill in
109  *      the given sockaddr with all the corresponding info.
110  *
111  * Arguments:
112  *      aname : Host name or dotted IP address.
113   *     asockaddr: Ptr to sockaddr to fill in for the above host.
114  *
115  * Returns:
116  *      0 if everything went well,
117  *      -1 if it couldn't be translated.
118  *
119  * Environment:
120  *      Nothing interesting.
121  *
122  * Side Effects:
123  *      None.
124  *----------------------------------------------------------------------------
125  */
126
127 int
128 bc_ParseHost(char *aname, struct sockaddr_in *asockaddr)
129
130 {                               /*bc_ParseHost */
131
132     register struct hostent *th;        /*Host entry */
133     afs_int32 addr;             /*Converted address */
134     afs_int32 b1, b2, b3, b4;   /*Byte-sized address chunks */
135     register afs_int32 code;    /*Return code from sscanf() */
136     afs_int32 tmp1, tmp2;
137
138     /*
139      * Try to parse the given name as a dot-notation IP address first.
140      */
141     code = sscanf(aname, "%d.%d.%d.%d", &b1, &b2, &b3, &b4);
142     if (code == 4) {
143         /*
144          * Four chunks were read, so we assume success.  Construct the socket.
145          */
146 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
147         asockaddr->sin_len = sizeof(struct sockaddr_in);
148 #endif
149         asockaddr->sin_family = AF_INET;
150         asockaddr->sin_port = 0;
151         addr = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4;
152         memcpy(&tmp1, &addr, sizeof(afs_int32));
153         tmp2 = htonl(tmp1);
154         memcpy(&asockaddr->sin_addr.s_addr, &tmp2, sizeof(afs_int32));
155         return (0);
156     }
157
158     /*
159      * The given string isn't a dotted IP address.  Try to map it as a host
160      * name, or leave it as a wild-card.
161      */
162
163     if (strcmp(aname, ".*") == 0) {
164         memset(asockaddr, 0, sizeof(struct sockaddr_in));
165         return 0;
166     }
167
168     th = gethostbyname(aname);
169     if (!th)
170         /*
171          * No such luck, return failure.
172          */
173         return (BC_NOHOST);
174
175     /*
176      * We found a mapping; construct the socket.
177      */
178 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
179     asockaddr->sin_len = sizeof(struct sockaddr_in);
180 #endif
181     asockaddr->sin_family = AF_INET;
182     asockaddr->sin_port = 0;
183     memcpy(&tmp1, th->h_addr, sizeof(afs_int32));
184     tmp2 = htonl(tmp1);
185     memcpy(&(asockaddr->sin_addr.s_addr), &tmp2,
186            sizeof(asockaddr->sin_addr.s_addr));
187     return (0);
188
189 }                               /*bc_ParseHost */
190
191
192 /* create an empty volume set, new items are added via bc_AddVolumeItem */
193 int
194 bc_CreateVolumeSet(struct bc_config *aconfig, char *avolName,
195                    afs_int32 aflags)
196 {
197     register struct bc_volumeSet **tlast, *tset, *nset;
198
199     if (bc_FindVolumeSet(aconfig, avolName))
200         return -1;              /* already exists */
201     /* move to end of the list */
202
203     nset = (struct bc_volumeSet *)malloc(sizeof(struct bc_volumeSet));
204     memset(nset, 0, sizeof(*nset));
205     nset->flags = aflags;
206     nset->name = (char *)malloc(strlen(avolName) + 1);
207     strcpy(nset->name, avolName);
208     if (aflags & VSFLAG_TEMPORARY) {
209         /* Add to beginning of list */
210         nset->next = aconfig->vset;
211         aconfig->vset = nset;
212     } else {
213         /* Add to end of list */
214         for (tlast = &aconfig->vset, tset = *tlast; tset;
215              tlast = &tset->next, tset = *tlast);
216         *tlast = nset;
217     }
218     return 0;
219 }
220
221 static int
222 FreeVolumeEntry(register struct bc_volumeEntry *aentry)
223 {
224     free(aentry->name);
225     free(aentry->serverName);
226     free(aentry->partname);
227     free(aentry);
228     return 0;
229 }
230
231 static int
232 FreeVolumeEntryList(register struct bc_volumeEntry *aentry)
233 {
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
245
246 void
247 FreeVolumeSet(struct bc_volumeSet *avset)
248 {
249     FreeVolumeEntryList(avset->ventries);
250     free(avset->name);
251     free(avset);
252 }
253
254 int
255 bc_DeleteVolumeSet(struct bc_config *aconfig, 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 int
276 bc_DeleteVolumeItem(struct bc_config *aconfig, char *avolName,
277                     afs_int32 anumber)
278 {
279     register afs_int32 i;
280     register struct bc_volumeSet *tset;
281     register struct bc_volumeEntry *tentry, **tlast;
282
283     tset = bc_FindVolumeSet(aconfig, avolName);
284     if (!tset)
285         return -1;
286
287     tlast = &tset->ventries;
288     for (i = 1, tentry = *tlast; tentry;
289          tlast = &tentry->next, tentry = *tlast, i++) {
290         if (anumber == i) {
291             /* found entry we want */
292             *tlast = tentry->next;
293             FreeVolumeEntry(tentry);
294             return 0;
295         }
296     }
297     return -2;                  /* not found */
298 }
299
300 int
301 bc_AddVolumeItem(struct bc_config *aconfig, char *avolName, char *ahost, 
302                  char *apart, char *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)
310         return (BC_NOVOLSET);
311
312     /* otherwise append this item to the end of the real list */
313     tlast = &tset->ventries;
314
315     /* move to end of the list */
316     for (tentry = *tlast; tentry; tlast = &tentry->next, tentry = *tlast);
317     tentry = (struct bc_volumeEntry *)malloc(sizeof(struct bc_volumeEntry));
318     memset(tentry, 0, sizeof(*tentry));
319     tentry->serverName = (char *)malloc(strlen(ahost) + 1);
320     strcpy(tentry->serverName, ahost);
321     tentry->partname = (char *)malloc(strlen(apart) + 1);
322     strcpy(tentry->partname, apart);
323     tentry->name = (char *)malloc(strlen(avol) + 1);
324     strcpy(tentry->name, avol);
325
326     code = bc_ParseHost(tentry->serverName, &tentry->server);
327     if (code)
328         return (code);
329
330     code = bc_GetPartitionID(tentry->partname, &tentry->partition);
331     if (code)
332         return (code);
333
334     *tlast = tentry;            /* thread on the list */
335     return 0;
336 }
337
338 struct bc_volumeSet *
339 bc_FindVolumeSet(struct bc_config *aconfig, char *aname)
340 {                               /*bc_FindVolumeSet */
341
342     register struct bc_volumeSet *tvs;
343
344     for (tvs = aconfig->vset; tvs; tvs = tvs->next) {
345         if (!strcmp(tvs->name, aname))
346             return (tvs);
347     }
348     return (struct bc_volumeSet *)0;
349
350 }                               /*bc_FindVolumeSet */
351
352 /* ------------------------------------
353  * dumpschedule management code
354  * ------------------------------------
355  */
356
357 /* bc_CreateDumpSchedule
358  *      Add another node to the dump schedule.
359  * entry:
360  *      aconfig - in core configuration structures
361  *      adumpName - name of new dump node
362  *      expDate - expiration date
363  *      expType - absolute or relative
364  */
365
366 int 
367 bc_CreateDumpSchedule(struct bc_config *aconfig, char *adumpName, 
368                       afs_int32 expDate, afs_int32 expType)
369 {
370     register struct bc_dumpSchedule *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     memset(tdump, 0, 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 int
408 bc_DeleteDumpScheduleAddr(struct bc_config *aconfig, 
409                           struct bc_dumpSchedule *adumpAddr)
410 {
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 *
445 bc_FindDumpSchedule(struct bc_config *aconfig, char *aname)
446 {
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 int
460 bc_DeleteDumpSchedule(struct bc_config *aconfig, char *adumpName)
461 {
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 int
491 bc_ProcessDumpSchedule(struct bc_config *aconfig)
492 {
493     register struct bc_dumpSchedule *tds;
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         tds->parent = (struct bc_dumpSchedule *)0;
502         tds->nextSibling = (struct bc_dumpSchedule *)0;
503         tds->firstChild = (struct bc_dumpSchedule *)0;
504     }
505
506     for (tds = aconfig->dsched; tds; tds = tds->next) {
507         retval = FindDump(aconfig, tds->name, &parentptr, &nodeptr);
508         if (retval != 0) {
509             printf("bc_processdumpschedule: finddump returns %d\n", retval);
510             exit(1);
511         }
512
513         /* only need to do work if it is not a root node */
514         if (parentptr != 0) {
515             nodeptr->parent = parentptr;
516             nodeptr->nextSibling = parentptr->firstChild;
517             parentptr->firstChild = nodeptr;
518         }
519     }
520     return 0;
521 }
522
523
524 /* FindDump
525  * entry:
526  * exit:
527  *      parentptr - set to parent node, if one exists
528  *      nodeptr - set to node requested
529  *
530  *      return values are:
531  *      0 - success, parentptr and nodeptr set appropriately
532  *      -1 - node not found, parent exists if reqd. Will be 0 for root nodes.
533  *      -2 - path search error. Some node on the path does not exist.
534  *      -3 - name specification error
535  * notes:
536  *      pathname checking should be done externally. In particular, trailing
537  *      / symbols may return confusing error codes. (e.g on missing last
538  *      node returns -2 rather than -1)
539  */
540
541 int
542 FindDump(struct bc_config *aconfig, char *nodeString, 
543          struct bc_dumpSchedule **parentptr, 
544          struct bc_dumpSchedule **nodeptr)
545 {
546     struct bc_dumpSchedule *dsptr;
547     char *separator;
548     int matchLength;
549     char *curptr;
550
551     *parentptr = 0;
552     *nodeptr = 0;
553
554     /* ensure first char is correct separator */
555     if ((nodeString[0] != '/')
556         || (strlen(&nodeString[0]) <= 1)
557         ) {
558         printf("FindDump: %s, error in dump name specification\n",
559                nodeString);
560         return (-3);
561     }
562
563     matchLength = 0;
564     curptr = &nodeString[1];    /* past first / */
565     separator = strchr(curptr, '/');
566     if (separator == 0)
567         matchLength = strlen(curptr) + 1;       /* +1 for leading / */
568     else
569         matchLength = (separator - &nodeString[0]);
570
571     /* printf("matchLength = %d\n", matchLength); */
572     while (1) {
573         /* now search all the nodes for this name */
574         for (dsptr = aconfig->dsched; dsptr != 0; dsptr = dsptr->next) {
575             /* printf("compare %s with %s for %d\n",
576              * dsptr->name, nodeString, matchLength); */
577             if (strlen(dsptr->name) != matchLength)
578                 continue;
579
580             if (strncmp(dsptr->name, nodeString, matchLength) == 0) {
581                 *nodeptr = dsptr;
582                 break;
583             }
584         }
585
586         if (nodeString[matchLength] == 0) {
587             /* last node in the path */
588             if (*nodeptr)
589                 return (0);     /* all ok */
590             else
591                 /* node not found; parent exists for non root nodes */
592                 return (-1);
593         }
594
595         if (*nodeptr == 0)
596             /* failed to find a node in the path */
597             return (-2);
598
599         curptr = separator + 1;
600         if (*curptr == 0) {
601             printf("FindDump: trailing / in %s\n", nodeString);
602             return (-3);
603         }
604
605         separator = strchr(curptr, '/');
606         if (separator == 0)
607             matchLength = strlen(&nodeString[0]);
608         else
609             matchLength = separator - &nodeString[0];
610
611         *parentptr = *nodeptr;
612         *nodeptr = 0;
613     }
614 }