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