/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ /* * ALL RIGHTS RESERVED * * (C) COPYRIGHT IBM CORPORATION 1987, 1998 * LICENSED MATERIALS - PROPERTY OF IBM */ #include #include #include #include #include #include #include "bc.h" #include "bucoord_internal.h" struct ubik_client *cstructp; /*Ptr to Ubik client structure */ static int FreeVolumeEntryList(struct bc_volumeEntry *aentry); static int FreeVolumeEntry(struct bc_volumeEntry *aentry); /* Code to maintain dump schedule and volume set abstractions. * A volume set looks like this: * vsname: servername partition-name * * A dump schedule looks like this: * dsname: vsname period parent-ds */ /* get partition id from a name */ afs_int32 bc_GetPartitionID(char *aname, afs_int32 *aval) { /*bc_GetPartitionID */ char tc; char ascii[3]; /* special-case "anything" */ if (strcmp(aname, ".*") == 0) { *aval = -1; return 0; } tc = *aname; if (tc == 0) return -1; /* unknown */ /* numbers go straight through */ if (tc >= '0' && tc <= '9') { *aval = bc_SafeATOI(aname); return 0; } /* otherwise check for vicepa or /vicepa, or just plain "a" */ ascii[2] = 0; if (strlen(aname) <= 2) { strcpy(ascii, aname); } else if (!strncmp(aname, "/vicep", 6)) { strncpy(ascii, aname + 6, 2); } else if (!strncmp(aname, "vicep", 5)) { strncpy(ascii, aname + 5, 2); } else return (BC_NOPARTITION); /* bad partition name */ /* now partitions are named /vicepa ... /vicepz, /vicepaa, /vicepab, .../vicepzz, and are numbered * from 0. Do the appropriate conversion */ if (ascii[1] == 0) { /* one char name, 0..25 */ if (ascii[0] < 'a' || ascii[0] > 'z') return -1; /* wrongo */ *aval = ascii[0] - 'a'; return 0; } else { /* two char name, 26 .. */ if (ascii[0] < 'a' || ascii[0] > 'z') return -1; /* wrongo */ if (ascii[1] < 'a' || ascii[1] > 'z') return -1; /* just as bad */ *aval = (ascii[0] - 'a') * 26 + (ascii[1] - 'a') + 26; return 0; } } /*bc_GetPartitionID */ /*---------------------------------------------------------------------------- * bc_ParseHost * * Description: * Given a string containing a host name or its IP address in dot notation, fill in * the given sockaddr with all the corresponding info. * * Arguments: * aname : Host name or dotted IP address. * asockaddr: Ptr to sockaddr to fill in for the above host. * * Returns: * 0 if everything went well, * -1 if it couldn't be translated. * * Environment: * Nothing interesting. * * Side Effects: * None. *---------------------------------------------------------------------------- */ int bc_ParseHost(char *aname, struct sockaddr_in *asockaddr) { /*bc_ParseHost */ struct hostent *th; /*Host entry */ afs_uint32 addr; /*Converted address */ afs_int32 b1, b2, b3, b4; /*Byte-sized address chunks */ afs_int32 code; /*Return code from sscanf() */ afs_int32 tmp1, tmp2; /* * Try to parse the given name as a dot-notation IP address first. */ code = sscanf(aname, "%d.%d.%d.%d", &b1, &b2, &b3, &b4); if (code == 4) { /* * Four chunks were read, so we assume success. Construct the socket. */ #ifdef STRUCT_SOCKADDR_HAS_SA_LEN asockaddr->sin_len = sizeof(struct sockaddr_in); #endif asockaddr->sin_family = AF_INET; asockaddr->sin_port = 0; addr = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; memcpy(&tmp1, &addr, sizeof(afs_int32)); tmp2 = htonl(tmp1); memcpy(&asockaddr->sin_addr.s_addr, &tmp2, sizeof(afs_int32)); return (0); } /* * The given string isn't a dotted IP address. Try to map it as a host * name, or leave it as a wild-card. */ if (strcmp(aname, ".*") == 0) { memset(asockaddr, 0, sizeof(struct sockaddr_in)); return 0; } th = gethostbyname(aname); if (!th) /* * No such luck, return failure. */ return (BC_NOHOST); /* * We found a mapping; construct the socket. */ #ifdef STRUCT_SOCKADDR_HAS_SA_LEN asockaddr->sin_len = sizeof(struct sockaddr_in); #endif asockaddr->sin_family = AF_INET; asockaddr->sin_port = 0; memcpy(&tmp1, th->h_addr, sizeof(afs_int32)); tmp2 = htonl(tmp1); memcpy(&(asockaddr->sin_addr.s_addr), &tmp2, sizeof(asockaddr->sin_addr.s_addr)); return (0); } /*bc_ParseHost */ /* create an empty volume set, new items are added via bc_AddVolumeItem */ int bc_CreateVolumeSet(struct bc_config *aconfig, char *avolName, afs_int32 aflags) { struct bc_volumeSet **tlast, *tset, *nset; if (bc_FindVolumeSet(aconfig, avolName)) return -1; /* already exists */ /* move to end of the list */ nset = calloc(1, sizeof(struct bc_volumeSet)); nset->flags = aflags; nset->name = strdup(avolName); if (aflags & VSFLAG_TEMPORARY) { /* Add to beginning of list */ nset->next = aconfig->vset; aconfig->vset = nset; } else { /* Add to end of list */ for (tlast = &aconfig->vset, tset = *tlast; tset; tlast = &tset->next, tset = *tlast); *tlast = nset; } return 0; } static int FreeVolumeEntry(struct bc_volumeEntry *aentry) { free(aentry->name); free(aentry->serverName); free(aentry->partname); free(aentry); return 0; } static int FreeVolumeEntryList(struct bc_volumeEntry *aentry) { struct bc_volumeEntry *tnext; while (aentry) { tnext = aentry->next; FreeVolumeEntry(aentry); aentry = tnext; } return 0; } void FreeVolumeSet(struct bc_volumeSet *avset) { FreeVolumeEntryList(avset->ventries); free(avset->name); free(avset); } int bc_DeleteVolumeSet(struct bc_config *aconfig, char *avolName, afs_int32 *flags) { struct bc_volumeSet **tlast, *tset; *flags = 0; tlast = &aconfig->vset; for (tset = *tlast; tset; tlast = &tset->next, tset = *tlast) { if (strcmp(avolName, tset->name) == 0) { *flags = tset->flags; /* Remember flags */ *tlast = tset->next; /* Remove from chain */ FreeVolumeSet(tset); /* Free the volume set */ return 0; } } /* if we get here, we didn't find the item */ return -1; } int bc_DeleteVolumeItem(struct bc_config *aconfig, char *avolName, afs_int32 anumber) { afs_int32 i; struct bc_volumeSet *tset; struct bc_volumeEntry *tentry, **tlast; tset = bc_FindVolumeSet(aconfig, avolName); if (!tset) return -1; tlast = &tset->ventries; for (i = 1, tentry = *tlast; tentry; tlast = &tentry->next, tentry = *tlast, i++) { if (anumber == i) { /* found entry we want */ *tlast = tentry->next; FreeVolumeEntry(tentry); return 0; } } return -2; /* not found */ } int bc_AddVolumeItem(struct bc_config *aconfig, char *avolName, char *ahost, char *apart, char *avol) { struct bc_volumeSet *tset; struct bc_volumeEntry **tlast, *tentry; afs_int32 code; tset = bc_FindVolumeSet(aconfig, avolName); if (!tset) return (BC_NOVOLSET); /* otherwise append this item to the end of the real list */ tlast = &tset->ventries; /* move to end of the list */ for (tentry = *tlast; tentry; tlast = &tentry->next, tentry = *tlast); tentry = calloc(1, sizeof(struct bc_volumeEntry)); tentry->serverName = strdup(ahost); tentry->partname = strdup(apart); tentry->name = strdup(avol); code = bc_ParseHost(tentry->serverName, &tentry->server); if (code) return (code); code = bc_GetPartitionID(tentry->partname, &tentry->partition); if (code) return (code); *tlast = tentry; /* thread on the list */ return 0; } struct bc_volumeSet * bc_FindVolumeSet(struct bc_config *aconfig, char *aname) { /*bc_FindVolumeSet */ struct bc_volumeSet *tvs; for (tvs = aconfig->vset; tvs; tvs = tvs->next) { if (!strcmp(tvs->name, aname)) return (tvs); } return (struct bc_volumeSet *)0; } /*bc_FindVolumeSet */ /* ------------------------------------ * dumpschedule management code * ------------------------------------ */ /* bc_CreateDumpSchedule * Add another node to the dump schedule. * entry: * aconfig - in core configuration structures * adumpName - name of new dump node * expDate - expiration date * expType - absolute or relative */ int bc_CreateDumpSchedule(struct bc_config *aconfig, char *adumpName, afs_int32 expDate, afs_int32 expType) { struct bc_dumpSchedule *tdump; struct bc_dumpSchedule *parent, *node; afs_int32 code; if (strcmp(adumpName, "none") == 0) return -2; /* invalid name */ code = FindDump(aconfig, adumpName, &parent, &node); if (code == 0) return -1; /* node already exists */ else if (code != -1) return -2; /* name specification error */ tdump = calloc(1, sizeof(struct bc_dumpSchedule)); /* prepend this node to the dump schedule list */ tdump->next = aconfig->dsched; aconfig->dsched = tdump; /* save the name of this dump node */ tdump->name = strdup(adumpName); /* expiration information */ tdump->expDate = expDate; tdump->expType = expType; bc_ProcessDumpSchedule(aconfig); /* redo tree */ return 0; } /* Recursively remove this node and all of its children from aconfig's * list of dumps. Note that this leaves the sibling pointers damaged (pointing * to strange places), so we must call bc_ProcessDumpSchedule when we're done. */ int bc_DeleteDumpScheduleAddr(struct bc_config *aconfig, struct bc_dumpSchedule *adumpAddr) { struct bc_dumpSchedule **tlast, *tdump; struct bc_dumpSchedule *tnext; /* knock off all children first */ for (tdump = adumpAddr->firstChild; tdump; tdump = tnext) { /* extract next ptr now, since will be freed by recursive call below */ tnext = tdump->nextSibling; bc_DeleteDumpScheduleAddr(aconfig, tdump); } /* finally, remove us from the list of good dudes */ for (tlast = &aconfig->dsched, tdump = *tlast; tdump; tlast = &tdump->next, tdump = *tlast) { if (tdump == adumpAddr) { /* found the one we're looking for */ *tlast = tdump->next; /* remove us from basic list */ free(tdump->name); free(tdump); return 0; } } return 0; } /* bc_FindDumpSchedule * Finds dump schedule aname by doing a linear search * entry: * aconfig - handle for incore configuration tables * aname - (path)name to match on * exit: * 0 for failure, ptr to dumpschedule for success */ struct bc_dumpSchedule * bc_FindDumpSchedule(struct bc_config *aconfig, char *aname) { struct bc_dumpSchedule *tds; for (tds = aconfig->dsched; tds; tds = tds->next) { if (!strcmp(tds->name, aname)) return tds; } return (struct bc_dumpSchedule *)0; } /* bc_DeleteDumpSchedule * Delete dump node adumpName from the dump schedule */ int bc_DeleteDumpSchedule(struct bc_config *aconfig, char *adumpName) { struct bc_dumpSchedule *tdump; /* does a linear search of the dump schedules in order to find * the one to delete */ for (tdump = aconfig->dsched; tdump; tdump = tdump->next) { if (strcmp(tdump->name, adumpName) == 0) { /* found it, we can zap recursively */ bc_DeleteDumpScheduleAddr(aconfig, tdump); /* tree's been pruned, but we have to recompute the internal pointers * from first principles, since we didn't bother to maintain * the sibling and children pointers during the call to delete * the nodes */ bc_ProcessDumpSchedule(aconfig); return 0; } } /* if we make it here, there's no such dump schedule entry */ return -1; } /* bc_ProcessDumpSchedule * Walk over the dump schedule list, building it into a tree. This * algorithm is simple, but takes O(N*2) operations to run, with N=number * of dump schedule nodes. It probably will never matter */ int bc_ProcessDumpSchedule(struct bc_config *aconfig) { struct bc_dumpSchedule *tds; struct bc_dumpSchedule *parentptr, *nodeptr; int retval; /* first, clear all the links on all entries so that this function * may be called any number of times with no ill effects */ for (tds = aconfig->dsched; tds; tds = tds->next) { tds->parent = (struct bc_dumpSchedule *)0; tds->nextSibling = (struct bc_dumpSchedule *)0; tds->firstChild = (struct bc_dumpSchedule *)0; } for (tds = aconfig->dsched; tds; tds = tds->next) { retval = FindDump(aconfig, tds->name, &parentptr, &nodeptr); if (retval != 0) { printf("bc_processdumpschedule: finddump returns %d\n", retval); exit(1); } /* only need to do work if it is not a root node */ if (parentptr != 0) { nodeptr->parent = parentptr; nodeptr->nextSibling = parentptr->firstChild; parentptr->firstChild = nodeptr; } } return 0; } /* FindDump * entry: * exit: * parentptr - set to parent node, if one exists * nodeptr - set to node requested * * return values are: * 0 - success, parentptr and nodeptr set appropriately * -1 - node not found, parent exists if reqd. Will be 0 for root nodes. * -2 - path search error. Some node on the path does not exist. * -3 - name specification error * notes: * pathname checking should be done externally. In particular, trailing * / symbols may return confusing error codes. (e.g on missing last * node returns -2 rather than -1) */ int FindDump(struct bc_config *aconfig, char *nodeString, struct bc_dumpSchedule **parentptr, struct bc_dumpSchedule **nodeptr) { struct bc_dumpSchedule *dsptr; char *separator; int matchLength; char *curptr; *parentptr = 0; *nodeptr = 0; /* ensure first char is correct separator */ if ((nodeString[0] != '/') || (strlen(&nodeString[0]) <= 1) ) { printf("FindDump: %s, error in dump name specification\n", nodeString); return (-3); } curptr = &nodeString[1]; /* past first / */ separator = strchr(curptr, '/'); if (separator == 0) matchLength = strlen(curptr) + 1; /* +1 for leading / */ else matchLength = (separator - &nodeString[0]); /* printf("matchLength = %d\n", matchLength); */ while (1) { /* now search all the nodes for this name */ for (dsptr = aconfig->dsched; dsptr != 0; dsptr = dsptr->next) { /* printf("compare %s with %s for %d\n", * dsptr->name, nodeString, matchLength); */ if (strlen(dsptr->name) != matchLength) continue; if (strncmp(dsptr->name, nodeString, matchLength) == 0) { *nodeptr = dsptr; break; } } if (nodeString[matchLength] == 0) { /* last node in the path */ if (*nodeptr) return (0); /* all ok */ else /* node not found; parent exists for non root nodes */ return (-1); } if (*nodeptr == 0) /* failed to find a node in the path */ return (-2); curptr = separator + 1; if (*curptr == 0) { printf("FindDump: trailing / in %s\n", nodeString); return (-3); } separator = strchr(curptr, '/'); if (separator == 0) matchLength = strlen(&nodeString[0]); else matchLength = separator - &nodeString[0]; *parentptr = *nodeptr; *nodeptr = 0; } }