/* * dump-tree.c -- * * Operations to dump the OID tree in a human readable format. * * Copyright (c) 1999 Frank Strauss, Technical University of Braunschweig. * Copyright (c) 1999 J. Schoenwaelder, Technical University of Braunschweig. * Copyright (c) 2002 J. Schoenwaelder, University of Osnabrueck. * * See the file "COPYING" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * @(#) $Id: dump-tree.c 8090 2008-04-18 12:56:29Z strauss $ */ #include #include #include #include "smi.h" #include "smidump.h" static int pmodc = 0; static SmiModule **pmodv = NULL; static int ignoreconformance = 0; static int ignoreleafs = 0; static int full = 0; static int compact = 0; static char *getFlags(SmiNode *smiNode) { switch (smiNode->access) { case SMI_ACCESS_UNKNOWN: return "---"; case SMI_ACCESS_NOT_ACCESSIBLE: return "---"; case SMI_ACCESS_EVENT_ONLY: return "---"; case SMI_ACCESS_NOTIFY: return "--n"; case SMI_ACCESS_READ_ONLY: return "r-n"; case SMI_ACCESS_READ_WRITE: return "rwn"; case SMI_ACCESS_NOT_IMPLEMENTED: return "---"; case SMI_ACCESS_INSTALL: return "-i-"; case SMI_ACCESS_INSTALL_NOTIFY: return "-in"; case SMI_ACCESS_REPORT_ONLY: return "--r"; } return ""; } static char getStatusChar(SmiStatus status) { switch (status) { case SMI_STATUS_UNKNOWN: return '+'; case SMI_STATUS_CURRENT: return '+'; case SMI_STATUS_DEPRECATED: return 'x'; case SMI_STATUS_MANDATORY: return '+'; case SMI_STATUS_OPTIONAL: return '+'; case SMI_STATUS_OBSOLETE: return 'o'; } return ' '; } static char *getTypeName(SmiNode *smiNode) { char *type; SmiType *smiType, *parentType; smiType = smiGetNodeType(smiNode); if (!smiType || smiNode->nodekind == SMI_NODEKIND_TABLE) return NULL; if (smiType->decl == SMI_DECL_IMPLICIT_TYPE) { parentType = smiGetParentType(smiType); if (!parentType) return NULL; smiType = parentType; } type = xstrdup(smiType->name); return type; } static void fprintIndex(FILE *f, SmiNode *smiNode) { char *indexname; int i; SmiElement *smiElement; indexname = NULL; for (i = -1, smiElement = smiGetFirstElement(smiNode); smiElement; smiElement = smiGetNextElement(smiElement), i++) { if (i > 0) fprintf(f, ","); if (indexname) { fprintf(f, "%s", indexname); } indexname = smiGetElementNode(smiElement)->name; } if (indexname) { fprintf(f, "%s%s%s", (i > 0) ? "," : "", (smiNode->implied) ? "*" : "", indexname); } } static void fprintObjects(FILE *f, SmiNode *smiNode) { char *objectname; int i; SmiElement *smiElement; objectname = NULL; for (i = -1, smiElement = smiGetFirstElement(smiNode); smiElement; smiElement = smiGetNextElement(smiElement), i++) { if (i > 0) fprintf(f, ","); if (objectname) { fprintf(f, "%s", objectname); } objectname = smiGetElementNode(smiElement)->name; } if (objectname) { fprintf(f, "%s%s", (i > 0) ? "," : "", objectname); } } static int isPartOfLoadedModules(SmiNode *smiNode) { SmiModule *smiModule; int i; smiModule = smiGetNodeModule(smiNode); for (i = 0; i < pmodc; i++) { if (strcmp(pmodv[i]->name, smiModule->name) == 0) { return 1; } } return 0; } /* * The following function pruneSubTree() is tricky. There are some * interactions between the supported options. See the detailed * comments below. Good examples to test the implemented behaviour * are: * * smidump -u -f tree --tree-no-leaf IF-MIB ETHER-CHIPSET-MIB * * (And the example above does _not_ work in combination with * --tree-no-conformance so the code below is still broken.) */ static int pruneSubTree(SmiNode *smiNode) { SmiNode *childNode; const int confmask = (SMI_NODEKIND_GROUP | SMI_NODEKIND_COMPLIANCE); const int leafmask = (SMI_NODEKIND_GROUP | SMI_NODEKIND_COMPLIANCE | SMI_NODEKIND_COLUMN | SMI_NODEKIND_SCALAR | SMI_NODEKIND_ROW | SMI_NODEKIND_NOTIFICATION); if (! smiNode) { return 1; } /* * First, prune all nodes which the user has told us to ignore. * In the case of ignoreleafs, we have to special case nodes with * an unknown status (which actually represent OBJECT-IDENTITY * definitions). More special case code is needed to exclude * module identity nodes. */ if (ignoreconformance && (smiNode->nodekind & confmask)) { return 1; } if (ignoreleafs) { if (smiNode->nodekind & leafmask) { return 1; } if (smiNode->nodekind == SMI_NODEKIND_NODE && smiNode->status != SMI_STATUS_UNKNOWN) { SmiModule *smiModule = smiGetNodeModule(smiNode); if (smiModule && smiNode != smiGetModuleIdentityNode(smiModule)) { return 1; } } } /* * Next, generally do not prune nodes that belong to the set of * modules we are looking at. */ if (isPartOfLoadedModules(smiNode)) { if (!ignoreconformance || !smiGetFirstChildNode(smiNode)) { return 0; } } /* * Finally, prune all nodes where all child nodes are pruned. */ for (childNode = smiGetFirstChildNode(smiNode); childNode; childNode = smiGetNextChildNode(childNode)) { /* * In the case of ignoreleafs, we have to peek at the child * nodes. Otherwise, we would prune too much. we still want to * see the path to the leafs we have pruned away. This also * interact with the semantics of ignoreconformance since we * still want in combination with ignoreleafs to see the path * to the pruned conformance leafs. */ if (ignoreleafs && (childNode->nodekind & leafmask)) { if (isPartOfLoadedModules(childNode)) { if (ignoreconformance && (childNode->nodekind & confmask)) { return 1; } return 0; } } if (! pruneSubTree(childNode)) { return 0; } } return 1; } static void fprintSubTree(FILE *f, SmiNode *smiNode, char *prefix, size_t typefieldlen) { SmiNode *childNode, *indexNode; SmiNodekind lastNodeKind = SMI_NODEKIND_UNKNOWN; SmiType *type; int i = 0, cnt, prefixlen; size_t newtypefieldlen = 9; char c = 0; char *type_name; if (smiNode) { prefixlen = strlen(prefix); switch (smiNode->nodekind) { case SMI_NODEKIND_SCALAR: case SMI_NODEKIND_COLUMN: if (prefixlen > 0) { c = prefix[prefixlen-1]; prefix[prefixlen-1] = getStatusChar(smiNode->status); } type_name = getTypeName(smiNode); if (type_name) { fprintf(f, "%s-- %s %-*s %s(%u)\n", prefix, getFlags(smiNode), typefieldlen, type_name, smiNode->name, smiNode->oid[smiNode->oidlen-1]); xfree(type_name); } if (prefixlen > 0 && c) { prefix[prefixlen-1] = c; } break; case SMI_NODEKIND_ROW: if (prefixlen > 0) { c = prefix[prefixlen-1]; prefix[prefixlen-1] = getStatusChar(smiNode->status); } fprintf(f, "%s--%s(%u) [", prefix, smiNode->name, smiNode->oid[smiNode->oidlen-1]); switch (smiNode->indexkind) { case SMI_INDEX_INDEX: case SMI_INDEX_REORDER: fprintIndex(f, smiNode); break; case SMI_INDEX_EXPAND: /* TODO: we have to do more work here! */ break; case SMI_INDEX_AUGMENT: case SMI_INDEX_SPARSE: indexNode = smiGetRelatedNode(smiNode); if (indexNode) { fprintIndex(f, indexNode); } break; case SMI_INDEX_UNKNOWN: break; } fprintf(f, "]\n"); if (prefixlen > 0 && c) { prefix[prefixlen-1] = c; } break; case SMI_NODEKIND_NOTIFICATION: if (prefixlen > 0) { c = prefix[prefixlen-1]; prefix[prefixlen-1] = getStatusChar(smiNode->status); } fprintf(f, "%s--%s(%u) [", prefix, smiNode->name, smiNode->oid[smiNode->oidlen-1]); fprintObjects(f, smiNode); fprintf(f, "]\n"); if (prefixlen > 0 && c) { prefix[prefixlen-1] = c; } break; default: if (prefixlen > 0) { c = prefix[prefixlen-1]; prefix[prefixlen-1] = getStatusChar(smiNode->status); } if (smiNode->oid) if (prefixlen > 0) { fprintf(f, "%s--%s(%u)\n", prefix, smiNode->name ? smiNode->name : " ", smiNode->oid[smiNode->oidlen-1]); } else { unsigned int j; fprintf(f, "%s--%s(", prefix, smiNode->name ? smiNode->name : " "); for (j = 0; j < smiNode->oidlen; j++) { fprintf(f, "%s%u", j ? "." : "", smiNode->oid[j]); } fprintf(f, ")\n"); } else fprintf(f, "%s--%s(?)\n", prefix, smiNode->name ? smiNode->name : " "); if (prefixlen > 0 && c) { prefix[prefixlen-1] = c; } } for (childNode = smiGetFirstChildNode(smiNode), cnt = 0; childNode; childNode = smiGetNextChildNode(childNode)) { if (! pruneSubTree(childNode)) { type = smiGetNodeType(childNode); if (type) { type_name = getTypeName(childNode); if (type_name) { if (strlen(type_name) > newtypefieldlen) { newtypefieldlen = strlen(type_name); } xfree(type_name); } } cnt++; } } for (childNode = smiGetFirstChildNode(smiNode); childNode; childNode = smiGetNextChildNode(childNode)) { char *newprefix; if (pruneSubTree(childNode)) { continue; } i++; if (! compact && ((childNode->nodekind != SMI_NODEKIND_COLUMN && childNode->nodekind != SMI_NODEKIND_SCALAR) || (lastNodeKind != childNode->nodekind))) { fprintf(f, "%s |\n", prefix); } newprefix = xmalloc(strlen(prefix)+10); strcpy(newprefix, prefix); if (cnt == 1 || cnt == i) { strcat(newprefix, " "); } else { strcat(newprefix, " |"); } fprintSubTree(f, childNode, newprefix, newtypefieldlen); xfree(newprefix); lastNodeKind = childNode->nodekind; } } } static void fprintTree(FILE *f) { SmiNode *smiNode; SmiNode *childNode; SmiNode *nextNode; int cnt; smiNode = smiGetNode(NULL, "iso"); if (! full) { do { for (childNode = smiGetFirstChildNode(smiNode), cnt = 0, nextNode = NULL; childNode; childNode = smiGetNextChildNode(childNode)) { if (! pruneSubTree(childNode)) { cnt++; if (! nextNode) { nextNode = childNode; } } } if (cnt == 1) { smiNode = nextNode; } } while (cnt == 1); } if (smiNode) { fprintSubTree(f, smiNode, "", 0); } } static void dumpTree(int modc, SmiModule **modv, int flags, char *output) { int i; FILE *f = stdout; if (output) { f = fopen(output, "w"); if (!f) { fprintf(stderr, "smidump: cannot open %s for writing: ", output); perror(NULL); exit(1); } } if (flags & SMIDUMP_FLAG_UNITE) { pmodc = modc; pmodv = modv; if (! (flags & SMIDUMP_FLAG_SILENT)) { fprintf(f, "# united registration tree (generated by smidump " SMI_VERSION_STRING ")\n\n"); } if (! (flags & SMIDUMP_FLAG_SILENT) && (flags & SMIDUMP_FLAG_ERROR)) { fprintf(f, "# WARNING: this output may be incorrect due to " "significant parse errors\n\n"); } fprintTree(f); } else { for (i = 0; i < modc; i++) { pmodc = 1; pmodv = &(modv[i]); if (! (flags & SMIDUMP_FLAG_SILENT)) { fprintf(f, "# %s registration tree (generated by smidump " SMI_VERSION_STRING ")\n\n", modv[i]->name); } if (! (flags & SMIDUMP_FLAG_SILENT) && (flags & SMIDUMP_FLAG_ERROR)) { fprintf(f, "# WARNING: this output may be incorrect due to " "significant parse errors\n\n"); } fprintTree(f); } } if (fflush(f) || ferror(f)) { perror("smidump: write error"); exit(1); } if (output) { fclose(f); } } void initTree() { static SmidumpDriverOption opt[] = { { "no-conformance", OPT_FLAG, &ignoreconformance, 0, "do not show conformance nodes"}, { "no-leafs", OPT_FLAG, &ignoreleafs, 0, "do not show leaf nodes"}, { "full-root", OPT_FLAG, &full, 0, "generate the full path to the root"}, { "compact", OPT_FLAG, &compact, 0, "generate a more compact representation"}, { 0, OPT_END, 0, 0 } }; static SmidumpDriver driver = { "tree", dumpTree, SMI_FLAG_NODESCR, 0, "structure of the OID tree", opt, NULL }; smidumpRegisterDriver(&driver); }