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