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