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