Import Upstream version 1.8.5
[hcoop/debian/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 = calloc(1, sizeof(struct bc_volumeSet));
194 nset->flags = aflags;
195 nset->name = strdup(avolName);
196 if (aflags & VSFLAG_TEMPORARY) {
197 /* Add to beginning of list */
198 nset->next = aconfig->vset;
199 aconfig->vset = nset;
200 } else {
201 /* Add to end of list */
202 for (tlast = &aconfig->vset, tset = *tlast; tset;
203 tlast = &tset->next, tset = *tlast);
204 *tlast = nset;
205 }
206 return 0;
207 }
208
209 static int
210 FreeVolumeEntry(struct bc_volumeEntry *aentry)
211 {
212 free(aentry->name);
213 free(aentry->serverName);
214 free(aentry->partname);
215 free(aentry);
216 return 0;
217 }
218
219 static int
220 FreeVolumeEntryList(struct bc_volumeEntry *aentry)
221 {
222 struct bc_volumeEntry *tnext;
223
224 while (aentry) {
225 tnext = aentry->next;
226 FreeVolumeEntry(aentry);
227 aentry = tnext;
228 }
229 return 0;
230 }
231
232
233
234 void
235 FreeVolumeSet(struct bc_volumeSet *avset)
236 {
237 FreeVolumeEntryList(avset->ventries);
238 free(avset->name);
239 free(avset);
240 }
241
242 int
243 bc_DeleteVolumeSet(struct bc_config *aconfig, char *avolName,
244 afs_int32 *flags)
245 {
246 struct bc_volumeSet **tlast, *tset;
247
248 *flags = 0;
249 tlast = &aconfig->vset;
250 for (tset = *tlast; tset; tlast = &tset->next, tset = *tlast) {
251 if (strcmp(avolName, tset->name) == 0) {
252 *flags = tset->flags; /* Remember flags */
253 *tlast = tset->next; /* Remove from chain */
254 FreeVolumeSet(tset); /* Free the volume set */
255 return 0;
256 }
257 }
258
259 /* if we get here, we didn't find the item */
260 return -1;
261 }
262
263 int
264 bc_DeleteVolumeItem(struct bc_config *aconfig, char *avolName,
265 afs_int32 anumber)
266 {
267 afs_int32 i;
268 struct bc_volumeSet *tset;
269 struct bc_volumeEntry *tentry, **tlast;
270
271 tset = bc_FindVolumeSet(aconfig, avolName);
272 if (!tset)
273 return -1;
274
275 tlast = &tset->ventries;
276 for (i = 1, tentry = *tlast; tentry;
277 tlast = &tentry->next, tentry = *tlast, i++) {
278 if (anumber == i) {
279 /* found entry we want */
280 *tlast = tentry->next;
281 FreeVolumeEntry(tentry);
282 return 0;
283 }
284 }
285 return -2; /* not found */
286 }
287
288 int
289 bc_AddVolumeItem(struct bc_config *aconfig, char *avolName, char *ahost,
290 char *apart, char *avol)
291 {
292 struct bc_volumeSet *tset;
293 struct bc_volumeEntry **tlast, *tentry;
294 afs_int32 code;
295
296 tset = bc_FindVolumeSet(aconfig, avolName);
297 if (!tset)
298 return (BC_NOVOLSET);
299
300 /* otherwise append this item to the end of the real list */
301 tlast = &tset->ventries;
302
303 /* move to end of the list */
304 for (tentry = *tlast; tentry; tlast = &tentry->next, tentry = *tlast);
305 tentry = calloc(1, sizeof(struct bc_volumeEntry));
306 tentry->serverName = strdup(ahost);
307 tentry->partname = strdup(apart);
308 tentry->name = strdup(avol);
309
310 code = bc_ParseHost(tentry->serverName, &tentry->server);
311 if (code)
312 return (code);
313
314 code = bc_GetPartitionID(tentry->partname, &tentry->partition);
315 if (code)
316 return (code);
317
318 *tlast = tentry; /* thread on the list */
319 return 0;
320 }
321
322 struct bc_volumeSet *
323 bc_FindVolumeSet(struct bc_config *aconfig, char *aname)
324 { /*bc_FindVolumeSet */
325
326 struct bc_volumeSet *tvs;
327
328 for (tvs = aconfig->vset; tvs; tvs = tvs->next) {
329 if (!strcmp(tvs->name, aname))
330 return (tvs);
331 }
332 return (struct bc_volumeSet *)0;
333
334 } /*bc_FindVolumeSet */
335
336 /* ------------------------------------
337 * dumpschedule management code
338 * ------------------------------------
339 */
340
341 /* bc_CreateDumpSchedule
342 * Add another node to the dump schedule.
343 * entry:
344 * aconfig - in core configuration structures
345 * adumpName - name of new dump node
346 * expDate - expiration date
347 * expType - absolute or relative
348 */
349
350 int
351 bc_CreateDumpSchedule(struct bc_config *aconfig, char *adumpName,
352 afs_int32 expDate, afs_int32 expType)
353 {
354 struct bc_dumpSchedule *tdump;
355 struct bc_dumpSchedule *parent, *node;
356 afs_int32 code;
357
358 if (strcmp(adumpName, "none") == 0)
359 return -2; /* invalid name */
360
361 code = FindDump(aconfig, adumpName, &parent, &node);
362 if (code == 0)
363 return -1; /* node already exists */
364 else if (code != -1)
365 return -2; /* name specification error */
366
367 tdump = calloc(1, sizeof(struct bc_dumpSchedule));
368
369 /* prepend this node to the dump schedule list */
370 tdump->next = aconfig->dsched;
371 aconfig->dsched = tdump;
372
373 /* save the name of this dump node */
374 tdump->name = strdup(adumpName);
375
376 /* expiration information */
377 tdump->expDate = expDate;
378 tdump->expType = expType;
379
380 bc_ProcessDumpSchedule(aconfig); /* redo tree */
381 return 0;
382 }
383
384
385 /* Recursively remove this node and all of its children from aconfig's
386 * list of dumps. Note that this leaves the sibling pointers damaged (pointing
387 * to strange places), so we must call bc_ProcessDumpSchedule when we're done.
388 */
389 int
390 bc_DeleteDumpScheduleAddr(struct bc_config *aconfig,
391 struct bc_dumpSchedule *adumpAddr)
392 {
393 struct bc_dumpSchedule **tlast, *tdump;
394 struct bc_dumpSchedule *tnext;
395
396 /* knock off all children first */
397 for (tdump = adumpAddr->firstChild; tdump; tdump = tnext) {
398 /* extract next ptr now, since will be freed by recursive call below */
399 tnext = tdump->nextSibling;
400 bc_DeleteDumpScheduleAddr(aconfig, tdump);
401 }
402
403 /* finally, remove us from the list of good dudes */
404 for (tlast = &aconfig->dsched, tdump = *tlast; tdump;
405 tlast = &tdump->next, tdump = *tlast) {
406 if (tdump == adumpAddr) {
407 /* found the one we're looking for */
408 *tlast = tdump->next; /* remove us from basic list */
409 free(tdump->name);
410 free(tdump);
411 return 0;
412 }
413 }
414 return 0;
415 }
416
417 /* bc_FindDumpSchedule
418 * Finds dump schedule aname by doing a linear search
419 * entry:
420 * aconfig - handle for incore configuration tables
421 * aname - (path)name to match on
422 * exit:
423 * 0 for failure, ptr to dumpschedule for success
424 */
425
426 struct bc_dumpSchedule *
427 bc_FindDumpSchedule(struct bc_config *aconfig, char *aname)
428 {
429 struct bc_dumpSchedule *tds;
430 for (tds = aconfig->dsched; tds; tds = tds->next) {
431 if (!strcmp(tds->name, aname))
432 return tds;
433 }
434 return (struct bc_dumpSchedule *)0;
435 }
436
437 /* bc_DeleteDumpSchedule
438 * Delete dump node adumpName from the dump schedule
439 */
440
441 int
442 bc_DeleteDumpSchedule(struct bc_config *aconfig, char *adumpName)
443 {
444 struct bc_dumpSchedule *tdump;
445
446 /* does a linear search of the dump schedules in order to find
447 * the one to delete
448 */
449 for (tdump = aconfig->dsched; tdump; tdump = tdump->next) {
450 if (strcmp(tdump->name, adumpName) == 0) {
451 /* found it, we can zap recursively */
452 bc_DeleteDumpScheduleAddr(aconfig, tdump);
453 /* tree's been pruned, but we have to recompute the internal pointers
454 * from first principles, since we didn't bother to maintain
455 * the sibling and children pointers during the call to delete
456 * the nodes */
457 bc_ProcessDumpSchedule(aconfig);
458 return 0;
459 }
460 }
461 /* if we make it here, there's no such dump schedule entry */
462 return -1;
463 }
464
465
466 /* bc_ProcessDumpSchedule
467 * Walk over the dump schedule list, building it into a tree. This
468 * algorithm is simple, but takes O(N*2) operations to run, with N=number
469 * of dump schedule nodes. It probably will never matter
470 */
471
472 int
473 bc_ProcessDumpSchedule(struct bc_config *aconfig)
474 {
475 struct bc_dumpSchedule *tds;
476 struct bc_dumpSchedule *parentptr, *nodeptr;
477 int retval;
478
479 /* first, clear all the links on all entries so that this function
480 * may be called any number of times with no ill effects
481 */
482 for (tds = aconfig->dsched; tds; tds = tds->next) {
483 tds->parent = (struct bc_dumpSchedule *)0;
484 tds->nextSibling = (struct bc_dumpSchedule *)0;
485 tds->firstChild = (struct bc_dumpSchedule *)0;
486 }
487
488 for (tds = aconfig->dsched; tds; tds = tds->next) {
489 retval = FindDump(aconfig, tds->name, &parentptr, &nodeptr);
490 if (retval != 0) {
491 printf("bc_processdumpschedule: finddump returns %d\n", retval);
492 exit(1);
493 }
494
495 /* only need to do work if it is not a root node */
496 if (parentptr != 0) {
497 nodeptr->parent = parentptr;
498 nodeptr->nextSibling = parentptr->firstChild;
499 parentptr->firstChild = nodeptr;
500 }
501 }
502 return 0;
503 }
504
505
506 /* FindDump
507 * entry:
508 * exit:
509 * parentptr - set to parent node, if one exists
510 * nodeptr - set to node requested
511 *
512 * return values are:
513 * 0 - success, parentptr and nodeptr set appropriately
514 * -1 - node not found, parent exists if reqd. Will be 0 for root nodes.
515 * -2 - path search error. Some node on the path does not exist.
516 * -3 - name specification error
517 * notes:
518 * pathname checking should be done externally. In particular, trailing
519 * / symbols may return confusing error codes. (e.g on missing last
520 * node returns -2 rather than -1)
521 */
522
523 int
524 FindDump(struct bc_config *aconfig, char *nodeString,
525 struct bc_dumpSchedule **parentptr,
526 struct bc_dumpSchedule **nodeptr)
527 {
528 struct bc_dumpSchedule *dsptr;
529 char *separator;
530 int matchLength;
531 char *curptr;
532
533 *parentptr = 0;
534 *nodeptr = 0;
535
536 /* ensure first char is correct separator */
537 if ((nodeString[0] != '/')
538 || (strlen(&nodeString[0]) <= 1)
539 ) {
540 printf("FindDump: %s, error in dump name specification\n",
541 nodeString);
542 return (-3);
543 }
544
545 curptr = &nodeString[1]; /* past first / */
546 separator = strchr(curptr, '/');
547 if (separator == 0)
548 matchLength = strlen(curptr) + 1; /* +1 for leading / */
549 else
550 matchLength = (separator - &nodeString[0]);
551
552 /* printf("matchLength = %d\n", matchLength); */
553 while (1) {
554 /* now search all the nodes for this name */
555 for (dsptr = aconfig->dsched; dsptr != 0; dsptr = dsptr->next) {
556 /* printf("compare %s with %s for %d\n",
557 * dsptr->name, nodeString, matchLength); */
558 if (strlen(dsptr->name) != matchLength)
559 continue;
560
561 if (strncmp(dsptr->name, nodeString, matchLength) == 0) {
562 *nodeptr = dsptr;
563 break;
564 }
565 }
566
567 if (nodeString[matchLength] == 0) {
568 /* last node in the path */
569 if (*nodeptr)
570 return (0); /* all ok */
571 else
572 /* node not found; parent exists for non root nodes */
573 return (-1);
574 }
575
576 if (*nodeptr == 0)
577 /* failed to find a node in the path */
578 return (-2);
579
580 curptr = separator + 1;
581 if (*curptr == 0) {
582 printf("FindDump: trailing / in %s\n", nodeString);
583 return (-3);
584 }
585
586 separator = strchr(curptr, '/');
587 if (separator == 0)
588 matchLength = strlen(&nodeString[0]);
589 else
590 matchLength = separator - &nodeString[0];
591
592 *parentptr = *nodeptr;
593 *nodeptr = 0;
594 }
595 }