/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * alloc.c -- memory allocation wrapper functions, replacable in more * constrained environments, such as within a DE. */ #include #include #include "alloc.h" #include "out.h" #include "stats.h" static struct stats *Malloctotal; static struct stats *Malloccount; void alloc_init(void) { Malloctotal = stats_new_counter("alloc.total", "bytes allocated", 1); Malloccount = stats_new_counter("alloc.calls", "total calls", 1); } void alloc_fini(void) { struct stats *mt, *mc; mt = Malloctotal; mc = Malloccount; Malloctotal = NULL; Malloccount = NULL; stats_delete(mt); stats_delete(mc); } /* * alloc_malloc -- a malloc() with checks * * this routine is typically called via the MALLOC() macro in alloc.h */ void * alloc_malloc(size_t nbytes, const char *fname, int line) { void *retval = malloc(nbytes); if (retval == NULL) outfl(O_DIE, fname, line, "malloc: out of memory"); if (Malloctotal) stats_counter_add(Malloctotal, nbytes); if (Malloccount) stats_counter_bump(Malloccount); return (retval); } /* * alloc_realloc -- a realloc() with checks * * this routine is typically called via the REALLOC() macro in alloc.h */ void * alloc_realloc(void *ptr, size_t nbytes, const char *fname, int line) { void *retval = realloc(ptr, nbytes); if (retval == NULL) out(O_DIE, fname, line, "realloc: out of memory"); return (retval); } /* * alloc_strdup -- a strdup() with checks * * this routine is typically called via the STRDUP() macro in alloc.h */ char * alloc_strdup(const char *ptr, const char *fname, int line) { char *retval = strdup(ptr); if (retval == NULL) outfl(O_DIE, fname, line, "strdup: out of memory"); return (retval); } /* * alloc_free -- a free() with checks * * this routine is typically called via the FREE() macro in alloc.h */ /*ARGSUSED1*/ void alloc_free(void *ptr, const char *fname, int line) { /* nothing to check in this version */ free(ptr); } /* * variants that don't maintain size in header - saves space */ void * alloc_xmalloc(size_t nbytes) { void *retval; retval = malloc(nbytes); if (retval == NULL) out(O_DIE, "malloc: out of memory"); if (Malloctotal) stats_counter_add(Malloctotal, nbytes); if (Malloccount) stats_counter_bump(Malloccount); return (retval); } /*ARGSUSED*/ void alloc_xfree(void *ptr, size_t size) { free(ptr); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * alloc.h -- public definitions for memory allocation module * */ #ifndef _ESC_COMMON_ALLOC_H #define _ESC_COMMON_ALLOC_H #ifdef __cplusplus extern "C" { #endif void alloc_init(void); void alloc_fini(void); void *alloc_realloc(void *ptr, size_t nbytes, const char *fname, int line); void *alloc_malloc(size_t nbytes, const char *fname, int line); void alloc_free(void *ptr, const char *fname, int line); char *alloc_strdup(const char *ptr, const char *fname, int line); void *alloc_xmalloc(size_t size); void alloc_xfree(void *ptr, size_t size); #ifdef DEBUG #define MALLOC(nbytes) alloc_malloc(nbytes, __FILE__, __LINE__) #define REALLOC(ptr, nbytes) alloc_realloc(ptr, nbytes, __FILE__, __LINE__) #define FREE(ptr) alloc_free(ptr, __FILE__, __LINE__) #define STRDUP(ptr) alloc_strdup(ptr, __FILE__, __LINE__) #else #define MALLOC(nbytes) alloc_malloc(nbytes, "???", __LINE__) #define REALLOC(ptr, nbytes) alloc_realloc(ptr, nbytes, "???", __LINE__) #define FREE(ptr) alloc_free(ptr, "???", __LINE__) #define STRDUP(ptr) alloc_strdup(ptr, "???", __LINE__) #endif #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_ALLOC_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * check.c -- routines for checking the prop tree * * this module provides semantic checks on the parse tree. most of * these checks happen during the construction of the parse tree, * when the various tree_X() routines call the various check_X() * routines. in a couple of special cases, a check function will * process the parse tree after it has been fully constructed. these * cases are noted in the comments above the check function. */ #include #include "out.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "tree.h" #include "ptree.h" #include "check.h" static int check_reportlist(enum nodetype t, const char *s, struct node *np); static int check_num(enum nodetype t, const char *s, struct node *np); static int check_quote(enum nodetype t, const char *s, struct node *np); static int check_action(enum nodetype t, const char *s, struct node *np); static int check_num_func(enum nodetype t, const char *s, struct node *np); static int check_fru_asru(enum nodetype t, const char *s, struct node *np); static int check_engine(enum nodetype t, const char *s, struct node *np); static int check_count(enum nodetype t, const char *s, struct node *np); static int check_timeval(enum nodetype t, const char *s, struct node *np); static int check_id(enum nodetype t, const char *s, struct node *np); static int check_serd_method(enum nodetype t, const char *s, struct node *np); static int check_serd_id(enum nodetype t, const char *s, struct node *np); static int check_nork(struct node *np); static void check_cycle_lhs(struct node *stmtnp, struct node *arrow); static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs); static void check_cycle_rhs(struct node *rhs); static void check_proplists_lhs(enum nodetype t, struct node *lhs); static struct { enum nodetype t; const char *name; int required; int (*checker)(enum nodetype t, const char *s, struct node *np); int outflags; } Allowednames[] = { { T_FAULT, "FITrate", 0, check_num_func, O_ERR }, { T_FAULT, "FRU", 0, check_fru_asru, O_ERR }, { T_FAULT, "ASRU", 0, check_fru_asru, O_ERR }, { T_FAULT, "message", 0, check_num_func, O_ERR }, { T_FAULT, "retire", 0, check_num_func, O_ERR }, { T_FAULT, "response", 0, check_num_func, O_ERR }, { T_FAULT, "action", 0, check_action, O_ERR }, { T_FAULT, "count", 0, check_count, O_ERR }, { T_FAULT, "engine", 0, check_engine, O_ERR }, { T_UPSET, "engine", 0, check_engine, O_ERR }, { T_DEFECT, "FRU", 0, check_fru_asru, O_ERR }, { T_DEFECT, "ASRU", 0, check_fru_asru, O_ERR }, { T_DEFECT, "engine", 0, check_engine, O_ERR }, { T_DEFECT, "FITrate", 0, check_num_func, O_ERR }, { T_EREPORT, "poller", 0, check_id, O_ERR }, { T_EREPORT, "delivery", 0, check_timeval, O_ERR }, { T_EREPORT, "discard_if_config_unknown", 0, check_num, O_ERR }, { T_SERD, "N", 1, check_num, O_ERR }, { T_SERD, "T", 1, check_timeval, O_ERR }, { T_SERD, "method", 0, check_serd_method, O_ERR }, { T_SERD, "trip", 0, check_reportlist, O_ERR }, { T_SERD, "FRU", 0, check_fru_asru, O_ERR }, { T_SERD, "id", 0, check_serd_id, O_ERR }, { T_ERROR, "ASRU", 0, check_fru_asru, O_ERR }, { T_CONFIG, NULL, 0, check_quote, O_ERR }, { 0, NULL, 0 }, }; void check_init(void) { int i; for (i = 0; Allowednames[i].t; i++) if (Allowednames[i].name != NULL) Allowednames[i].name = stable(Allowednames[i].name); } void check_fini(void) { } /*ARGSUSED*/ void check_report_combination(struct node *np) { /* nothing to check for here. poller is only prop and it is optional */ } /* * check_path_iterators -- verify all iterators are explicit */ static void check_path_iterators(struct node *np) { if (np == NULL) return; switch (np->t) { case T_ARROW: check_path_iterators(np->u.arrow.lhs); check_path_iterators(np->u.arrow.rhs); break; case T_LIST: check_path_iterators(np->u.expr.left); check_path_iterators(np->u.expr.right); break; case T_EVENT: check_path_iterators(np->u.event.epname); break; case T_NAME: if (np->u.name.child == NULL) outfl(O_DIE, np->file, np->line, "internal error: check_path_iterators: " "unexpected implicit iterator: %s", np->u.name.s); check_path_iterators(np->u.name.next); break; default: outfl(O_DIE, np->file, np->line, "internal error: check_path_iterators: " "unexpected type: %s", ptree_nodetype2str(np->t)); } } void check_arrow(struct node *np) { ASSERTinfo(np->t == T_ARROW, ptree_nodetype2str(np->t)); if (np->u.arrow.lhs->t != T_ARROW && np->u.arrow.lhs->t != T_LIST && np->u.arrow.lhs->t != T_EVENT) { outfl(O_ERR, np->u.arrow.lhs->file, np->u.arrow.lhs->line, "%s not allowed on left-hand side of arrow", ptree_nodetype2str(np->u.arrow.lhs->t)); } if (!check_nork(np->u.arrow.nnp) || !check_nork(np->u.arrow.knp)) outfl(O_ERR, np->file, np->line, "counts associated with propagation arrows " "must be integers"); check_path_iterators(np); } /* * make sure the nork values are valid. * Nork values must be "A" for all(T_NAME), * a number(T_NUM), or a simple * expression(T_SUB, T_ADD, T_MUL, T_DIV) */ static int check_nork(struct node *np) { int rval = 0; /* NULL means no nork value which is allowed */ if (np == NULL) { rval = 1; } else { /* if the nork is a name it must be A for "All" */ if (np->t == T_NAME) if (*np->u.name.s == 'A') return (1); /* T_NUM allowed */ if (np->t == T_NUM) rval = 1; /* simple expressions allowed */ if (np->t == T_SUB || np->t == T_ADD || np->t == T_MUL || np->t == T_DIV) rval = 1; } return (rval); } static int check_reportlist(enum nodetype t, const char *s, struct node *np) { if (np == NULL) return (1); else if (np->t == T_EVENT) { if (np->u.event.ename->u.name.t != N_EREPORT) { outfl(O_ERR, np->file, np->line, "%s %s property must begin with \"ereport.\"", ptree_nodetype2str(t), s); } else if (tree_event2np_lut_lookup(Ereports, np) == NULL) { outfl(O_ERR, np->file, np->line, "%s %s property contains undeclared name", ptree_nodetype2str(t), s); } check_type_iterator(np); } else if (np->t == T_LIST) { (void) check_reportlist(t, s, np->u.expr.left); (void) check_reportlist(t, s, np->u.expr.right); } return (1); } static int check_num(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_NUM) outfl(O_ERR, np->file, np->line, "%s %s property must be a single number", ptree_nodetype2str(t), s); return (1); } /*ARGSUSED1*/ static int check_quote(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_QUOTE) outfl(O_ERR, np->file, np->line, "%s properties must be quoted strings", ptree_nodetype2str(t)); return (1); } static int check_action(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_FUNC) outfl(O_ERR, np->file, np->line, "%s %s property must be a function or list of functions", ptree_nodetype2str(t), s); return (1); } static int check_num_func(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_NUM && np->t != T_FUNC) outfl(O_ERR, np->file, np->line, "%s %s property must be a number or function", ptree_nodetype2str(t), s); return (1); } static int check_fru_asru(enum nodetype t, const char *s, struct node *np) { ASSERT(s != NULL); /* make sure it is a node type T_NAME? */ if (np->t == T_NAME) { if (s == L_ASRU) { if (tree_name2np_lut_lookup_name(ASRUs, np) == NULL) outfl(O_ERR, np->file, np->line, "ASRU property contains undeclared asru"); } else if (s == L_FRU) { if (tree_name2np_lut_lookup_name(FRUs, np) == NULL) outfl(O_ERR, np->file, np->line, "FRU property contains undeclared fru"); } else { outfl(O_ERR, np->file, np->line, "illegal property name in %s declaration: %s", ptree_nodetype2str(t), s); } check_type_iterator(np); } else outfl(O_ERR, np->file, np->line, "illegal type used for %s property: %s", s, ptree_nodetype2str(np->t)); return (1); } static int check_engine(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_EVENT) outfl(O_ERR, np->file, np->line, "%s %s property must be an engine name " "(i.e. serd.x or serd.x@a/b)", ptree_nodetype2str(t), s); return (1); } static int check_count(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_EVENT) outfl(O_ERR, np->file, np->line, "%s %s property must be an engine name " "(i.e. stat.x or stat.x@a/b)", ptree_nodetype2str(t), s); /* XXX confirm engine has been declared */ return (1); } static int check_timeval(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_TIMEVAL) outfl(O_ERR, np->file, np->line, "%s %s property must be a number with time units", ptree_nodetype2str(t), s); return (1); } static int check_id(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_NAME || np->u.name.next || np->u.name.child) outfl(O_ERR, np->file, np->line, "%s %s property must be simple name", ptree_nodetype2str(t), s); return (1); } static int check_serd_method(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_NAME || np->u.name.next || np->u.name.child || (np->u.name.s != L_volatile && np->u.name.s != L_persistent)) outfl(O_ERR, np->file, np->line, "%s %s property must be \"volatile\" or \"persistent\"", ptree_nodetype2str(t), s); return (1); } static int check_serd_id(enum nodetype t, const char *s, struct node *np) { ASSERTinfo(np != NULL, ptree_nodetype2str(t)); if (np->t != T_GLOBID) outfl(O_ERR, np->file, np->line, "%s %s property must be a global ID", ptree_nodetype2str(t), s); return (1); } void check_stmt_required_properties(struct node *stmtnp) { struct lut *lutp = stmtnp->u.stmt.lutp; struct node *np = stmtnp->u.stmt.np; int i; for (i = 0; Allowednames[i].t; i++) if (stmtnp->t == Allowednames[i].t && Allowednames[i].required && tree_s2np_lut_lookup(lutp, Allowednames[i].name) == NULL) outfl(Allowednames[i].outflags, np->file, np->line, "%s statement missing property: %s", ptree_nodetype2str(stmtnp->t), Allowednames[i].name); } void check_stmt_allowed_properties(enum nodetype t, struct node *nvpairnp, struct lut *lutp) { int i; const char *s = nvpairnp->u.expr.left->u.name.s; struct node *np; for (i = 0; Allowednames[i].t; i++) if (t == Allowednames[i].t && Allowednames[i].name == NULL) { /* NULL name means just call checker */ (*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right); return; } else if (t == Allowednames[i].t && s == Allowednames[i].name) break; if (Allowednames[i].name == NULL) outfl(O_ERR, nvpairnp->file, nvpairnp->line, "illegal property name in %s declaration: %s", ptree_nodetype2str(t), s); else if ((np = tree_s2np_lut_lookup(lutp, s)) != NULL) { /* * redeclaring prop is allowed if value is the same */ if (np->t != nvpairnp->u.expr.right->t) outfl(O_ERR, nvpairnp->file, nvpairnp->line, "property redeclared (with differnt type) " "in %s declaration: %s", ptree_nodetype2str(t), s); switch (np->t) { case T_NUM: case T_TIMEVAL: if (np->u.ull == nvpairnp->u.expr.right->u.ull) return; break; case T_NAME: if (tree_namecmp(np, nvpairnp->u.expr.right) == 0) return; break; case T_EVENT: if (tree_eventcmp(np, nvpairnp->u.expr.right) == 0) return; break; default: outfl(O_ERR, nvpairnp->file, nvpairnp->line, "value for property \"%s\" is an " "invalid type: %s", nvpairnp->u.expr.left->u.name.s, ptree_nodetype2str(np->t)); return; } outfl(O_ERR, nvpairnp->file, nvpairnp->line, "property redeclared in %s declaration: %s", ptree_nodetype2str(t), s); } else (*Allowednames[i].checker)(t, s, nvpairnp->u.expr.right); } void check_propnames(enum nodetype t, struct node *np, int from, int to) { struct node *dnp; struct lut *lutp; ASSERT(np != NULL); ASSERTinfo(np->t == T_EVENT || np->t == T_LIST || np->t == T_ARROW, ptree_nodetype2str(np->t)); if (np->t == T_EVENT) { switch (np->u.event.ename->u.name.t) { case N_UNSPEC: outfl(O_ERR, np->file, np->line, "name in %s statement must begin with " "type (example: \"error.\")", ptree_nodetype2str(t)); return; case N_FAULT: lutp = Faults; if (to) { outfl(O_ERR, np->file, np->line, "%s has fault on right side of \"->\"", ptree_nodetype2str(t)); return; } if (!from) { outfl(O_DIE, np->file, np->line, "internal error: %s has fault without " "from flag", ptree_nodetype2str(t)); } break; case N_UPSET: lutp = Upsets; if (to) { outfl(O_ERR, np->file, np->line, "%s has upset on right side of \"->\"", ptree_nodetype2str(t)); return; } if (!from) outfl(O_DIE, np->file, np->line, "internal error: %s has upset without " "from flag", ptree_nodetype2str(t)); break; case N_DEFECT: lutp = Defects; if (to) { outfl(O_ERR, np->file, np->line, "%s has defect on right side of \"->\"", ptree_nodetype2str(t)); return; } if (!from) { outfl(O_DIE, np->file, np->line, "internal error: %s has defect without " "from flag", ptree_nodetype2str(t)); } break; case N_ERROR: lutp = Errors; if (!from && !to) outfl(O_DIE, np->file, np->line, "%s has error without from or to flags", ptree_nodetype2str(t)); break; case N_EREPORT: lutp = Ereports; if (from) { outfl(O_ERR, np->file, np->line, "%s has report on left side of \"->\"", ptree_nodetype2str(t)); return; } if (!to) outfl(O_DIE, np->file, np->line, "internal error: %s has report without " "to flag", ptree_nodetype2str(t)); break; default: outfl(O_DIE, np->file, np->line, "internal error: check_propnames: " "unexpected type: %d", np->u.name.t); } if ((dnp = tree_event2np_lut_lookup(lutp, np)) == NULL) { outfl(O_ERR, np->file, np->line, "%s statement contains undeclared event", ptree_nodetype2str(t)); } else dnp->u.stmt.flags |= STMT_REF; np->u.event.declp = dnp; } else if (np->t == T_LIST) { check_propnames(t, np->u.expr.left, from, to); check_propnames(t, np->u.expr.right, from, to); } else if (np->t == T_ARROW) { check_propnames(t, np->u.arrow.lhs, 1, to); check_propnames(t, np->u.arrow.rhs, from, 1); } } static struct lut * record_iterators(struct node *np, struct lut *ex) { if (np == NULL) return (ex); switch (np->t) { case T_ARROW: ex = record_iterators(np->u.arrow.lhs, ex); ex = record_iterators(np->u.arrow.rhs, ex); break; case T_LIST: ex = record_iterators(np->u.expr.left, ex); ex = record_iterators(np->u.expr.right, ex); break; case T_EVENT: ex = record_iterators(np->u.event.epname, ex); break; case T_NAME: if (np->u.name.child && np->u.name.child->t == T_NAME) ex = lut_add(ex, (void *) np->u.name.child->u.name.s, (void *) np, NULL); ex = record_iterators(np->u.name.next, ex); break; default: outfl(O_DIE, np->file, np->line, "record_iterators: internal error: unexpected type: %s", ptree_nodetype2str(np->t)); } return (ex); } void check_exprscope(struct node *np, struct lut *ex) { if (np == NULL) return; switch (np->t) { case T_EVENT: check_exprscope(np->u.event.eexprlist, ex); break; case T_ARROW: check_exprscope(np->u.arrow.lhs, ex); check_exprscope(np->u.arrow.rhs, ex); break; case T_NAME: if (np->u.name.child && np->u.name.child->t == T_NAME) { if (lut_lookup(ex, (void *) np->u.name.child->u.name.s, NULL) == NULL) outfl(O_ERR, np->file, np->line, "constraint contains undefined" " iterator: %s", np->u.name.child->u.name.s); } check_exprscope(np->u.name.next, ex); break; case T_QUOTE: case T_GLOBID: break; case T_ASSIGN: case T_NE: case T_EQ: case T_LIST: case T_AND: case T_OR: case T_NOT: case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: case T_LT: case T_LE: case T_GT: case T_GE: case T_BITAND: case T_BITOR: case T_BITXOR: case T_BITNOT: case T_LSHIFT: case T_RSHIFT: case T_CONDIF: case T_CONDELSE: check_exprscope(np->u.expr.left, ex); check_exprscope(np->u.expr.right, ex); break; case T_FUNC: check_exprscope(np->u.func.arglist, ex); break; case T_NUM: case T_TIMEVAL: break; default: outfl(O_DIE, np->file, np->line, "check_exprscope: internal error: unexpected type: %s", ptree_nodetype2str(np->t)); } } /* * check_propscope -- check constraints for out of scope variable refs */ void check_propscope(struct node *np) { struct lut *ex; ex = record_iterators(np, NULL); check_exprscope(np, ex); lut_free(ex, NULL, NULL); } /* * check_upset_engine -- validate the engine property in an upset statement * * we do this after the full parse tree has been constructed rather than while * building the parse tree because it is inconvenient for the user if we * require SERD engines to be declared before used in an upset "engine" * property. */ /*ARGSUSED*/ void check_upset_engine(struct node *lhs, struct node *rhs, void *arg) { enum nodetype t = (enum nodetype)arg; struct node *engnp; struct node *declp; ASSERTeq(rhs->t, t, ptree_nodetype2str); if ((engnp = tree_s2np_lut_lookup(rhs->u.stmt.lutp, L_engine)) == NULL) return; ASSERT(engnp->t == T_EVENT); if ((declp = tree_event2np_lut_lookup(SERDs, engnp)) == NULL) { outfl(O_ERR, engnp->file, engnp->line, "%s %s property contains undeclared name", ptree_nodetype2str(t), L_engine); return; } engnp->u.event.declp = declp; } /* * check_refcount -- see if declared names are used * * this is run after the entire parse tree is constructed, so a refcount * of zero means the name has been declared but otherwise not used. */ void check_refcount(struct node *lhs, struct node *rhs, void *arg) { enum nodetype t = (enum nodetype)arg; ASSERTeq(rhs->t, t, ptree_nodetype2str); if (rhs->u.stmt.flags & STMT_REF) return; outfl(O_WARN|O_NONL, rhs->file, rhs->line, "%s name declared but not used: ", ptree_nodetype2str(t)); ptree_name(O_WARN|O_NONL, lhs); out(O_WARN, NULL); } /* * set check_cycle_warninglevel only for val >= 0 */ int check_cycle_level(long long val) { static int check_cycle_warninglevel = -1; if (val == 0) check_cycle_warninglevel = 0; else if (val > 0) check_cycle_warninglevel = 1; return (check_cycle_warninglevel); } /* * check_cycle -- see props from an error have cycles * * this is run after the entire parse tree is constructed, for * each error that has been declared. */ /*ARGSUSED*/ void check_cycle(struct node *lhs, struct node *rhs, void *arg) { struct node *np; ASSERTeq(rhs->t, T_ERROR, ptree_nodetype2str); if (rhs->u.stmt.flags & STMT_CYCLE) return; /* already reported this cycle */ if (rhs->u.stmt.flags & STMT_CYMARK) { #ifdef ESC int warninglevel; warninglevel = check_cycle_level(-1); if (warninglevel <= 0) { int olevel = O_ERR; if (warninglevel == 0) olevel = O_WARN; out(olevel|O_NONL, "cycle in propagation tree: "); ptree_name(olevel|O_NONL, rhs->u.stmt.np); out(olevel, NULL); } #endif /* ESC */ rhs->u.stmt.flags |= STMT_CYCLE; } rhs->u.stmt.flags |= STMT_CYMARK; /* for each propagation */ for (np = Props; np; np = np->u.stmt.next) check_cycle_lhs(rhs, np->u.stmt.np); rhs->u.stmt.flags &= ~STMT_CYMARK; } /* * check_cycle_lhs -- find the lhs of an arrow for cycle checking */ static void check_cycle_lhs(struct node *stmtnp, struct node *arrow) { struct node *trylhs; struct node *tryrhs; /* handle cascaded arrows */ switch (arrow->u.arrow.lhs->t) { case T_ARROW: /* first recurse left */ check_cycle_lhs(stmtnp, arrow->u.arrow.lhs); /* * return if there's a list of events internal to * cascaded props (which is not allowed) */ if (arrow->u.arrow.lhs->u.arrow.rhs->t != T_EVENT) return; /* then try this arrow (thing cascaded *to*) */ trylhs = arrow->u.arrow.lhs->u.arrow.rhs; tryrhs = arrow->u.arrow.rhs; break; case T_EVENT: case T_LIST: trylhs = arrow->u.arrow.lhs; tryrhs = arrow->u.arrow.rhs; break; default: out(O_DIE, "lhs: unexpected type: %s", ptree_nodetype2str(arrow->u.arrow.lhs->t)); /*NOTREACHED*/ } check_cycle_lhs_try(stmtnp, trylhs, tryrhs); } /* * check_cycle_lhs_try -- try matching an event name on lhs of an arrow */ static void check_cycle_lhs_try(struct node *stmtnp, struct node *lhs, struct node *rhs) { if (lhs->t == T_LIST) { check_cycle_lhs_try(stmtnp, lhs->u.expr.left, rhs); check_cycle_lhs_try(stmtnp, lhs->u.expr.right, rhs); return; } ASSERT(lhs->t == T_EVENT); if (tree_eventcmp(stmtnp->u.stmt.np, lhs) != 0) return; /* no match */ check_cycle_rhs(rhs); } /* * check_cycle_rhs -- foreach error on rhs, see if we cycle to a marked error */ static void check_cycle_rhs(struct node *rhs) { struct node *dnp; if (rhs->t == T_LIST) { check_cycle_rhs(rhs->u.expr.left); check_cycle_rhs(rhs->u.expr.right); return; } ASSERT(rhs->t == T_EVENT); if (rhs->u.event.ename->u.name.t != N_ERROR) return; if ((dnp = tree_event2np_lut_lookup(Errors, rhs)) == NULL) { outfl(O_ERR|O_NONL, rhs->file, rhs->line, "unexpected undeclared event during cycle check"); ptree_name(O_ERR|O_NONL, rhs); out(O_ERR, NULL); return; } check_cycle(NULL, dnp, 0); } /* * Force iterators to be simple names, expressions, or numbers */ void check_name_iterator(struct node *np) { if (np->u.name.child->t != T_NUM && np->u.name.child->t != T_NAME && np->u.name.child->t != T_CONDIF && np->u.name.child->t != T_SUB && np->u.name.child->t != T_ADD && np->u.name.child->t != T_MUL && np->u.name.child->t != T_DIV && np->u.name.child->t != T_MOD && np->u.name.child->t != T_LSHIFT && np->u.name.child->t != T_RSHIFT) { outfl(O_ERR|O_NONL, np->file, np->line, "invalid iterator: "); ptree_name_iter(O_ERR|O_NONL, np); out(O_ERR, NULL); } } /* * Iterators on a declaration may only be implicit */ void check_type_iterator(struct node *np) { while (np != NULL) { if (np->t == T_EVENT) { np = np->u.event.epname; } else if (np->t == T_NAME) { if (np->u.name.child != NULL && np->u.name.child->t != T_NUM) { outfl(O_ERR|O_NONL, np->file, np->line, "explicit iterators disallowed " "in declarations: "); ptree_name_iter(O_ERR|O_NONL, np); out(O_ERR, NULL); } np = np->u.name.next; } else { break; } } } void check_cat_list(struct node *np) { if (np->t == T_FUNC) check_func(np); else if (np->t == T_LIST) { check_cat_list(np->u.expr.left); check_cat_list(np->u.expr.right); } } void check_func(struct node *np) { struct node *arglist = np->u.func.arglist; ASSERTinfo(np->t == T_FUNC, ptree_nodetype2str(np->t)); if (np->u.func.s == L_within) { switch (arglist->t) { case T_NUM: if (arglist->u.ull != 0ULL) { outfl(O_ERR, arglist->file, arglist->line, "parameter of within must be 0" ", \"infinity\" or a time value."); } break; case T_NAME: if (arglist->u.name.s != L_infinity) { outfl(O_ERR, arglist->file, arglist->line, "parameter of within must be 0" ", \"infinity\" or a time value."); } break; case T_LIST: /* * if two parameters, the left or min must be * either T_NUM or T_TIMEVAL */ if (arglist->u.expr.left->t != T_NUM && arglist->u.expr.left->t != T_TIMEVAL) { outfl(O_ERR, arglist->file, arglist->line, "first parameter of within must be" " either a time value or zero."); } /* * if two parameters, the right or max must * be either T_NUM, T_NAME or T_TIMEVAL */ if (arglist->u.expr.right->t != T_NUM && arglist->u.expr.right->t != T_TIMEVAL && arglist->u.expr.right->t != T_NAME) { outfl(O_ERR, arglist->file, arglist->line, "second parameter of within must " "be 0, \"infinity\" or time value."); } /* * if right or left is a T_NUM it must * be zero */ if ((arglist->u.expr.left->t == T_NUM) && (arglist->u.expr.left->u.ull != 0ULL)) { outfl(O_ERR, arglist->file, arglist->line, "within parameter must be " "0 or a time value."); } if ((arglist->u.expr.right->t == T_NUM) && (arglist->u.expr.right->u.ull != 0ULL)) { outfl(O_ERR, arglist->file, arglist->line, "within parameter must be " "0 or a time value."); } /* if right is a T_NAME it must be "infinity" */ if ((arglist->u.expr.right->t == T_NAME) && (arglist->u.expr.right->u.name.s != L_infinity)) { outfl(O_ERR, arglist->file, arglist->line, "\"infinity\" is the only " "valid name for within parameter."); } /* * the first parameter [min] must not be greater * than the second parameter [max]. */ if (arglist->u.expr.left->u.ull > arglist->u.expr.right->u.ull) { outfl(O_ERR, arglist->file, arglist->line, "the first value (min) of" " within must be less than" " the second (max) value"); } break; case T_TIMEVAL: break; /* no restrictions on T_TIMEVAL */ default: outfl(O_ERR, arglist->file, arglist->line, "parameter of within must be 0" ", \"infinity\" or a time value."); } } else if (np->u.func.s == L_call) { if (arglist->t != T_QUOTE && arglist->t != T_LIST && arglist->t != T_GLOBID && arglist->t != T_CONDIF && arglist->t != T_LIST && arglist->t != T_FUNC) outfl(O_ERR, arglist->file, arglist->line, "invalid first argument to call()"); } else if (np->u.func.s == L_fru) { if (arglist->t != T_NAME) outfl(O_ERR, arglist->file, arglist->line, "argument to fru() must be a path"); } else if (np->u.func.s == L_asru) { if (arglist->t != T_NAME) outfl(O_ERR, arglist->file, arglist->line, "argument to asru() must be a path"); } else if (np->u.func.s == L_is_connected || np->u.func.s == L_is_under) { if (arglist->t == T_LIST && (arglist->u.expr.left->t == T_NAME || arglist->u.expr.left->t == T_FUNC) && (arglist->u.expr.right->t == T_NAME || arglist->u.expr.right->t == T_FUNC)) { if (arglist->u.expr.left->t == T_FUNC) check_func(arglist->u.expr.left); if (arglist->u.expr.right->t == T_FUNC) check_func(arglist->u.expr.right); } else { outfl(O_ERR, arglist->file, arglist->line, "%s() must have paths or calls to " "fru() and/or asru() as arguments", np->u.func.s); } } else if (np->u.func.s == L_is_on) { if (arglist->t == T_NAME || arglist->t == T_FUNC) { if (arglist->t == T_FUNC) check_func(arglist); } else { outfl(O_ERR, arglist->file, arglist->line, "argument to is_on() must be a path or a call to " "fru() or asru()"); } } else if (np->u.func.s == L_is_present) { if (arglist->t == T_NAME || arglist->t == T_FUNC) { if (arglist->t == T_FUNC) check_func(arglist); } else { outfl(O_ERR, arglist->file, arglist->line, "argument to is_present() must be a path or a call " "to fru() or asru()"); } } else if (np->u.func.s == L_has_fault) { if (arglist->t == T_LIST && (arglist->u.expr.left->t == T_NAME || arglist->u.expr.left->t == T_FUNC) && arglist->u.expr.right->t == T_QUOTE) { if (arglist->u.expr.left->t == T_FUNC) check_func(arglist->u.expr.left); } else { outfl(O_ERR, arglist->file, arglist->line, "%s() must have path or call to " "fru() and/or asru() as first argument; " "second argument must be a string", np->u.func.s); } } else if (np->u.func.s == L_is_type) { if (arglist->t == T_NAME || arglist->t == T_FUNC) { if (arglist->t == T_FUNC) check_func(arglist); } else { outfl(O_ERR, arglist->file, arglist->line, "argument to is_type() must be a path or a call to " "fru() or asru()"); } } else if (np->u.func.s == L_confcall) { if (arglist->t != T_QUOTE && (arglist->t != T_LIST || arglist->u.expr.left->t != T_QUOTE)) outfl(O_ERR, arglist->file, arglist->line, "confcall(): first argument must be a string " "(the name of the operation)"); } else if (np->u.func.s == L_confprop || np->u.func.s == L_confprop_defined) { if (arglist->t == T_LIST && (arglist->u.expr.left->t == T_NAME || arglist->u.expr.left->t == T_FUNC) && arglist->u.expr.right->t == T_QUOTE) { if (arglist->u.expr.left->t == T_FUNC) check_func(arglist->u.expr.left); } else { outfl(O_ERR, arglist->file, arglist->line, "%s(): first argument must be a path or a call to " "fru() or asru(); " "second argument must be a string", np->u.func.s); } } else if (np->u.func.s == L_count) { if (arglist->t != T_EVENT) { outfl(O_ERR, arglist->file, arglist->line, "count(): argument must be an engine name"); } } else if (np->u.func.s == L_defined) { if (arglist->t != T_GLOBID) outfl(O_ERR, arglist->file, arglist->line, "argument to defined() must be a global"); } else if (np->u.func.s == L_payloadprop) { if (arglist->t != T_QUOTE) outfl(O_ERR, arglist->file, arglist->line, "argument to payloadprop() must be a string"); } else if (np->u.func.s == L_payloadprop_contains) { if (arglist->t != T_LIST || arglist->u.expr.left->t != T_QUOTE || arglist->u.expr.right == NULL) outfl(O_ERR, arglist->file, arglist->line, "args to payloadprop_contains(): must be a quoted " "string (property name) and an expression " "(to match)"); } else if (np->u.func.s == L_payloadprop_defined) { if (arglist->t != T_QUOTE) outfl(O_ERR, arglist->file, arglist->line, "arg to payloadprop_defined(): must be a quoted " "string"); } else if (np->u.func.s == L_setpayloadprop) { if (arglist->t == T_LIST && arglist->u.expr.left->t == T_QUOTE) { if (arglist->u.expr.right->t == T_FUNC) check_func(arglist->u.expr.right); } else { outfl(O_ERR, arglist->file, arglist->line, "setpayloadprop(): " "first arg must be a string, " "second arg a value"); } } else if (np->u.func.s == L_setserdn || np->u.func.s == L_setserdt || np->u.func.s == L_setserdsuffix || np->u.func.s == L_setserdincrement) { if (arglist->t == T_FUNC) check_func(arglist); } else if (np->u.func.s == L_cat) { check_cat_list(arglist); } else if (np->u.func.s == L_envprop) { if (arglist->t != T_QUOTE) outfl(O_ERR, arglist->file, arglist->line, "argument to envprop() must be a string"); } else outfl(O_WARN, np->file, np->line, "possible platform-specific function: %s", np->u.func.s); } void check_expr(struct node *np) { ASSERT(np != NULL); switch (np->t) { case T_ASSIGN: ASSERT(np->u.expr.left != NULL); if (np->u.expr.left->t != T_GLOBID) outfl(O_ERR, np->file, np->line, "assignment only allowed to globals (e.g. $a)"); break; } } void check_event(struct node *np) { ASSERT(np != NULL); ASSERTinfo(np->t == T_EVENT, ptree_nodetype2str(np->t)); if (np->u.event.epname == NULL) { outfl(O_ERR|O_NONL, np->file, np->line, "pathless events not allowed: "); ptree_name(O_ERR|O_NONL, np->u.event.ename); out(O_ERR, NULL); } } /* * check for properties that are required on declarations. This * should be done after all declarations since they can be * redeclared with a different set of properties. */ /*ARGSUSED*/ void check_required_props(struct node *lhs, struct node *rhs, void *arg) { ASSERTeq(rhs->t, (enum nodetype)arg, ptree_nodetype2str); check_stmt_required_properties(rhs); } /* * check that cascading prop statements do not contain lists internally. * the first and last event lists in the cascading prop may be single * events or lists of events. */ /*ARGSUSED*/ void check_proplists(enum nodetype t, struct node *np) { ASSERT(np->t == T_ARROW); /* * not checking the right hand side of the top level prop * since it is the last part of the propagation and can be * an event or list of events */ check_proplists_lhs(t, np->u.arrow.lhs); } /*ARGSUSED*/ static void check_proplists_lhs(enum nodetype t, struct node *lhs) { if (lhs->t == T_ARROW) { if (lhs->u.arrow.rhs->t == T_LIST) { outfl(O_ERR, lhs->file, lhs->line, "lists are not allowed internally on cascading %s", (t == T_PROP) ? "propagations" : "masks"); } check_proplists_lhs(t, lhs->u.arrow.lhs); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * check.h -- public definitions for check module * * the checking functions are called by the tree_X() functions * in tree.c. this header file exports the checking functions * from check.c so that tree.c can see them. nobody else uses them. */ #ifndef _ESC_COMMON_CHECK_H #define _ESC_COMMON_CHECK_H #ifdef __cplusplus extern "C" { #endif void check_init(void); void check_fini(void); void check_report_combination(struct node *np); void check_arrow(struct node *np); void check_stmt_required_properties(struct node *stmtnp); void check_stmt_allowed_properties(enum nodetype t, struct node *nvpairnp, struct lut *lutp); void check_propnames(enum nodetype t, struct node *np, int from, int to); void check_propscope(struct node *np); void check_proplists(enum nodetype t, struct node *np); void check_upset_engine(struct node *lhs, struct node *rhs, void *arg); void check_refcount(struct node *lhs, struct node *rhs, void *arg); int check_cycle_level(long long val); void check_cycle(struct node *lhs, struct node *rhs, void *arg); void check_type_iterator(struct node *np); void check_name_iterator(struct node *np); void check_func(struct node *np); void check_expr(struct node *np); void check_event(struct node *np); void check_required_props(struct node *lhs, struct node *rhs, void *arg); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_CHECK_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * eft.h -- public definitions for eft files * */ #ifndef _ESC_COMMON_EFT_H #define _ESC_COMMON_EFT_H #ifdef __cplusplus extern "C" { #endif #include /* eft file header */ #define EFT_HDR_MAGIC 0x45465400 #define EFT_HDR_MAJOR 3 #define EFT_HDR_MINOR 1 #define EFT_HDR_MAXCOMMENT 256 struct eftheader { uint32_t magic; uint16_t major; uint16_t minor; uint16_t cmajor; uint16_t cminor; uint32_t identlen; uint32_t dictlen; uint32_t unused[2]; uint32_t csum; char comment[EFT_HDR_MAXCOMMENT]; }; #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_EFT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * eftread.c -- routines for reading .eft files * */ #include #include #include #include #include #include #include #include #include "out.h" #include "stable.h" #include "lut.h" #include "tree.h" #include "eft.h" #include "eftread.h" #include "esclex.h" #include "version.h" #include "ptree.h" /* for uintX_t, htonl(), etc */ #include #include #include #ifndef MIN #define MIN(x, y) ((x) <= (y) ? (x) : (y)) #endif static int Showheader; /* * eftread_showheader -- set showheader flag * */ void eftread_showheader(int newval) { Showheader = newval; } /* * eftread_fopen -- fopen an EFT file for reading * */ FILE * eftread_fopen(const char *fname, char *idbuf, size_t idbufsz) { FILE *fp; FILE *tfp; struct eftheader hdr; #define BUFLEN 8192 char buf[BUFLEN]; int cc; uint32_t csum = 0; char *ptr; if ((ptr = strrchr(fname, '.')) == NULL || strcmp(ptr, ".eft") != 0) { out(O_ERR, "%s: not a valid EFT (bad extension)", fname); return (NULL); } if ((fp = fopen(fname, "r")) == NULL) { out(O_ERR|O_SYS, "%s", fname); return (NULL); } if (fread(&hdr, 1, sizeof (hdr), fp) < sizeof (hdr)) { (void) fclose(fp); out(O_ERR, "%s: not a valid EFT (too short)", fname); return (NULL); } hdr.magic = ntohl(hdr.magic); hdr.major = ntohs(hdr.major); hdr.minor = ntohs(hdr.minor); hdr.cmajor = ntohs(hdr.cmajor); hdr.cminor = ntohs(hdr.cminor); hdr.identlen = ntohl(hdr.identlen); hdr.dictlen = ntohl(hdr.dictlen); hdr.csum = ntohl(hdr.csum); if (Showheader) out(O_VERB, "%s: magic %x EFT version %d.%d esc version %d.%d", fname, hdr.magic, hdr.major, hdr.minor, hdr.cmajor, hdr.cminor); if (hdr.magic != EFT_HDR_MAGIC) { (void) fclose(fp); out(O_ERR, "%s: not a valid EFT (bad magic)", fname); return (NULL); } if (hdr.major != EFT_HDR_MAJOR || hdr.minor > EFT_HDR_MINOR) { (void) fclose(fp); out(O_ERR, "%s is version %d.%d, " "this program supports up to %d.%d", fname, hdr.major, hdr.minor, EFT_HDR_MAJOR, EFT_HDR_MINOR); return (NULL); } bzero(idbuf, idbufsz); if (hdr.identlen != 0) { long npos = ftell(fp) + (long)hdr.identlen; /* after ident */ size_t rsz = MIN(hdr.identlen, idbufsz - 1); if (fread(idbuf, 1, rsz, fp) != rsz) out(O_DIE|O_SYS, "%s: fread", fname); if (fseek(fp, npos, SEEK_SET) == -1) out(O_DIE|O_SYS, "%s: fseek", fname); } if (hdr.dictlen && (hdr.dictlen < 2 || hdr.dictlen > 1000)) { (void) fclose(fp); out(O_ERR, "%s: bad dictlen: %d", fname, hdr.dictlen); return (NULL); } /* read in dict strings */ if (hdr.dictlen) { char *dbuf = alloca(hdr.dictlen); char *dptr; if ((cc = fread(dbuf, 1, hdr.dictlen, fp)) != hdr.dictlen) out(O_DIE|O_SYS, "short fread on %s (dictlen %d)", fname, hdr.dictlen); /* work from end of string array backwards, finding names */ for (dptr = &dbuf[hdr.dictlen - 2]; dptr > dbuf; dptr--) if (*dptr == '\0') { /* found separator, record string */ Dicts = lut_add(Dicts, (void *)stable(dptr + 1), (void *)0, NULL); } /* record the first string */ Dicts = lut_add(Dicts, (void *)stable(dptr), (void *)0, NULL); } if ((tfp = tmpfile()) == NULL) out(O_DIE|O_SYS, "cannot create temporary file"); while ((cc = fread(buf, 1, BUFLEN, fp)) > 0) { char *ptr; for (ptr = buf; ptr < &buf[cc]; ptr++) { *ptr = ~((unsigned char)*ptr); csum += (uint32_t)*ptr; } if (cc != fwrite(buf, 1, cc, tfp) || ferror(tfp)) out(O_DIE|O_SYS, "fwrite on tmpfile"); } if (ferror(fp)) out(O_DIE|O_SYS, "fread on %s", fname); (void) fclose(fp); if (hdr.csum != csum) { out(O_ERR, "%s: bad checksum (%x != %x)", fname, hdr.csum, csum); (void) fclose(tfp); return (NULL); } if (Showheader) { int len = strlen(hdr.comment); if (len > 0 && hdr.comment[len - 1] == '\n') hdr.comment[len - 1] = '\0'; out(O_OK, "%s:\n\t%s", fname, hdr.comment); } rewind(tfp); return (tfp); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * eftread.h -- public definitions for eftread module * */ #ifndef _ESC_COMMON_EFTREAD_H #define _ESC_COMMON_EFTREAD_H #ifdef __cplusplus extern "C" { #endif void eftread_showheader(int newval); FILE *eftread_fopen(const char *fname, char *idbuf, size_t idbufsz); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_EFTREAD_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * eftwrite.c -- routines for writing .eft files * * this module emits the table resulting from compilation of the * source files. this code done nothing unless the -o option * was given on the command line. */ #include #include #include #include #include #include "out.h" #include "stats.h" #include "stable.h" #include "lut.h" #include "tree.h" #include "eft.h" #include "eftwrite.h" #include "esclex.h" #include "version.h" #include "ptree.h" /* for uintX_t, htonl(), etc */ #include #include #include extern char Args[]; static struct stats *Outbytes; static int Identlen; static int Dictlen; void eftwrite_init(void) { Outbytes = stats_new_counter("eftwrite.total", "bytes written", 1); } /*ARGSUSED*/ static void ident_lencalc(const char *s, void *rhs, void *arg) { Identlen += strlen(s) + 1; } /*ARGSUSED*/ static void dict_lencalc(const char *s, void *rhs, void *arg) { Dictlen += strlen(s) + 1; } /*ARGSUSED*/ static void ident_printer(const char *s, void *rhs, void *arg) { FILE *fp = (FILE *)arg; (void) fwrite(s, strlen(s) + 1, 1, fp); } /*ARGSUSED*/ static void dict_printer(const char *s, void *rhs, void *arg) { FILE *fp = (FILE *)arg; (void) fwrite(s, strlen(s) + 1, 1, fp); } void eftwrite(const char *fname) { FILE *fp; FILE *tfp; struct eftheader hdr; #define BUFLEN 8192 char buf[BUFLEN]; int cc; if ((tfp = tmpfile()) == NULL) out(O_DIE|O_SYS, "cannot create temporary file"); /* XXX switch stdout to tfp temporarily */ /* XXX for now */ out_altfp(tfp); ptree(O_ALTFP, tree_root(NULL), 0, 1); rewind(tfp); lut_walk(Ident, (lut_cb)ident_lencalc, (void *)0); lut_walk(Dicts, (lut_cb)dict_lencalc, (void *)0); bzero(&hdr, sizeof (hdr)); hdr.magic = EFT_HDR_MAGIC; hdr.major = EFT_HDR_MAJOR; hdr.minor = EFT_HDR_MINOR; hdr.cmajor = VERSION_MAJOR; hdr.cminor = VERSION_MINOR; hdr.identlen = Identlen; hdr.dictlen = Dictlen; buf[BUFLEN - 1] = '\0'; #ifdef DEBUG (void) snprintf(hdr.comment, EFT_HDR_MAXCOMMENT, "Built using esc-%d.%d\tArgs: \"%s\"\n", VERSION_MAJOR, VERSION_MINOR, Args); #else (void) snprintf(hdr.comment, EFT_HDR_MAXCOMMENT, "Built using esc-%d.%d\n", VERSION_MAJOR, VERSION_MINOR); #endif if ((fp = fopen(fname, "w")) == NULL) out(O_DIE|O_SYS, "can't open output file: %s", fname); while ((cc = fread(buf, 1, BUFLEN, tfp)) > 0) { char *ptr; for (ptr = buf; ptr < &buf[cc]; ptr++) hdr.csum += (uint32_t)*ptr; } if (ferror(tfp)) out(O_DIE|O_SYS, "fread on tmpfile"); rewind(tfp); hdr.magic = htonl(hdr.magic); hdr.major = htons(hdr.major); hdr.minor = htons(hdr.minor); hdr.cmajor = htons(hdr.cmajor); hdr.cminor = htons(hdr.cminor); hdr.identlen = htonl(hdr.identlen); hdr.dictlen = htonl(hdr.dictlen); hdr.csum = htonl(hdr.csum); (void) fwrite(&hdr, sizeof (hdr), 1, fp); if (ferror(fp)) out(O_DIE|O_SYS, "%s: can't write header", fname); stats_counter_add(Outbytes, sizeof (hdr)); lut_walk(Ident, (lut_cb)ident_printer, (void *)fp); stats_counter_add(Outbytes, Identlen); lut_walk(Dicts, (lut_cb)dict_printer, (void *)fp); stats_counter_add(Outbytes, Dictlen); while ((cc = fread(buf, 1, BUFLEN, tfp)) > 0) { char *ptr; for (ptr = buf; ptr < &buf[cc]; ptr++) *ptr = ~((unsigned char)*ptr); if (cc != fwrite(buf, 1, cc, fp) || ferror(fp)) out(O_DIE|O_SYS, "fwrite on %s", fname); stats_counter_add(Outbytes, cc); } if (ferror(tfp)) out(O_DIE|O_SYS, "fread on tmpfile"); (void) fclose(tfp); (void) fclose(fp); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * eftwrite.h -- public definitions for eftwrite module * */ #ifndef _ESC_COMMON_EFTWRITE_H #define _ESC_COMMON_EFTWRITE_H #ifdef __cplusplus extern "C" { #endif void eftwrite_init(void); void eftwrite(const char *fname); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_EFTWRITE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * esclex.c -- lexer for esc * * this module provides lexical analysis and error handling routine * expected by the yacc-generated parser (i.e. yylex() and yyerror()). * it also does lots of tracking of things like filenames, line numbers, * and what tokens are seen on a line up to the point where a syntax error * was found. this module also arranges for the input source files to * be run through cpp. */ #include #include #include #include #include #include #include #include "out.h" #include "alloc.h" #include "stats.h" #include "stable.h" #include "lut.h" #include "literals.h" #include "tree.h" #include "esclex.h" #include "eftread.h" #include "check.h" #include "y.tab.h" struct lut *Dicts; struct lut *Ident; struct lut *Timesuffixlut; int Pragma_trust_ereports; int Pragma_new_errors_only; /* ridiculously long token buffer -- disallow any token longer than this */ #define MAXTOK 8192 static char Tok[MAXTOK]; /* some misc stats we keep on the lexer & parser */ static struct stats *Tokcount; static struct stats *Lexelapse; struct stats *Filecount; struct filestats { struct filestats *next; struct stats *stats; struct stats *idstats; } *Fstats; static int Errcount; /* input file state */ static char **Files; static const char *Fileopened; static FILE *Fp; static int Line; static const char *File; static const char *Cpp = "/usr/lib/cpp"; #ifdef ESC static const char *Cppargs; static const char *Cppstdargs = "-undef"; #endif /* ESC */ /* for debugging */ static int Lexecho; /* echo tokens as we read them */ /* forward declarations of our internal routines */ static int record(int tok, const char *s); static void dumpline(int flags); static void doident(); static void dopragma(const char *tok); /* * table of reserved words. this table is only used by lex_init() * to intialize the Rwords lookup table. */ static const struct { const char *word; const int val; } Rwords[] = { { "asru", ASRU }, { "count", COUNT }, { "div", DIV }, { "engine", ENGINE }, { "event", EVENT }, { "fru", FRU }, { "if", IF }, { "mask", MASK }, { "prop", PROP }, { "config", CONFIG }, /* * PATHFUNC indicates functions that operate only on paths * and quotes */ { "is_connected", PATHFUNC }, { "is_under", PATHFUNC }, { "is_on", PATHFUNC }, { "is_present", PATHFUNC }, { "is_type", PATHFUNC }, { "has_fault", PATHFUNC }, { "confprop", PATHFUNC }, { "confprop_defined", PATHFUNC }, }; /* * Rwordslut is a lookup table of reserved words. lhs is the word * (in the string table) and the rhs is the token value returned * by the yylex() for that word. */ static struct lut *Rwordslut; static const struct { const char *suffix; const unsigned long long nsec; } Timesuffix[] = { { "nanosecond", 1ULL }, { "nanoseconds", 1ULL }, { "nsec", 1ULL }, { "nsecs", 1ULL }, { "ns", 1ULL }, { "microsecond", 1000ULL }, { "microseconds", 1000ULL }, { "usec", 1000ULL }, { "usecs", 1000ULL }, { "us", 1000ULL }, { "millisecond", 1000000ULL }, { "milliseconds", 1000000ULL }, { "msec", 1000000ULL }, { "msecs", 1000000ULL }, { "ms", 1000000ULL }, { "second", 1000000000ULL }, { "seconds", 1000000000ULL }, { "s", 1000000000ULL }, { "minute", 1000000000ULL * 60 }, { "minutes", 1000000000ULL * 60 }, { "min", 1000000000ULL * 60 }, { "mins", 1000000000ULL * 60 }, { "m", 1000000000ULL * 60 }, { "hour", 1000000000ULL * 60 * 60 }, { "hours", 1000000000ULL * 60 * 60 }, { "hr", 1000000000ULL * 60 * 60 }, { "hrs", 1000000000ULL * 60 * 60 }, { "h", 1000000000ULL * 60 * 60 }, { "day", 1000000000ULL * 60 * 60 * 24 }, { "days", 1000000000ULL * 60 * 60 * 24 }, { "d", 1000000000ULL * 60 * 60 * 24 }, { "week", 1000000000ULL * 60 * 60 * 24 * 7 }, { "weeks", 1000000000ULL * 60 * 60 * 24 * 7 }, { "wk", 1000000000ULL * 60 * 60 * 24 * 7 }, { "wks", 1000000000ULL * 60 * 60 * 24 * 7 }, { "month", 1000000000ULL * 60 * 60 * 24 * 30 }, { "months", 1000000000ULL * 60 * 60 * 24 * 30 }, { "year", 1000000000ULL * 60 * 60 * 24 * 365 }, { "years", 1000000000ULL * 60 * 60 * 24 * 365 }, { "yr", 1000000000ULL * 60 * 60 * 24 * 365 }, { "yrs", 1000000000ULL * 60 * 60 * 24 * 365 }, }; /* * some wrappers around the general lut functions to provide type checking... */ static struct lut * lex_s2i_lut_add(struct lut *root, const char *s, intptr_t i) { return (lut_add(root, (void *)s, (void *)i, NULL)); } static int lex_s2i_lut_lookup(struct lut *root, const char *s) { return ((intptr_t)lut_lookup(root, (void *)s, NULL)); } static struct lut * lex_s2ullp_lut_add(struct lut *root, const char *s, const unsigned long long *ullp) { return (lut_add(root, (void *)s, (void *)ullp, NULL)); } const unsigned long long * lex_s2ullp_lut_lookup(struct lut *root, const char *s) { return ((unsigned long long *)lut_lookup(root, (void *)s, NULL)); } /* * lex_init -- initialize the lexer with appropriate filenames & debug flags */ /*ARGSUSED*/ void lex_init(char **av, const char *cppargs, int lexecho) { int i; #ifdef ESC const char *ptr; #endif /* ESC */ Lexecho = lexecho; Tokcount = stats_new_counter("lex.tokens", "total tokens in", 1); Filecount = stats_new_counter("lex.files", "total files read", 0); Lexelapse = stats_new_elapse("lex.time", "elapsed lex/parse time", 1); #ifdef ESC Cppargs = cppargs; /* allow user to tell us where cpp is if it is some weird place */ if (ptr = getenv("_ESC_CPP")) Cpp = ptr; /* and in case it takes some special stdargs */ if (ptr = getenv("_ESC_CPP_STDARGS")) Cppstdargs = ptr; /* verify we can find cpp */ if (access(Cpp, X_OK) < 0) { Cpp = "/usr/lib/cpp"; if (access(Cpp, X_OK) < 0) out(O_DIE, "can't locate cpp"); } #endif /* ESC */ Files = av; /* verify we can find all the input files */ while (*av) { if (strlen(*av) >= MAXTOK - strlen(Cpp) - 3) out(O_DIE, "filename too long: %.100s...", *av); if (access(*av, R_OK) < 0) out(O_DIE|O_SYS, "%s", *av); av++; stats_counter_bump(Filecount); } /* put reserved words into the string table & a lookup table */ for (i = 0; i < sizeof (Rwords) / sizeof (*Rwords); i++) Rwordslut = lex_s2i_lut_add(Rwordslut, stable(Rwords[i].word), Rwords[i].val); /* initialize table of timeval suffixes */ for (i = 0; i < sizeof (Timesuffix) / sizeof (*Timesuffix); i++) { Timesuffixlut = lex_s2ullp_lut_add(Timesuffixlut, stable(Timesuffix[i].suffix), &Timesuffix[i].nsec); } /* record start time */ stats_elapse_start(Lexelapse); } void closefile(void) { if (Fp != NULL) { #ifdef ESC if (pclose(Fp) > 0) out(O_DIE, "cpp errors while reading \"%s\", " "bailing out.", Fileopened); #else (void) fclose(Fp); #endif /* ESC */ } Fp = NULL; } /* * yylex -- the lexer, called yylex() because that's what yacc wants */ int yylex(void) { int c; int nextc; char *ptr = Tok; char *eptr = &Tok[MAXTOK]; const char *cptr; int startline; int val; static int bol = 1; /* true if we're at beginning of line */ for (;;) { while (Fp == NULL) { char ibuf[80]; if (*Files == NULL) return (record(EOF, NULL)); Fileopened = stable(*Files++); #ifdef ESC sprintf(Tok, "%s %s %s %s", Cpp, Cppstdargs, Cppargs, Fileopened); if ((Fp = popen(Tok, "r")) == NULL) out(O_DIE|O_SYS, "%s", Tok); #else Fp = eftread_fopen(Fileopened, ibuf, sizeof (ibuf)); #endif /* ESC */ Line = 1; bol = 1; /* add name to stats for visibility */ if (Fp != NULL) { static int fnum; char nbuf[100]; struct filestats *nfs = MALLOC(sizeof (*nfs)); (void) sprintf(nbuf, "lex.file%d", fnum); nfs->stats = stats_new_string(nbuf, "", 0); stats_string_set(nfs->stats, Fileopened); if (ibuf[0] != '\0') { (void) sprintf(nbuf, "lex.file%d-ident", fnum); nfs->idstats = stats_new_string(nbuf, "", 0); stats_string_set(nfs->idstats, ibuf); } else { nfs->idstats = NULL; } nfs->next = Fstats; Fstats = nfs; fnum++; } } switch (c = getc(Fp)) { case '#': /* enforce that we're at beginning of line */ if (!bol) return (record(c, NULL)); while ((c = getc(Fp)) != EOF && (c == ' ' || c == '\t')) ; if (!isdigit(c)) { /* * three cases here: * #pragma * #ident * #something-we-don't-understand * anything we don't expect we just ignore. */ *ptr++ = c; while ((c = getc(Fp)) != EOF && isalnum(c)) if (ptr < eptr - 1) *ptr++ = c; *ptr++ = '\0'; if (strcmp(Tok, "pragma") == 0) { /* skip white space */ while ((c = getc(Fp)) != EOF && (c == ' ' || c == '\t')) ; if (c == EOF || c == '\n') outfl(O_DIE, File, Line, "bad #pragma"); /* pull in next token */ ptr = Tok; *ptr++ = c; while ((c = getc(Fp)) != EOF && !isspace(c)) if (ptr < eptr - 1) *ptr++ = c; *ptr++ = '\0'; (void) ungetc(c, Fp); dopragma(Tok); } else if (strcmp(Tok, "ident") == 0) doident(); } else { /* handle file & line info from cpp */ Line = 0; do { if (!isdigit(c)) break; Line = Line * 10 + c - '0'; } while ((c = getc(Fp)) != EOF); Line--; /* newline will increment it */ while (c != EOF && isspace(c)) c = getc(Fp); if (c != '"') outfl(O_DIE, File, Line, "bad # statement (file name)"); while ((c = getc(Fp)) != EOF && c != '"') if (ptr < eptr - 1) *ptr++ = c; *ptr++ = '\0'; if (c != '"') outfl(O_DIE, File, Line, "bad # statement (quotes)"); File = stable(Tok); } /* skip the rest of the cpp line */ while ((c = getc(Fp)) != EOF && c != '\n' && c != '\r') ; if (c == EOF) return (record(c, NULL)); else (void) ungetc(c, Fp); ptr = Tok; break; case EOF: closefile(); continue; case '\n': Line++; bol = 1; break; case '\r': case ' ': case '\t': bol = 0; break; case '/': bol = 0; /* comment handling */ if ((nextc = getc(Fp)) == EOF) outfl(O_DIE, File, Line, "unexpected EOF"); else if (nextc == '*') { startline = Line; while ((c = getc(Fp)) != EOF) { if (c == '\n') Line++; else if (c == '*' && (((c = getc(Fp)) == EOF) || (c == '/'))) break; } if (c == EOF) { outfl(O_DIE, File, Line, "end of comment not seen " "(started on line %d)", startline); } } else { /* wasn't a comment, return the '/' token */ (void) ungetc(nextc, Fp); return (record(c, NULL)); } break; case '"': { int prevc; bol = 0; prevc = '\0'; /* quoted string handling */ startline = Line; for (;;) { c = getc(Fp); if (c == EOF) outfl(O_DIE, File, Line, "end of string not seen " "(started on line %d)", startline); else if (c == '\n') Line++; else if (c == '"' && prevc != '\\') break; else if (ptr < eptr) *ptr++ = c; prevc = c; } if (ptr >= eptr) out(O_DIE, File, Line, "string too long"); *ptr++ = '\0'; return (record(QUOTE, stable(Tok))); } case '&': bol = 0; /* && */ if ((nextc = getc(Fp)) == '&') return (record(AND, NULL)); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; case '|': bol = 0; /* || */ if ((nextc = getc(Fp)) == '|') return (record(OR, NULL)); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; case '!': bol = 0; /* ! or != */ if ((nextc = getc(Fp)) == '=') return (record(NE, NULL)); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; case '=': bol = 0; /* == */ if ((nextc = getc(Fp)) == '=') return (record(EQ, NULL)); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; case '-': bol = 0; /* -> */ if ((nextc = getc(Fp)) == '>') return (record(ARROW, stable(Tok))); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; case '<': bol = 0; if ((nextc = getc(Fp)) == '=') /* <= */ return (record(LE, NULL)); else if (nextc == '<') /* << */ return (record(LSHIFT, NULL)); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; case '>': bol = 0; if ((nextc = getc(Fp)) == '=') /* >= */ return (record(GE, NULL)); else if (nextc == '>') /* >> */ return (record(RSHIFT, NULL)); else { (void) ungetc(nextc, Fp); return (record(c, NULL)); } /*NOTREACHED*/ break; default: bol = 0; if (isdigit(c)) { int base; /* collect rest of number */ if (c == '0') { *ptr++ = c; if ((c = getc(Fp)) == EOF) { *ptr++ = '\0'; return (record(NUMBER, stable(Tok))); } else if (c == 'x' || c == 'X') { *ptr++ = c; base = 16; } else { (void) ungetc(c, Fp); base = 8; } } else { *ptr++ = c; base = 10; } while ((c = getc(Fp)) != EOF) { if (ptr >= eptr) out(O_DIE, File, Line, "number too long"); switch (base) { case 16: if (c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F') { *ptr++ = c; continue; } /*FALLTHRU*/ case 10: if (c >= '8' && c <= '9') { *ptr++ = c; continue; } /*FALLTHRU*/ case 8: if (c >= '0' && c <= '7') { *ptr++ = c; continue; } /* not valid for this base */ *ptr++ = '\0'; (void) ungetc(c, Fp); return (record(NUMBER, stable(Tok))); } } *ptr++ = '\0'; return (record(NUMBER, stable(Tok))); } else if (isalpha(c)) { /* collect identifier */ *ptr++ = c; for (;;) { c = getc(Fp); if ((isalnum(c) || c == '_') && ptr < eptr) *ptr++ = c; else { (void) ungetc(c, Fp); break; } } if (ptr >= eptr) out(O_DIE, File, Line, "identifier too long"); *ptr++ = '\0'; cptr = stable(Tok); if (val = lex_s2i_lut_lookup(Rwordslut, cptr)) { return (record(val, cptr)); } return (record(ID, cptr)); } else return (record(c, NULL)); } /*NOTREACHED*/ } } /* * the record()/dumpline() routines are used to track & report * the list of tokens seen on a given line. this is used in two ways. * first, syntax errors found by the parser are reported by us (via * yyerror()) and we tack on the tokens processed so far on the current * line to help indicate exactly where the error is. second, if "lexecho" * debugging is turned on, these routines provide it. */ #define MAXRECORD 1000 static int Recordedline; static struct { int tok; const char *s; } Recorded[MAXRECORD]; static int Recordnext; static int record(int tok, const char *s) { stats_counter_bump(Tokcount); if (Line != Recordedline) { /* starting new line, dump out the previous line */ if (Lexecho && Recordedline) { outfl(O_NONL, File, Recordedline, "lex: "); dumpline(O_OK); } Recordedline = Line; Recordnext = 0; } if (Recordnext >= MAXRECORD) outfl(O_DIE, File, Line, "line too long, bailing out"); Recorded[Recordnext].tok = tok; Recorded[Recordnext++].s = s; yylval.tok.s = s; yylval.tok.file = File; yylval.tok.line = Line; return (tok); } /*ARGSUSED*/ static void dumpline(int flags) { int i; for (i = 0; i < Recordnext; i++) if (Recorded[i].s && Recorded[i].tok != ARROW) switch (Recorded[i].tok) { case T_QUOTE: out(flags|O_NONL, " \"%s\"", Recorded[i].s); break; default: out(flags|O_NONL, " %s", Recorded[i].s); break; } else switch (Recorded[i].tok) { case EOF: out(flags|O_NONL, " EOF"); break; case ARROW: out(flags|O_NONL, " ->%s", Recorded[i].s); break; case EQ: out(flags|O_NONL, " =="); break; case NE: out(flags|O_NONL, " !="); break; case OR: out(flags|O_NONL, " ||"); break; case AND: out(flags|O_NONL, " &&"); break; case LE: out(flags|O_NONL, " <="); break; case GE: out(flags|O_NONL, " >="); break; case LSHIFT: out(flags|O_NONL, " <<"); break; case RSHIFT: out(flags|O_NONL, " >>"); break; default: if (isprint(Recorded[i].tok)) out(flags|O_NONL, " %c", Recorded[i].tok); else out(flags|O_NONL, " '\\%03o'", Recorded[i].tok); break; } out(flags, NULL); } /* * yyerror -- report a pareser error, called yyerror because yacc wants it */ int yyerror(const char *s) { Errcount++; outfl(O_ERR|O_NONL, File, Line, "%s, tokens: ", s); dumpline(O_ERR); return (0); } /* * doident -- handle "#pragma ident" directives */ static void doident() { int c; char *ptr = Tok; char *eptr = &Tok[MAXTOK]; /* skip white space and quotes */ while ((c = getc(Fp)) != EOF && (c == ' ' || c == '\t' || c == '"')) ; if (c == EOF || c == '\n') outfl(O_DIE, File, Line, "bad ident"); /* pull in next token */ ptr = Tok; *ptr++ = c; while ((c = getc(Fp)) != EOF && c != '"' && c != '\n') if (ptr < eptr - 1) *ptr++ = c; *ptr++ = '\0'; if (c != '\n') { /* skip to end of line (including close quote, if any) */ while ((c = getc(Fp)) != EOF && c != '\n') ; } (void) ungetc(c, Fp); Ident = lut_add(Ident, (void *)stable(Tok), (void *)0, NULL); outfl(O_VERB, File, Line, "pragma set: ident \"%s\"", Tok); } /* * dodictionary -- handle "#pragma dictionary" directives */ static void dodictionary() { int c; char *ptr = Tok; char *eptr = &Tok[MAXTOK]; /* skip white space and quotes */ while ((c = getc(Fp)) != EOF && (c == ' ' || c == '\t' || c == '"')) ; if (c == EOF || c == '\n') outfl(O_DIE, File, Line, "bad dictionary"); /* pull in next token */ ptr = Tok; *ptr++ = c; while ((c = getc(Fp)) != EOF && c != '"' && c != '\n') if (ptr < eptr - 1) *ptr++ = c; *ptr++ = '\0'; if (c != '\n') { /* skip to end of line (including close quote, if any) */ while ((c = getc(Fp)) != EOF && c != '\n') ; } (void) ungetc(c, Fp); Dicts = lut_add(Dicts, (void *)stable(Tok), (void *)0, NULL); outfl(O_VERB, File, Line, "pragma set: dictionary \"%s\"", Tok); } /* * doallow_cycles -- handle "#pragma allow_cycles" directives */ static void doallow_cycles() { int c; char *ptr = Tok; char *eptr = &Tok[MAXTOK]; unsigned long long newlevel; /* * by default the compiler does not allow cycles or loops * in propagations. when cycles are encountered, the * compiler prints out an error message. * * "#pragma allow_cycles" and * "#pragma allow_cycles 0" * allow cycles, but any such cycle will produce a warning * message. * * "#pragma allow_cycles N" * with N > 0 will allow cycles and not produce any * warning messages. */ /* skip white space and quotes */ while ((c = getc(Fp)) != EOF && (c == ' ' || c == '\t' || c == '"')) ; if (c == EOF || c == '\n') newlevel = 0ULL; else { /* pull in next token */ ptr = Tok; *ptr++ = c; while ((c = getc(Fp)) != EOF && c != '"' && c != '\n') if (ptr < eptr - 1) *ptr++ = c; *ptr++ = '\0'; if (c != '\n') { /* skip to end of line */ while ((c = getc(Fp)) != EOF && c != '\n') ; } newlevel = strtoll(Tok, NULL, 0); } (void) ungetc(c, Fp); (void) check_cycle_level(newlevel); outfl(O_VERB, File, Line, "pragma set: allow_cycles (%s)", newlevel ? "no warnings" : "with warnings"); } /* * dopragma -- handle #pragma directives */ static void dopragma(const char *tok) { if (strcmp(tok, "ident") == 0) doident(); else if (strcmp(tok, "dictionary") == 0) dodictionary(); else if (strcmp(tok, "new_errors_only") == 0) { if (Pragma_new_errors_only++ == 0) outfl(O_VERB, File, Line, "pragma set: new_errors_only"); } else if (strcmp(tok, "trust_ereports") == 0) { if (Pragma_trust_ereports++ == 0) outfl(O_VERB, File, Line, "pragma set: trust_ereports"); } else if (strcmp(tok, "allow_cycles") == 0) doallow_cycles(); else outfl(O_VERB, File, Line, "unknown pragma ignored: \"%s\"", tok); } /* * lex_fini -- finalize the lexer */ int lex_fini(void) { stats_elapse_stop(Lexelapse); closefile(); if (Lexecho) { outfl(O_OK, File, Line, "lex: "); dumpline(O_OK); } return (Errcount); } void lex_free(void) { struct filestats *nfstats = Fstats; /* * Free up memory consumed by the lexer */ stats_delete(Tokcount); stats_delete(Filecount); stats_delete(Lexelapse); while (nfstats != NULL) { Fstats = nfstats->next; stats_delete(nfstats->stats); if (nfstats->idstats != NULL) stats_delete(nfstats->idstats); FREE(nfstats); nfstats = Fstats; } lut_free(Timesuffixlut, NULL, NULL); lut_free(Rwordslut, NULL, NULL); lut_free(Ident, NULL, NULL); lut_free(Dicts, NULL, NULL); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * esclex.h -- public definitions for esclex module * * this module provides lexical analysis (i.e. tokenizing the * input files) and the lex-level error routines expected by * yacc like yyerror(). yylex() and yyerror() are called only * by yacc-generated code. the lex_X() routines are called * only by main(). * * the tokstr struct is the communication mechanism between the lexer * and the parser. */ #ifndef _ESC_COMMON_ESCLEX_H #define _ESC_COMMON_ESCLEX_H #ifdef __cplusplus extern "C" { #endif /* information returned by lexer for tokens with string table entries */ struct tokstr { const char *s; /* the string (in the string table) */ const char *file; /* file where this token appeared */ int line; /* line where this token appeared */ }; void lex_init(char **av, const char *cppargs, int lexecho); int lex_fini(void); void lex_free(void); const unsigned long long *lex_s2ullp_lut_lookup(struct lut *root, const char *s); /* lut containing "ident" strings */ extern struct lut *Ident; /* lut containing "dictionary" strings */ extern struct lut *Dicts; /* lut containing valid timeval suffixes */ extern struct lut *Timesuffixlut; /* flags set by #pragmas */ extern int Pragma_new_errors_only; extern int Pragma_trust_ereports; extern int Pragma_allow_cycles; /* exported by esclex.c but names are mandated by the way yacc works... */ int yylex(void); int yyerror(const char *s); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_ESCLEX_H */ %{ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * escparse.y -- parser for esc * * this is the yacc-based parser for Eversholt. the syntax is simple * and is defined by the LALR(1) grammar described by this file. there * should be no shift/reduce or reduce/reduce messages when building this * file. * * as the input is parsed, a parse tree is built by calling the * tree_X() functions defined in tree.c. any syntax errors cause * us to skip to the next semicolon, achieved via the "error" clause * in the stmt rule below. the yacc state machine code will call * yyerror() in esclex.c and that will keep count of the errors and * display the filename, line number, and current input stream of tokens * to help the user figure out the problem. the -Y flag to this program * turns on the yacc debugging output which is quite large. you probably * only need to do that if you're debugging the grammar below. * */ #include #include #include #include #include #include #include #include "out.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "esclex.h" #include "tree.h" %} %union { struct tokstr tok; struct node *np; } %right '=' /* * make sure ':' comes immediately after '?' in precedence declarations */ %right '?' %nonassoc ':' %left OR %left AND %left '|' %left '^' %left '&' %left EQ NE %left LE GE '<' '>' %left LSHIFT RSHIFT %left '-' '+' %left '*' '%' DIV '/' %right '!' '~' %left '.' %token PROP MASK ARROW EVENT ENGINE ASRU FRU COUNT CONFIG %token ID QUOTE NUMBER IF PATHFUNC %type enameid %type root stmtlist stmt nvpairlist nvpair nvname nvexpr %type exprlist expr iterid ename pname epname eexprlist ipname iname %type numexpr cexpr func pfunc parglist parg %type eventlist event nork norkexpr globid propbody %% root : stmtlist { (void)tree_root($1); } ; stmtlist : /*empty*/ { $$ = NULL; } | stmtlist stmt { $$ = tree_expr(T_LIST, $1, $2); } ; stmt : error ';' { $$ = tree_nothing(); } | IF '(' expr ')' stmt { $$ = $5; } | IF '(' expr ')' '{' stmtlist '}' { $$ = $6; } | EVENT event nvpairlist ';' { $$ = tree_decl(T_EVENT, $2, $3, $1.file, $1.line); } | ENGINE event nvpairlist ';' { $$ = tree_decl(T_ENGINE, $2, $3, $1.file, $1.line); } | PROP propbody ';' { $$ = tree_stmt(T_PROP, $2, $1.file, $1.line); } | MASK propbody ';' { $$ = tree_stmt(T_MASK, $2, $1.file, $1.line); } | ASRU pname nvpairlist ';' { $$ = tree_decl(T_ASRU, $2, $3, $1.file, $1.line); } | FRU pname nvpairlist ';' { $$ = tree_decl(T_FRU, $2, $3, $1.file, $1.line); } | CONFIG ipname nvpairlist ';' { $$ = tree_decl(T_CONFIG, $2, $3, $1.file, $1.line); } | /*superfluous semicolons are ignored*/ ';' { $$ = tree_nothing(); } ; propbody: eventlist nork ARROW nork eventlist { $$ = tree_arrow($1, $2, $4, $5); } | propbody nork ARROW nork eventlist { $$ = tree_arrow($1, $2, $4, $5); } ; nork : /* empty */ { $$ = NULL; } | '(' norkexpr ')' { $$ = $2; } ; norkexpr: NUMBER { $$ = tree_num($1.s, $1.file, $1.line); } | ID /* really can only be 'A', enforced by check_arrow() later */ { $$ = tree_name($1.s, IT_NONE, $1.file, $1.line); } | '(' norkexpr ')' { $$ = $2; } | norkexpr '-' norkexpr { $$ = tree_expr(T_SUB, $1, $3); } | norkexpr '+' norkexpr { $$ = tree_expr(T_ADD, $1, $3); } | norkexpr '*' norkexpr { $$ = tree_expr(T_MUL, $1, $3); } | norkexpr DIV norkexpr { $$ = tree_expr(T_DIV, $1, $3); } | norkexpr '%' norkexpr { $$ = tree_expr(T_MOD, $1, $3); } ; nvpairlist: /* empty */ { $$ = NULL; } | nvpair | nvpairlist ',' nvpair { $$ = tree_expr(T_LIST, $1, $3); } ; nvpair : nvname '=' nvexpr { $$ = tree_expr(T_NVPAIR, $1, $3); } | ENGINE '=' nvexpr /* "engine" is a reserved word, but a valid property name */ { $$ = tree_expr(T_NVPAIR, tree_name($1.s, IT_NONE, $1.file, $1.line), $3); } | COUNT '=' nvexpr /* "count" is a reserved word, but a valid property name */ { $$ = tree_expr(T_NVPAIR, tree_name($1.s, IT_NONE, $1.file, $1.line), $3); } ; nvname : ID { $$ = tree_name($1.s, IT_NONE, $1.file, $1.line); } | nvname '-' ID { /* hack to allow dashes in property names */ $$ = tree_name_repairdash($1, $3.s); } ; /* the RHS of an nvpair can be a value, or an ename, or an ename@pname */ nvexpr : numexpr | ename epname { $$ = tree_event($1, $2, NULL); } | pname | globid | func | NUMBER ID /* * ID must be timevals only ("ms", "us", etc.). * enforced by tree_timeval(). */ { $$ = tree_timeval($1.s, $2.s, $1.file, $1.line); } | QUOTE { $$ = tree_quote($1.s, $1.file, $1.line); } ; /* arithmetic operations, no variables or symbols */ numexpr : numexpr '-' numexpr { $$ = tree_expr(T_SUB, $1, $3); } | numexpr '+' numexpr { $$ = tree_expr(T_ADD, $1, $3); } | numexpr '*' numexpr { $$ = tree_expr(T_MUL, $1, $3); } | numexpr DIV numexpr { $$ = tree_expr(T_DIV, $1, $3); } | numexpr '/' numexpr { $$ = tree_expr(T_DIV, $1, $3); } | numexpr '%' numexpr { $$ = tree_expr(T_MOD, $1, $3); } | '(' numexpr ')' { $$ = $2; } | NUMBER { $$ = tree_num($1.s, $1.file, $1.line); } ; eventlist: event | eventlist ',' event { $$ = tree_expr(T_LIST, $1, $3); } ; event : ename epname eexprlist { $$ = tree_event($1, $2, $3); } ; epname : /* empty */ { $$ = NULL; } | '@' pname { $$ = $2; } ; eexprlist: /* empty */ { $$ = NULL; } | '{' exprlist '}' { $$ = $2; } ; exprlist: expr | exprlist ',' expr { $$ = tree_expr(T_LIST, $1, $3); } ; /* * note that expr does not include pname, to avoid reduce/reduce * conflicts between cexpr and iterid involving the use of ID */ expr : cexpr | NUMBER ID /* * ID must be timevals only ("ms", "us", etc.). * enforced by tree_timeval(). */ { $$ = tree_timeval($1.s, $2.s, $1.file, $1.line); } ; cexpr : cexpr '=' cexpr { $$ = tree_expr(T_ASSIGN, $1, $3); } | cexpr '?' cexpr { $$ = tree_expr(T_CONDIF, $1, $3); } | cexpr ':' cexpr { $$ = tree_expr(T_CONDELSE, $1, $3); } | cexpr OR cexpr { $$ = tree_expr(T_OR, $1, $3); } | cexpr AND cexpr { $$ = tree_expr(T_AND, $1, $3); } | cexpr '|' cexpr { $$ = tree_expr(T_BITOR, $1, $3); } | cexpr '^' cexpr { $$ = tree_expr(T_BITXOR, $1, $3); } | cexpr '&' cexpr { $$ = tree_expr(T_BITAND, $1, $3); } | cexpr EQ cexpr { $$ = tree_expr(T_EQ, $1, $3); } | cexpr NE cexpr { $$ = tree_expr(T_NE, $1, $3); } | cexpr '<' cexpr { $$ = tree_expr(T_LT, $1, $3); } | cexpr LE cexpr { $$ = tree_expr(T_LE, $1, $3); } | cexpr '>' cexpr { $$ = tree_expr(T_GT, $1, $3); } | cexpr GE cexpr { $$ = tree_expr(T_GE, $1, $3); } | cexpr LSHIFT cexpr { $$ = tree_expr(T_LSHIFT, $1, $3); } | cexpr RSHIFT cexpr { $$ = tree_expr(T_RSHIFT, $1, $3); } | cexpr '-' cexpr { $$ = tree_expr(T_SUB, $1, $3); } | cexpr '+' cexpr { $$ = tree_expr(T_ADD, $1, $3); } | cexpr '*' cexpr { $$ = tree_expr(T_MUL, $1, $3); } | cexpr DIV cexpr { $$ = tree_expr(T_DIV, $1, $3); } | cexpr '/' cexpr { $$ = tree_expr(T_DIV, $1, $3); } | cexpr '%' cexpr { $$ = tree_expr(T_MOD, $1, $3); } | '!' cexpr { $$ = tree_expr(T_NOT, $2, NULL); } | '~' cexpr { $$ = tree_expr(T_BITNOT, $2, NULL); } | '(' cexpr ')' { $$ = $2; } | func | NUMBER { $$ = tree_num($1.s, $1.file, $1.line); } | ID { /* iteration variable */ $$ = tree_name($1.s, IT_NONE, $1.file, $1.line); } | globid | QUOTE { $$ = tree_quote($1.s, $1.file, $1.line); } ; func : ID '(' ')' { $$ = tree_func($1.s, NULL, $1.file, $1.line); } | ID '(' exprlist ')' { $$ = tree_func($1.s, $3, $1.file, $1.line); } | PATHFUNC '(' parglist ')' { $$ = tree_func($1.s, $3, $1.file, $1.line); } | pfunc ; parglist: parg | parglist ',' parg { $$ = tree_expr(T_LIST, $1, $3); } ; parg : pfunc | pname { $$ = tree_pname($1); } | QUOTE { $$ = tree_quote($1.s, $1.file, $1.line); } | ID '(' exprlist ')' { $$ = tree_func($1.s, $3, $1.file, $1.line); } ; /* * these functions are in the grammar so we can force the arg to be * a path or an event. they show up as functions in the parse tree. */ pfunc : ASRU '(' pname ')' { $$ = tree_func($1.s, tree_pname($3), $1.file, $1.line); } | FRU '(' pname ')' { $$ = tree_func($1.s, tree_pname($3), $1.file, $1.line); } | COUNT '(' event ')' { $$ = tree_func($1.s, $3, $1.file, $1.line); } ; globid : '$' ID { $$ = tree_globid($2.s, $2.file, $2.line); } ; iterid : ID { $$ = tree_name($1.s, IT_VERTICAL, $1.file, $1.line); } | ID '[' ']' { $$ = tree_name($1.s, IT_VERTICAL, $1.file, $1.line); } | ID '[' cexpr ']' { $$ = tree_name_iterator( tree_name($1.s, IT_VERTICAL, $1.file, $1.line), $3); } | ID '<' '>' { $$ = tree_name($1.s, IT_HORIZONTAL, $1.file, $1.line); } | ID '<' ID '>' { $$ = tree_name_iterator( tree_name($1.s, IT_HORIZONTAL, $1.file, $1.line), tree_name($3.s, IT_NONE, $3.file, $3.line)); } | ID '-' iterid { /* hack to allow dashes in path name components */ $$ = tree_name_repairdash2($1.s, $3); } ; /* iname is an ID where we can peel numbers off the end */ iname : ID { $$ = tree_iname($1.s, $1.file, $1.line); } ; /* base case of ID.ID instead of just ID requires ename to contain one dot */ ename : ID '.' enameid { $$ = tree_name_append( tree_name($1.s, IT_ENAME, $1.file, $1.line), tree_name($3.s, IT_NONE, $3.file, $3.line)); } | ename '.' enameid { $$ = tree_name_append($1, tree_name($3.s, IT_NONE, $3.file, $3.line)); } | ename '-' enameid { /* * hack to allow dashes in class names. when we * detect the dash here, we know we're in a class * name because the left recursion of this rule * means we've already matched at least: * ID '.' ID * so the ename here has an incomplete final * component (because the lexer stopped at the * dash). so we repair that final component here. */ $$ = tree_name_repairdash($1, $3.s); } ; /* like an ID, but we let reserved words act unreserved in enames */ enameid : ID | PROP | MASK | EVENT | ENGINE | ASRU | FRU | CONFIG | IF ; /* pname is a pathname, like x/y, x/y[0], etc */ pname : iterid | pname '/' iterid { $$ = tree_name_append($1, $3); } ; /* ipname is an "instanced" pathname, like x0/y1 */ ipname : iname | ipname '/' iname { $$ = tree_name_append($1, $3); } ; %% /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * io.c -- io wrapper functions, replacable in more constrained * environments, such as within a DE. */ #include #include void io_abort(const char *buf) { (void) fprintf(stderr, "%s\n", buf); abort(); } void io_die(const char *buf) { (void) fprintf(stderr, "%s\n", buf); exit(1); } void io_err(const char *buf) { (void) fprintf(stderr, "%s\n", buf); } void io_out(const char *buf) { (void) printf("%s\n", buf); } void io_exit(int code) { exit(code); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * io -- input/output routines */ #ifndef _ESC_COMMON_IO_H #define _ESC_COMMON_IO_H #ifdef __cplusplus extern "C" { #endif void io_abort(const char *buf) __NORETURN; void io_die(const char *buf) __NORETURN; void io_err(const char *buf); void io_out(const char *buf); void io_exit(int code) __NORETURN; #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_IO_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * literals.c -- literals initialization module * * this file intializes all the literals so they are stored in the * string table. instead of using: * stable("fault") * other modules can use: * L_fault * and avoid repeated calls to stable(). */ #include #include "out.h" #include "stable.h" #define L_DECL(s) const char *L_##s #include "literals.h" void literals_init() { /* * this turns the statements like: * extern const char *L_something; * in literals.h into initialization statements like: * L_something = stable("something"); * */ #undef _ESC_COMMON_LITERALS_H #undef L_DECL #define L_DECL(s) L_##s = stable(#s) #include "literals.h" } void literals_fini(void) { } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * literals.h -- public definitions for literals in string table * * all strings in this program are kept in the string table provided * by the routines in stable.c. this allows us to see if two strings * are equal by checking their pointers rather than calling strcmp(). * when we want to check for a specific string we can either do this: * if (s == stable("word")) * or define the literal here in this file and then do this: * if (s == L_word) * * the macro L_DECL() below expands to an extern const char * declaration * for files that include it. the exception is some cpp statements done by * literals.c which change L_DECL() to initialize the string by calling * stable(). */ #ifndef _ESC_COMMON_LITERALS_H #define _ESC_COMMON_LITERALS_H #ifdef __cplusplus extern "C" { #endif #ifndef L_DECL #define L_DECL(s) extern const char *L_##s #endif /* reserved words */ L_DECL(asru); L_DECL(div); L_DECL(engine); L_DECL(event); L_DECL(fru); L_DECL(if); L_DECL(mask); L_DECL(prop); L_DECL(config); /* event types */ L_DECL(fault); L_DECL(upset); L_DECL(defect); L_DECL(error); L_DECL(ereport); /* engine types */ L_DECL(serd); L_DECL(stat); /* timeval suffixes */ L_DECL(nanosecond); L_DECL(nanoseconds); L_DECL(nsec); L_DECL(nsecs); L_DECL(ns); L_DECL(microsecond); L_DECL(microseconds); L_DECL(usec); L_DECL(usecs); L_DECL(us); L_DECL(millisecond); L_DECL(milliseconds); L_DECL(msec); L_DECL(msecs); L_DECL(ms); L_DECL(second); L_DECL(seconds); L_DECL(s); L_DECL(minute); L_DECL(minutes); L_DECL(min); L_DECL(mins); L_DECL(m); L_DECL(hour); L_DECL(hours); L_DECL(hr); L_DECL(hrs); L_DECL(h); L_DECL(day); L_DECL(days); L_DECL(d); L_DECL(week); L_DECL(weeks); L_DECL(wk); L_DECL(wks); L_DECL(month); L_DECL(months); L_DECL(year); L_DECL(years); L_DECL(yr); L_DECL(yrs); L_DECL(infinity); /* property names */ L_DECL(ASRU); L_DECL(action); L_DECL(FITrate); L_DECL(FRU); L_DECL(id); L_DECL(message); L_DECL(retire); L_DECL(response); L_DECL(FRUID); L_DECL(N); L_DECL(T); L_DECL(count); L_DECL(method); L_DECL(poller); L_DECL(timeout); L_DECL(trip); L_DECL(discard_if_config_unknown); /* property values */ L_DECL(A); L_DECL(volatile); L_DECL(persistent); /* event bubble types */ L_DECL(from); L_DECL(to); L_DECL(inhibit); /* * internal function names. note that "fru" and "asru" are also function * names. */ L_DECL(within); L_DECL(call); L_DECL(cat); L_DECL(confcall); L_DECL(confprop); L_DECL(confprop_defined); L_DECL(defined); L_DECL(payloadprop); L_DECL(payloadprop_contains); L_DECL(payloadprop_defined); L_DECL(setpayloadprop); L_DECL(setserdsuffix); L_DECL(setserdincrement); L_DECL(setserdn); L_DECL(setserdt); L_DECL(envprop); L_DECL(is_connected); L_DECL(is_under); L_DECL(is_on); L_DECL(is_present); L_DECL(has_fault); L_DECL(is_type); L_DECL(count); /* our enumerated types (used for debugging) */ L_DECL(T_NOTHING); L_DECL(T_NAME); L_DECL(T_GLOBID); L_DECL(T_ENAME); L_DECL(T_EVENT); L_DECL(T_ENGINE); L_DECL(T_ASRU); L_DECL(T_FRU); L_DECL(T_TIMEVAL); L_DECL(T_NUM); L_DECL(T_QUOTE); L_DECL(T_FUNC); L_DECL(T_NVPAIR); L_DECL(T_ASSIGN); L_DECL(T_CONDIF); L_DECL(T_CONDELSE); L_DECL(T_NOT); L_DECL(T_AND); L_DECL(T_OR); L_DECL(T_EQ); L_DECL(T_NE); L_DECL(T_SUB); L_DECL(T_ADD); L_DECL(T_MUL); L_DECL(T_DIV); L_DECL(T_MOD); L_DECL(T_LT); L_DECL(T_LE); L_DECL(T_GT); L_DECL(T_GE); L_DECL(T_BITAND); L_DECL(T_BITOR); L_DECL(T_BITXOR); L_DECL(T_BITNOT); L_DECL(T_LSHIFT); L_DECL(T_RSHIFT); L_DECL(T_ARROW); L_DECL(T_LIST); L_DECL(T_FAULT); L_DECL(T_UPSET); L_DECL(T_DEFECT); L_DECL(T_ERROR); L_DECL(T_EREPORT); L_DECL(T_SERD); L_DECL(T_STAT); L_DECL(T_PROP); L_DECL(T_MASK); L_DECL(N_UNSPEC); L_DECL(N_FAULT); L_DECL(N_UPSET); L_DECL(N_DEFECT); L_DECL(N_ERROR); L_DECL(N_EREPORT); L_DECL(N_SERD); L_DECL(IT_NONE); L_DECL(IT_VERTICAL); L_DECL(IT_HORIZONTAL); L_DECL(IT_ENAME); /* misc */ L_DECL(nofile); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_LITERALS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * lut.c -- simple lookup table module * * this file contains a very simple lookup table (lut) implementation. * the tables only support insert & lookup, no delete, and can * only be walked in one order. if the key already exists * for something being inserted, the previous entry is simply * replaced. */ #include #include "alloc.h" #include "out.h" #include "stats.h" #include "lut.h" #include "lut_impl.h" #include "tree.h" static struct stats *Addtotal; static struct stats *Lookuptotal; static struct stats *Freetotal; void lut_init(void) { Addtotal = stats_new_counter("lut.adds", "total adds", 1); Lookuptotal = stats_new_counter("lut.lookup", "total lookups", 1); Freetotal = stats_new_counter("lut.frees", "total frees", 1); } void lut_fini(void) { stats_delete(Addtotal); stats_delete(Lookuptotal); stats_delete(Freetotal); } /* * lut_add -- add an entry to the table * * use it like this: * struct lut *root = NULL; * root = lut_add(root, key, value, cmp_func); * * the cmp_func can be strcmp(). pass in NULL and instead of * calling a cmp_func the routine will just look at the difference * between key pointers (useful when all strings are kept in a * string table so they are equal if their pointers are equal). * */ struct lut * lut_add(struct lut *root, void *lhs, void *rhs, lut_cmp cmp_func) { int diff; struct lut **tmp_hdl = &root, *parent = NULL, *tmp = root; while (tmp) { if (cmp_func) diff = (*cmp_func)(tmp->lut_lhs, lhs); else diff = (const char *)lhs - (const char *)tmp->lut_lhs; if (diff == 0) { /* already in tree, replace node */ tmp->lut_rhs = rhs; return (root); } else if (diff > 0) { tmp_hdl = &(tmp->lut_left); parent = tmp; tmp = tmp->lut_left; } else { tmp_hdl = &(tmp->lut_right); parent = tmp; tmp = tmp->lut_right; } } /* either empty tree or not in tree, so create new node */ *tmp_hdl = MALLOC(sizeof (*root)); (*tmp_hdl)->lut_lhs = lhs; (*tmp_hdl)->lut_rhs = rhs; (*tmp_hdl)->lut_parent = parent; (*tmp_hdl)->lut_left = (*tmp_hdl)->lut_right = NULL; stats_counter_bump(Addtotal); return (root); } void * lut_lookup(struct lut *root, void *lhs, lut_cmp cmp_func) { int diff; stats_counter_bump(Lookuptotal); while (root) { if (cmp_func) diff = (*cmp_func)(root->lut_lhs, lhs); else diff = (const char *)lhs - (const char *)root->lut_lhs; if (diff == 0) return (root->lut_rhs); else if (diff > 0) root = root->lut_left; else root = root->lut_right; } return (NULL); } void * lut_lookup_lhs(struct lut *root, void *lhs, lut_cmp cmp_func) { int diff; stats_counter_bump(Lookuptotal); while (root) { if (cmp_func) diff = (*cmp_func)(root->lut_lhs, lhs); else diff = (const char *)lhs - (const char *)root->lut_lhs; if (diff == 0) return (root->lut_lhs); else if (diff > 0) root = root->lut_left; else root = root->lut_right; } return (NULL); } /* * lut_walk -- walk the table in lexical order */ void lut_walk(struct lut *root, lut_cb callback, void *arg) { struct lut *tmp = root; struct lut *prev_child = NULL; if (root == NULL) return; while (tmp->lut_left != NULL) tmp = tmp->lut_left; /* do callback on leftmost node */ (*callback)(tmp->lut_lhs, tmp->lut_rhs, arg); for (;;) { if (tmp->lut_right != NULL && tmp->lut_right != prev_child) { tmp = tmp->lut_right; while (tmp->lut_left != NULL) tmp = tmp->lut_left; /* do callback on leftmost node */ (*callback)(tmp->lut_lhs, tmp->lut_rhs, arg); } else if (tmp->lut_parent != NULL) { prev_child = tmp; tmp = tmp->lut_parent; /* * do callback on parent only if we're coming up * from the left */ if (tmp->lut_right != prev_child) (*callback)(tmp->lut_lhs, tmp->lut_rhs, arg); } else return; } } /* * lut_free -- free the lut */ void lut_free(struct lut *root, lut_cb callback, void *arg) { struct lut *tmp = root; struct lut *prev_child = NULL; if (root == NULL) return; while (tmp->lut_left != NULL) tmp = tmp->lut_left; /* do callback on leftmost node */ if (callback) (*callback)(tmp->lut_lhs, tmp->lut_rhs, arg); for (;;) { if (tmp->lut_right != NULL && tmp->lut_right != prev_child) { tmp = tmp->lut_right; while (tmp->lut_left != NULL) tmp = tmp->lut_left; /* do callback on leftmost node */ if (callback) (*callback)(tmp->lut_lhs, tmp->lut_rhs, arg); } else if (tmp->lut_parent != NULL) { prev_child = tmp; tmp = tmp->lut_parent; FREE(prev_child); /* * do callback on parent only if we're coming up * from the left */ if (tmp->lut_right != prev_child && callback) (*callback)(tmp->lut_lhs, tmp->lut_rhs, arg); } else { /* * free the root node and then we're done */ FREE(tmp); return; } } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * lut.h -- public definitions for lookup table module * * this module is a very simple look-up table implementation. used * all over this program to implement tables of various sorts. */ #ifndef _ESC_COMMON_LUT_H #define _ESC_COMMON_LUT_H #ifdef __cplusplus extern "C" { #endif #include "tree.h" void lut_init(void); void lut_fini(void); typedef int (*lut_cmp)(void *lhs, void *rhs); struct lut *lut_add(struct lut *root, void *lhs, void *rhs, int (*cmp_func)(void *old_lhs, void *new_lhs)); void *lut_lookup(struct lut *root, void *lhs, lut_cmp cmp_func); void *lut_lookup_lhs(struct lut *root, void *lhs, lut_cmp cmp_func); typedef void (*lut_cb)(void *lhs, void *rhs, void *arg); void lut_walk(struct lut *root, lut_cb callback, void *arg); void lut_free(struct lut *root, lut_cb callback, void *arg); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_LUT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _LUT_IMPL_H #define _LUT_IMPL_H #ifdef __cplusplus extern "C" { #endif /* * ipath_impl.h -- ipath private data shared with mdb module */ /* info created by lut_add(), private to lut.c and its mdb module */ struct lut { struct lut *lut_left; struct lut *lut_right; struct lut *lut_parent; void *lut_lhs; /* search key */ void *lut_rhs; /* the datum */ }; #ifdef __cplusplus } #endif #endif /* _LUT_IMPL_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * out.c -- some basic output routines * */ #include #include #include #include #include #include #include #include #include #include "out.h" #include "stats.h" #include "io.h" /* stats we keep for "out" module */ static struct stats *Outcount; static struct stats *Errcount; static struct stats *Warncount; static int Exitcode; static const char *Myname; static FILE *Altfp; /* buffer used to format all prints */ #define MAXOUT 8192 static char Outbuf[MAXOUT]; static int Outidx; /* next unused char in Outbuf[] */ /* * out_init -- initialize this module */ void out_init(const char *myname) { Outcount = stats_new_counter("output.calls", "total calls", 1); Errcount = stats_new_counter("output.errors", "total errors", 0); Warncount = stats_new_counter("output.warnings", "total warnings", 0); if (myname == NULL) return; if ((Myname = strrchr(myname, '/')) == NULL && (Myname = strrchr(myname, '\\')) == NULL) Myname = myname; else Myname++; } void out_fini(void) { stats_delete(Outcount); Outcount = NULL; stats_delete(Errcount); Errcount = NULL; stats_delete(Warncount); Warncount = NULL; } /* * out_altfp -- store an alternate fp for O_ALTFP */ void out_altfp(FILE *fp) { Altfp = fp; } /* * voutbufprintf -- like vprintf, but appends to Outbuf */ static void voutbufprintf(const char *fmt, va_list ap) { int len = vsnprintf(&Outbuf[Outidx], MAXOUT - Outidx, fmt, ap); Outidx += len; if (Outidx >= MAXOUT) Outidx = MAXOUT - 1; } /* * outbufprintf -- like printf, but appends to Outbuf */ /*PRINTFLIKE1*/ static void outbufprintf(const char *fmt, ...) { va_list ap; va_start(ap, fmt); voutbufprintf(fmt, ap); va_end(ap); } /* * vout -- va_list version of out() * * all the output processing work is done here. */ static void vout(int flags, const char *fmt, va_list ap) { int safe_errno = errno; stats_counter_bump(Outcount); /* * just return if called with a disabled output type. this * prevents debug prints when Debug is off, verbose prints * when Verbose is off, and language warnings when warnings * are quenched (which they are when we're loading a .eft file). * */ if ((flags & O_DEBUG) && Debug == 0) return; if ((flags & O_VERB) && Verbose == 0) return; if ((flags & O_VERB2) && Verbose < 2) return; if ((flags & O_VERB3) && Verbose < 3) return; if ((flags & O_WARN) && Warn == 0) return; if ((flags & O_ALTFP) && Altfp == NULL) return; /* some things only happen at the beginning of a print */ if (Outidx == 0) { if (flags & O_USAGE) { Exitcode++; outbufprintf("usage: %s ", Myname); } else { if (Myname && flags & (O_DIE|O_ERR|O_WARN|O_PROG)) outbufprintf("%s: ", Myname); if (flags & O_DIE) { Exitcode++; outbufprintf("fatal error: "); } else if (flags & O_ERR) { Exitcode++; stats_counter_bump(Errcount); outbufprintf("error: "); } else if (flags & O_WARN) { stats_counter_bump(Warncount); outbufprintf("warning: "); } } } /* fmt can be NULL if the caller just wanted flags processed */ if (fmt != NULL) voutbufprintf(fmt, ap); /* O_SYS means convert errno to a string and append it */ if (flags & O_SYS) { const char *msg = strerror(safe_errno); if (Outidx != 0) outbufprintf(": "); if (msg) outbufprintf("%s", msg); else outbufprintf("(error %d)", safe_errno); } /* O_STAMP means convert add a timestamp */ if (flags & O_STAMP) { time_t clock; char *tmsg; (void) time(&clock); tmsg = ctime(&clock); if (tmsg && *tmsg) { tmsg[strlen(tmsg) - 1] = '\0'; if (Outidx != 0) outbufprintf(" "); outbufprintf("%s", tmsg); } } if (flags & O_NONL) return; /* not done filling Outbuf */ /* done filling Outbuf, platform calls will add newline */ if (flags & O_ALTFP) (void) fprintf(Altfp, "%s\n", Outbuf); else if (flags & O_ABORT) io_abort(Outbuf); else if (flags & O_DIE) io_die(Outbuf); else if (flags & O_ERR) io_err(Outbuf); else io_out(Outbuf); /* reset output buffer */ Outidx = 0; Outbuf[0] = '\0'; } /* * out -- spew a line of output, with various options */ /*PRINTFLIKE2*/ void out(int flags, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vout(flags, fmt, ap); va_end(ap); } /* * outfl -- spew a filename:linenumber message */ /*PRINTFLIKE4*/ void outfl(int flags, const char *fname, int line, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (fname) out(flags|O_NONL, "%s:%d: ", fname, line); vout(flags, fmt, ap); va_end(ap); } /* * out_exit -- exit the program */ void out_exit(int code) { io_exit(Exitcode + code); } /* * out_errcount -- return the number of O_ERR messages issued so far */ int out_errcount(void) { return (stats_counter_value(Errcount)); } /* * out_warncount -- return the number of O_WARN messages issued so far */ int out_warncount(void) { return (stats_counter_value(Warncount)); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * out.h -- public definitions for output module * * general output & error handling routines. the routine out() is * the most commonly used routine in this module -- called by virtually * all other modules. */ #ifndef _ESC_COMMON_OUT_H #define _ESC_COMMON_OUT_H #include #include #include #ifdef __cplusplus extern "C" { #endif void out_init(const char *myname); void out_fini(void); void out(int flags, const char *fmt, ...); void outfl(int flags, const char *fname, int line, const char *fmt, ...); void out_exit(int code) __NORETURN; void out_altfp(FILE *fp); int out_errcount(void); int out_warncount(void); /* flags for out() */ #define O_OK 0x0000 /* simple output pseudo-flag */ #define O_DIE 0x0001 /* O_PROG, stderr, bump exit code, call out_exit() */ #define O_ERR 0x0002 /* O_PROG, stderr, bump exit code */ #define O_WARN 0x0004 /* O_PROG, stderr, do nothing unless Warn is set */ #define O_SYS 0x0008 /* append errno text to message */ #define O_STAMP 0x0010 /* append a timestamp */ #define O_ALTFP 0x0020 /* write output to alternate file pointer */ #define O_PROG 0x0040 /* prepend program name to message */ #define O_NONL 0x0080 /* don't append a newline to message */ #define O_DEBUG 0x0100 /* do nothing unless Debug is set */ #define O_VERB 0x0200 /* do nothing unless Verbose is set */ #define O_VERB2 0x0400 /* do nothing unless Verbose >= 2 */ #define O_VERB3 0x2000 /* do nothing unless Verbose >= 3 */ #define O_USAGE 0x0800 /* stderr, usage message */ #define O_ABORT 0x1000 /* call abort() after issuing any output */ #ifdef DEBUG #define ASSERT(cnd) \ ((void)((cnd) || (outfl(O_ABORT, __FILE__, __LINE__, \ "assertion failure: %s", #cnd), 0))) #define ASSERTinfo(cnd, info) \ ((void)((cnd) || (outfl(O_ABORT, __FILE__, __LINE__, \ "assertion failure: %s (%s = %s)", #cnd, #info, info), 0))) #define ASSERTeq(lhs, rhs, tostring) \ ((void)(((lhs) == (rhs)) || (outfl(O_ABORT, __FILE__, __LINE__, \ "assertion failure: %s (%s) == %s (%s)", #lhs, \ tostring(lhs), #rhs, tostring(rhs)), 0))) #define ASSERTne(lhs, rhs, tostring) \ ((void)(((lhs) != (rhs)) || (outfl(O_ABORT, __FILE__, __LINE__, \ "assertion failure: %s (%s) != %s (%s)", #lhs, \ tostring(lhs), #rhs, tostring(rhs)), 0))) #else #define ASSERT(cnd) ((void)0) #define ASSERTinfo(cnd, info) ((void)0) #define ASSERTeq(lhs, rhs, tostring) ((void)0) #define ASSERTne(lhs, rhs, tostring) ((void)0) #endif extern int Debug; extern int Verbose; extern int Warn; /* * so you can say things like: * printf("\t%10d fault statement%s\n", OUTS(Faultcount)); */ #define OUTS(i) (i), ((i) == 1) ? "" : "s" #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_OUT_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ptree.c -- routines for printing the prop tree * * this module contains routines to print portions of the parse tree. */ #include #include #include #include #include "out.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "tree.h" #include "ptree.h" int Pchildgen; #ifdef FMAPLUGIN #include "itree.h" #include "eval.h" #include "config.h" static void cp2num(struct config *cp, int *num) { config_getcompname(cp, NULL, num); } #else /*ARGSUSED*/ static void cp2num(struct config *cp, int *num) { out(O_DIE, "ptree: non-NULL cp"); } #endif /* FMAPLUGIN */ static int is_stmt(struct node *np) { switch (np->t) { case T_FAULT: case T_UPSET: case T_DEFECT: case T_ERROR: case T_EREPORT: case T_SERD: case T_STAT: case T_PROP: case T_MASK: case T_ASRU: case T_FRU: case T_CONFIG: return (1); /*NOTREACHED*/ break; default: break; } return (0); } void ptree(int flags, struct node *np, int no_iterators, int fileline) { if (np == NULL) return; switch (np->t) { case T_NOTHING: break; case T_NAME: out(flags|O_NONL, "%s", np->u.name.s); if (!no_iterators) { if (np->u.name.cp != NULL) { int num; cp2num(np->u.name.cp, &num); out(flags|O_NONL, "%d", num); } else if (np->u.name.it == IT_HORIZONTAL) { if (np->u.name.child == NULL || (np->u.name.childgen && !Pchildgen)) out(flags|O_NONL, "<>"); else { out(flags|O_NONL, "<"); ptree(flags, np->u.name.child, no_iterators, fileline); out(flags|O_NONL, ">"); } } else if (np->u.name.child && (!np->u.name.childgen || Pchildgen)) { if (np->u.name.it != IT_NONE) out(flags|O_NONL, "["); ptree(flags, np->u.name.child, no_iterators, fileline); if (np->u.name.it != IT_NONE) out(flags|O_NONL, "]"); } } if (np->u.name.next) { ASSERT(np->u.name.next->t == T_NAME); if (np->u.name.it == IT_ENAME) out(flags|O_NONL, "."); else out(flags|O_NONL, "/"); ptree(flags, np->u.name.next, no_iterators, fileline); } break; case T_TIMEVAL: ptree_timeval(flags, &np->u.ull); break; case T_NUM: out(flags|O_NONL, "%llu", np->u.ull); break; case T_QUOTE: out(flags|O_NONL, "\"%s\"", np->u.quote.s); break; case T_GLOBID: out(flags|O_NONL, "$%s", np->u.globid.s); break; case T_FUNC: out(flags|O_NONL, "%s(", np->u.func.s); ptree(flags, np->u.func.arglist, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_NVPAIR: ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "="); ptree(flags, np->u.expr.right, no_iterators, fileline); break; case T_EVENT: ptree(flags, np->u.event.ename, no_iterators, fileline); if (np->u.event.epname) { out(flags|O_NONL, "@"); ptree(flags, np->u.event.epname, no_iterators, fileline); } if (np->u.event.eexprlist) { out(flags|O_NONL, "{"); ptree(flags, np->u.event.eexprlist, no_iterators, fileline); out(flags|O_NONL, "}"); } break; case T_ASSIGN: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "="); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_NOT: out(flags|O_NONL, "("); out(flags|O_NONL, "!"); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_AND: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "&&"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_OR: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "||"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_EQ: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "=="); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_NE: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "!="); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_SUB: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "-"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_ADD: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "+"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_MUL: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "*"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_DIV: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "/"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_MOD: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "%%"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_LT: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "<"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_LE: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "<="); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_GT: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, ">"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_GE: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, ">="); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_BITNOT: out(flags|O_NONL, "("); out(flags|O_NONL, "~"); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_BITAND: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "&"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_BITOR: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "|"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_BITXOR: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "^"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_LSHIFT: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "<<"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_RSHIFT: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, ">>"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_CONDIF: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, "?"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_CONDELSE: out(flags|O_NONL, "("); ptree(flags, np->u.expr.left, no_iterators, fileline); out(flags|O_NONL, ":"); ptree(flags, np->u.expr.right, no_iterators, fileline); out(flags|O_NONL, ")"); break; case T_ARROW: ptree(flags, np->u.arrow.lhs, no_iterators, fileline); if (np->u.arrow.nnp) { out(flags|O_NONL, "("); ptree(flags, np->u.arrow.nnp, no_iterators, fileline); out(flags|O_NONL, ")"); } out(flags|O_NONL, "->"); if (np->u.arrow.knp) { out(flags|O_NONL, "("); ptree(flags, np->u.arrow.knp, no_iterators, fileline); out(flags|O_NONL, ")"); } ptree(flags, np->u.arrow.rhs, no_iterators, fileline); break; case T_LIST: ptree(flags, np->u.expr.left, no_iterators, fileline); if (np->u.expr.left && np->u.expr.right && (np->u.expr.left->t != T_LIST || ! is_stmt(np->u.expr.right))) out(flags|O_NONL, ","); ptree(flags, np->u.expr.right, no_iterators, fileline); break; case T_FAULT: case T_UPSET: case T_DEFECT: case T_ERROR: case T_EREPORT: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "event "); ptree(flags, np->u.stmt.np, no_iterators, fileline); if (np->u.stmt.nvpairs) { out(flags|O_NONL, " "); ptree(flags, np->u.stmt.nvpairs, no_iterators, fileline); } out(flags, ";"); break; case T_SERD: case T_STAT: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "engine "); ptree(flags, np->u.stmt.np, no_iterators, fileline); if (np->u.stmt.nvpairs) { out(flags|O_NONL, " "); ptree(flags, np->u.stmt.nvpairs, no_iterators, fileline); } else if (np->u.stmt.lutp) { struct plut_wlk_data pd; pd.flags = flags; pd.first = 0; lut_walk(np->u.stmt.lutp, ptree_plut, &pd); } out(flags, ";"); break; case T_ASRU: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "asru "); ptree(flags, np->u.stmt.np, no_iterators, fileline); if (np->u.stmt.nvpairs) { out(flags|O_NONL, " "); ptree(flags, np->u.stmt.nvpairs, no_iterators, fileline); } out(flags, ";"); break; case T_FRU: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "fru "); ptree(flags, np->u.stmt.np, no_iterators, fileline); if (np->u.stmt.nvpairs) { out(flags|O_NONL, " "); ptree(flags, np->u.stmt.nvpairs, no_iterators, fileline); } out(flags, ";"); break; case T_CONFIG: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "config "); ptree(flags, np->u.stmt.np, no_iterators, fileline); if (np->u.stmt.nvpairs) { out(flags|O_NONL, " "); ptree(flags, np->u.stmt.nvpairs, no_iterators, fileline); } out(flags, ";"); break; case T_PROP: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "prop "); ptree(flags, np->u.stmt.np, no_iterators, fileline); out(flags, ";"); break; case T_MASK: if (fileline) out(flags, "# %d \"%s\"", np->line, np->file); out(flags|O_NONL, "mask "); ptree(flags, np->u.stmt.np, no_iterators, fileline); out(flags, ";"); break; default: out(O_DIE, "internal error: ptree unexpected nodetype: %d", np->t); /*NOTREACHED*/ } } void ptree_plut(void *name, void *val, void *arg) { struct plut_wlk_data *pd = (struct plut_wlk_data *)arg; int c; static int indent; indent++; if (pd->first == 0) out(pd->flags, ","); else pd->first = 0; for (c = indent; c > 0; c--) out(pd->flags|O_NONL, "\t"); out(pd->flags|O_NONL, "%s", (char *)name); out(pd->flags|O_NONL, "="); ptree(pd->flags, val, 0, 0); indent--; } void ptree_name(int flags, struct node *np) { ptree(flags, np, 1, 0); } void ptree_name_iter(int flags, struct node *np) { ptree(flags, np, 0, 0); } const char * ptree_nodetype2str(enum nodetype t) { static char buf[100]; switch (t) { case T_NOTHING: return L_T_NOTHING; case T_NAME: return L_T_NAME; case T_GLOBID: return L_T_GLOBID; case T_EVENT: return L_T_EVENT; case T_ENGINE: return L_T_ENGINE; case T_ASRU: return L_asru; case T_FRU: return L_fru; case T_CONFIG: return L_config; case T_TIMEVAL: return L_T_TIMEVAL; case T_NUM: return L_T_NUM; case T_QUOTE: return L_T_QUOTE; case T_FUNC: return L_T_FUNC; case T_NVPAIR: return L_T_NVPAIR; case T_ASSIGN: return L_T_ASSIGN; case T_CONDIF: return L_T_CONDIF; case T_CONDELSE: return L_T_CONDELSE; case T_NOT: return L_T_NOT; case T_AND: return L_T_AND; case T_OR: return L_T_OR; case T_EQ: return L_T_EQ; case T_NE: return L_T_NE; case T_SUB: return L_T_SUB; case T_ADD: return L_T_ADD; case T_MUL: return L_T_MUL; case T_DIV: return L_T_DIV; case T_MOD: return L_T_MOD; case T_LT: return L_T_LT; case T_LE: return L_T_LE; case T_GT: return L_T_GT; case T_GE: return L_T_GE; case T_BITAND: return L_T_BITAND; case T_BITOR: return L_T_BITOR; case T_BITXOR: return L_T_BITXOR; case T_BITNOT: return L_T_BITNOT; case T_LSHIFT: return L_T_LSHIFT; case T_RSHIFT: return L_T_RSHIFT; case T_ARROW: return L_T_ARROW; case T_LIST: return L_T_LIST; case T_FAULT: return L_fault; case T_UPSET: return L_upset; case T_DEFECT: return L_defect; case T_ERROR: return L_error; case T_EREPORT: return L_ereport; case T_SERD: return L_serd; case T_STAT: return L_stat; case T_PROP: return L_prop; case T_MASK: return L_mask; default: (void) sprintf(buf, "[unexpected nodetype: %d]", t); return (buf); } } const char * ptree_nametype2str(enum nametype t) { static char buf[100]; switch (t) { case N_UNSPEC: return L_N_UNSPEC; case N_FAULT: return L_fault; case N_DEFECT: return L_defect; case N_UPSET: return L_upset; case N_ERROR: return L_error; case N_EREPORT: return L_ereport; case N_SERD: return L_serd; case N_STAT: return L_stat; default: (void) sprintf(buf, "[unexpected nametype: %d]", t); return (buf); } } struct printer_info { enum nodetype t; const char *pat; int flags; }; static int name_pattern_match(struct node *np, const char *pat) { const char *cend; /* first character not in component in pat */ if (pat == NULL || *pat == '\0') return (1); /* either no pattern or we've matched it all */ if (np == NULL) return (0); /* there's more pattern and nothing to match */ ASSERTeq(np->t, T_NAME, ptree_nodetype2str); cend = strchr(pat, '/'); if (cend == NULL) cend = strchr(pat, '.'); if (cend == NULL) cend = &pat[strlen(pat)]; while (np) { const char *s = np->u.name.s; while (*s) { const char *cstart = pat; while (*s && tolower(*s) == tolower(*cstart)) { cstart++; if (cstart == cend) { /* component matched */ while (*cend == '/') cend++; return name_pattern_match(np->u.name.next, cend); } s++; } if (*s) s++; } np = np->u.name.next; } return (0); } static int name_pattern_match_in_subtree(struct node *np, const char *pat) { if (pat == NULL || *pat == '\0') return (1); if (np == NULL) return (0); if (np->t == T_NAME) return (name_pattern_match(np, pat)); else if (np->t == T_EVENT) return (name_pattern_match_in_subtree(np->u.event.ename, pat) || name_pattern_match_in_subtree(np->u.event.epname, pat) || name_pattern_match_in_subtree(np->u.event.eexprlist, pat)); else if (np->t == T_ARROW) return (name_pattern_match_in_subtree(np->u.arrow.lhs, pat) || name_pattern_match_in_subtree(np->u.arrow.rhs, pat)); else if (np->t == T_ASSIGN || np->t == T_CONDIF || np->t == T_CONDELSE || np->t == T_NOT || np->t == T_AND || np->t == T_OR || np->t == T_EQ || np->t == T_NE || np->t == T_SUB || np->t == T_ADD || np->t == T_MUL || np->t == T_DIV || np->t == T_MOD || np->t == T_LT || np->t == T_LE || np->t == T_GT || np->t == T_GE || np->t == T_BITAND || np->t == T_BITOR || np->t == T_BITXOR || np->t == T_BITNOT || np->t == T_LSHIFT || np->t == T_RSHIFT || np->t == T_LIST) { return (name_pattern_match_in_subtree(np->u.expr.left, pat) || name_pattern_match_in_subtree(np->u.expr.right, pat)); } else if (np->t == T_FUNC) { return (name_pattern_match_in_subtree(np->u.func.arglist, pat)); } return (0); } static void byname_printer(struct node *lhs, struct node *rhs, void *arg) { struct printer_info *infop = (struct printer_info *)arg; if (infop->t != T_NOTHING && rhs->t != infop->t) return; if (!name_pattern_match(lhs, infop->pat)) return; ptree(infop->flags, rhs, 0, 0); } static void ptree_type_pattern(int flags, enum nodetype t, const char *pat) { struct printer_info info; struct node *np; info.flags = flags; info.pat = pat; info.t = t; switch (t) { case T_FAULT: lut_walk(Faults, (lut_cb)byname_printer, (void *)&info); return; case T_UPSET: lut_walk(Upsets, (lut_cb)byname_printer, (void *)&info); return; case T_DEFECT: lut_walk(Defects, (lut_cb)byname_printer, (void *)&info); return; case T_ERROR: lut_walk(Errors, (lut_cb)byname_printer, (void *)&info); return; case T_EREPORT: lut_walk(Ereports, (lut_cb)byname_printer, (void *)&info); return; case T_SERD: lut_walk(SERDs, (lut_cb)byname_printer, (void *)&info); return; case T_STAT: lut_walk(STATs, (lut_cb)byname_printer, (void *)&info); return; case T_ASRU: lut_walk(ASRUs, (lut_cb)byname_printer, (void *)&info); return; case T_FRU: lut_walk(FRUs, (lut_cb)byname_printer, (void *)&info); return; case T_CONFIG: lut_walk(Configs, (lut_cb)byname_printer, (void *)&info); return; case T_PROP: for (np = Props; np; np = np->u.stmt.next) if (name_pattern_match_in_subtree(np->u.stmt.np, pat)) ptree(flags, np, 0, 0); return; case T_MASK: for (np = Masks; np; np = np->u.stmt.next) if (name_pattern_match_in_subtree(np->u.stmt.np, pat)) ptree(flags, np, 0, 0); return; default: ptree(flags, tree_root(NULL), 0, 0); } } void ptree_all(int flags, const char *pat) { ptree_type_pattern(flags, T_NOTHING, pat); } void ptree_fault(int flags, const char *pat) { ptree_type_pattern(flags, T_FAULT, pat); } void ptree_upset(int flags, const char *pat) { ptree_type_pattern(flags, T_UPSET, pat); } void ptree_defect(int flags, const char *pat) { ptree_type_pattern(flags, T_DEFECT, pat); } void ptree_error(int flags, const char *pat) { ptree_type_pattern(flags, T_ERROR, pat); } void ptree_ereport(int flags, const char *pat) { ptree_type_pattern(flags, T_EREPORT, pat); } void ptree_serd(int flags, const char *pat) { ptree_type_pattern(flags, T_SERD, pat); } void ptree_stat(int flags, const char *pat) { ptree_type_pattern(flags, T_STAT, pat); } void ptree_asru(int flags, const char *pat) { ptree_type_pattern(flags, T_ASRU, pat); } void ptree_fru(int flags, const char *pat) { ptree_type_pattern(flags, T_FRU, pat); } void ptree_prop(int flags, const char *pat) { ptree_type_pattern(flags, T_PROP, pat); } void ptree_mask(int flags, const char *pat) { ptree_type_pattern(flags, T_MASK, pat); } void ptree_timeval(int flags, unsigned long long *ullp) { unsigned long long val; #define NOREMAINDER(den, num, val) (((val) = ((den) / (num))) * (num) == (den)) if (*ullp == 0) out(flags|O_NONL, "0us"); else if (*ullp >= TIMEVAL_EVENTUALLY) out(flags|O_NONL, "infinity"); else if (NOREMAINDER(*ullp, 1000000000ULL*60*60*24*365, val)) out(flags|O_NONL, "%lluyear%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000000ULL*60*60*24*30, val)) out(flags|O_NONL, "%llumonth%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000000ULL*60*60*24*7, val)) out(flags|O_NONL, "%lluweek%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000000ULL*60*60*24, val)) out(flags|O_NONL, "%lluday%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000000ULL*60*60, val)) out(flags|O_NONL, "%lluhour%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000000ULL*60, val)) out(flags|O_NONL, "%lluminute%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000000ULL, val)) out(flags|O_NONL, "%llusecond%s", val, (val == 1) ? "" : "s"); else if (NOREMAINDER(*ullp, 1000000ULL, val)) out(flags|O_NONL, "%llums", val); else if (NOREMAINDER(*ullp, 1000ULL, val)) out(flags|O_NONL, "%lluus", val); else out(flags|O_NONL, "%lluns", *ullp); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ptree.h -- public definitions for tree print module * * these routines are used to print the "struct node" data * structures from tree.h. they call out() to do the printing. */ #ifndef _ESC_COMMON_PTREE_H #define _ESC_COMMON_PTREE_H #ifdef __cplusplus extern "C" { #endif #include /* * Use a pointer to one of these structs as the "arg" argument when * lut_walk()-ing with ptree_plut() as the callback. "plut" is a * property lut, where the lhs is expected to be a const char * and * the rhs is a struct node. * * flags is passed to out() * first = 1 indicates the first in a list, first != 1 implies a later * element and thus ptree_plut() adds a preceding comma * */ struct plut_wlk_data { int flags; int first; }; void ptree(int flags, struct node *np, int no_iterators, int fileline); void ptree_name(int flags, struct node *np); void ptree_name_iter(int flags, struct node *np); void ptree_all(int flags, const char *pat); void ptree_fault(int flags, const char *pat); void ptree_upset(int flags, const char *pat); void ptree_defect(int flags, const char *pat); void ptree_error(int flags, const char *pat); void ptree_ereport(int flags, const char *pat); void ptree_serd(int flags, const char *pat); void ptree_stat(int flags, const char *pat); void ptree_config(int flags, const char *pat); void ptree_prop(int flags, const char *pat); void ptree_mask(int flags, const char *pat); void ptree_timeval(int flags, unsigned long long *ullp); void ptree_plut(void *name, void *val, void *arg); const char *ptree_nodetype2str(enum nodetype t); const char *ptree_nametype2str(enum nametype t); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_PTREE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * stable.c -- string table module * * simple string table module. all read-only strings are entered in * this table, allowing us to compare pointers rather than characters * to see if two strings are equal. * */ #include #include #include "alloc.h" #include "out.h" #include "stats.h" #include "stable.h" #define MINPTR_ALIGN sizeof (char *) /* alignment boundary for pointers */ #define DEF_HASH_SIZE 11113 /* default hash table size */ #define CHUNK_SIZE 8192 /* grab more memory with this chunk size */ static char **Stable; /* the hash table */ static unsigned Stablesz; static char *Stableblock; static char *Stablenext; static struct stats *Stablecount; static struct stats *Blockcount; static struct stats *Add0; static struct stats *Add1; static struct stats *Add2; static struct stats *Add3; static struct stats *Addn; struct chunklst { struct chunklst *next; char *chunkp; }; struct chunklst *Stablechunks; /* * stable_init -- initialize the stable module * * hash table is sized according to sz. sz of zero means pick * reasonable default size. */ void stable_init(unsigned sz) { /* allocate hash table */ if (sz == 0) Stablesz = DEF_HASH_SIZE; else Stablesz = sz; Stable = MALLOC(Stablesz * sizeof (*Stable)); bzero((void *)Stable, Stablesz * sizeof (*Stable)); Stablecount = stats_new_counter("stable.size", "hash table size", 1); Blockcount = stats_new_counter("stable.blocks", "blocks allocated", 1); Add0 = stats_new_counter("stable.add0", "adds to empty buckets", 1); Add1 = stats_new_counter("stable.add1", "adds to 1-entry buckets", 1); Add2 = stats_new_counter("stable.add2", "adds to 2-entry buckets", 1); Add3 = stats_new_counter("stable.add3", "adds to 3-entry buckets", 1); Addn = stats_new_counter("stable.addn", "adds to n-entry buckets", 1); stats_counter_add(Stablecount, Stablesz); } void stable_fini(void) { struct chunklst *cp, *nc; stats_delete(Stablecount); stats_delete(Blockcount); stats_delete(Add0); stats_delete(Add1); stats_delete(Add2); stats_delete(Add3); stats_delete(Addn); FREE(Stable); cp = Stablechunks; nc = NULL; while (cp != NULL) { nc = cp->next; FREE(cp->chunkp); FREE(cp); cp = nc; } Stablechunks = NULL; } static char * stable_newchunk(void) { struct chunklst *save = Stablechunks; char *n; n = MALLOC(CHUNK_SIZE); bzero((void *)n, CHUNK_SIZE); stats_counter_bump(Blockcount); Stablechunks = MALLOC(sizeof (struct chunklst)); Stablechunks->next = save; Stablechunks->chunkp = n; return (n); } /* * stable -- create/lookup a string table entry */ const char * stable(const char *s) { unsigned slen = 0; unsigned hash = DEF_HASH_SIZE ^ ((unsigned)*s << 2); char **ptrp; char *ptr; char *eptr; const char *sptr; int collisions = 0; if (Stablesz == 0) out(O_DIE, "internal error: Stablesz not set"); for (sptr = &s[1]; *sptr; sptr++) { slen++; hash ^= (((unsigned)*sptr) << (slen % 3)) + ((unsigned)*(sptr - 1) << ((slen % 3 + 7))); } hash ^= slen; if (slen > CHUNK_SIZE - sizeof (char *) - 1 - 4) out(O_DIE, "too big for string table %.20s...", s); hash %= Stablesz; ptrp = &Stable[hash]; ptr = *ptrp; while (ptr) { /* hash brought us to something, see if it is the string */ sptr = s; eptr = ptr; while (*sptr && *eptr && *sptr++ == *eptr++) ; if (*sptr == '\0' && *eptr == '\0') return (ptr); /* found it */ /* strings didn't match, advance eptr to end of string */ while (*eptr) eptr++; eptr++; /* move past '\0' */ while ((uintptr_t)eptr % MINPTR_ALIGN) eptr++; /* pull in next pointer in bucket */ ptrp = (char **)(void *)eptr; ptr = *ptrp; collisions++; } /* string wasn't in table, add it and point ptr to it */ if (Stablenext == NULL || (&Stableblock[CHUNK_SIZE] - Stablenext) < (slen + sizeof (char *) + MINPTR_ALIGN + 4)) { /* need more room */ Stablenext = Stableblock = stable_newchunk(); } /* current chunk has room in it */ ptr = *ptrp = Stablenext; sptr = s; while (*Stablenext++ = *sptr++) ; while ((uintptr_t)Stablenext % MINPTR_ALIGN) Stablenext++; ptrp = (char **)(void *)Stablenext; Stablenext += sizeof (char *); *ptrp = NULL; /* just did an add, update stats */ if (collisions == 0) stats_counter_bump(Add0); else if (collisions == 1) stats_counter_bump(Add1); else if (collisions == 2) stats_counter_bump(Add2); else if (collisions == 3) stats_counter_bump(Add3); else stats_counter_bump(Addn); return (ptr); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * stable.h -- public definitions for string table module * * simple string table module. all calls to stable() with the same * input string will return the same string pointer. all strings * contained in the input file are stored in the string table. comparing * two strings is usually done by comparing their pointers, rather than * by calling strcmp(). */ #ifndef _ESC_COMMON_STABLE_H #define _ESC_COMMON_STABLE_H #ifdef __cplusplus extern "C" { #endif void stable_init(unsigned sz); void stable_fini(void); const char *stable(const char *s); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_STABLE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * stats.c -- simple stats tracking table module * */ #include #include #include "stats.h" #include "alloc.h" #include "out.h" struct stats { struct stats *next; const char *name; const char *desc; enum stats_type { STATS_COUNTER = 3000, STATS_ELAPSE, STATS_STRING } t; union { int counter; struct { hrtime_t start; hrtime_t stop; } elapse; const char *string; } u; }; static int Ext; /* true if extended stats are enabled */ static struct stats *Statslist; static struct stats *Laststats; /* * stats_init -- initialize the stats module * */ void stats_init(int ext) { Ext = ext; } void stats_fini(void) { } static struct stats * stats_new(const char *name, const char *desc, enum stats_type t) { struct stats *ret = MALLOC(sizeof (*ret)); bzero(ret, sizeof (*ret)); ret->t = t; ret->name = STRDUP(name); ret->desc = STRDUP(desc); if (Laststats == NULL) Statslist = ret; else Laststats->next = ret; Laststats = ret; return (ret); } void stats_delete(struct stats *sp) { struct stats *p, *s; if (sp == NULL) return; for (p = NULL, s = Statslist; s != NULL; s = s->next) if (s == sp) break; if (s == NULL) return; if (p == NULL) Statslist = s->next; else p->next = s->next; if (s == Laststats) Laststats = p; FREE((void *)sp->name); FREE((void *)sp->desc); FREE(sp); } struct stats * stats_new_counter(const char *name, const char *desc, int ext) { if (ext && !Ext) return (NULL); /* extended stats not enabled */ return (stats_new(name, desc, STATS_COUNTER)); } void stats_counter_bump(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_COUNTER); sp->u.counter++; } void stats_counter_add(struct stats *sp, int n) { if (sp == NULL) return; ASSERT(sp->t == STATS_COUNTER); sp->u.counter += n; } void stats_counter_reset(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_COUNTER); sp->u.counter = 0; } int stats_counter_value(struct stats *sp) { if (sp == NULL) return (0); ASSERT(sp->t == STATS_COUNTER); return (sp->u.counter); } struct stats * stats_new_elapse(const char *name, const char *desc, int ext) { if (ext && !Ext) return (NULL); /* extended stats not enabled */ return (stats_new(name, desc, STATS_ELAPSE)); } void stats_elapse_start(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_ELAPSE); sp->u.elapse.start = gethrtime(); } void stats_elapse_stop(struct stats *sp) { if (sp == NULL) return; ASSERT(sp->t == STATS_ELAPSE); sp->u.elapse.stop = gethrtime(); } struct stats * stats_new_string(const char *name, const char *desc, int ext) { if (ext && !Ext) return (NULL); /* extended stats not enabled */ return (stats_new(name, desc, STATS_STRING)); } void stats_string_set(struct stats *sp, const char *s) { if (sp == NULL) return; ASSERT(sp->t == STATS_STRING); sp->u.string = s; } /* * stats_publish -- spew all stats * */ void stats_publish(void) { struct stats *sp; for (sp = Statslist; sp; sp = sp->next) switch (sp->t) { case STATS_COUNTER: out(O_OK, "%32s %13d %s", sp->name, sp->u.counter, sp->desc); break; case STATS_ELAPSE: if (sp->u.elapse.start && sp->u.elapse.stop) { hrtime_t delta = sp->u.elapse.stop - sp->u.elapse.start; out(O_OK, "%32s %11lldns %s", sp->name, delta, sp->desc); } break; case STATS_STRING: out(O_OK, "%32s %13s %s", sp->name, sp->u.string, sp->desc); break; default: out(O_DIE, "stats_publish: unknown type %d", sp->t); } } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * stats.h -- public definitions for stats module * */ #ifndef _ESC_COMMON_STATS_H #define _ESC_COMMON_STATS_H #ifdef __cplusplus extern "C" { #endif void stats_init(int ext); void stats_fini(void); void stats_publish(void); struct stats *stats_new_counter(const char *name, const char *desc, int ext); void stats_delete(struct stats *sp); void stats_counter_bump(struct stats *sp); void stats_counter_add(struct stats *sp, int n); void stats_counter_reset(struct stats *sp); int stats_counter_value(struct stats *sp); struct stats *stats_new_elapse(const char *name, const char *desc, int ext); void stats_elapse_start(struct stats *sp); void stats_elapse_stop(struct stats *sp); struct stats *stats_new_string(const char *name, const char *desc, int ext); void stats_string_set(struct stats *sp, const char *s); #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_STATS_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * tree.c -- routines for manipulating the prop tree * * the actions in escparse.y call these routines to construct * the parse tree. these routines, in turn, call the check_X() * routines for semantic checking. */ #include #include #include #include #include #include "alloc.h" #include "out.h" #include "stats.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "esclex.h" #include "tree.h" #include "check.h" #include "ptree.h" struct lut *Faults; struct lut *Upsets; struct lut *Defects; struct lut *Errors; struct lut *Ereports; struct lut *Ereportenames; struct lut *Ereportenames_discard; struct lut *SERDs; struct lut *STATs; struct lut *ASRUs; struct lut *FRUs; struct lut *Configs; struct node *Props; struct node *Lastprops; struct node *Masks; struct node *Lastmasks; struct node *Problems; struct node *Lastproblems; static struct node *Root; static char *Newname; static struct stats *Faultcount; static struct stats *Upsetcount; static struct stats *Defectcount; static struct stats *Errorcount; static struct stats *Ereportcount; static struct stats *SERDcount; static struct stats *STATcount; static struct stats *ASRUcount; static struct stats *FRUcount; static struct stats *Configcount; static struct stats *Propcount; static struct stats *Maskcount; static struct stats *Nodecount; static struct stats *Namecount; static struct stats *Nodesize; struct lut *Usedprops; void tree_init(void) { Faultcount = stats_new_counter("parser.fault", "fault decls", 1); Upsetcount = stats_new_counter("parser.upset", "upset decls", 1); Defectcount = stats_new_counter("parser.defect", "defect decls", 1); Errorcount = stats_new_counter("parser.error", "error decls", 1); Ereportcount = stats_new_counter("parser.ereport", "ereport decls", 1); SERDcount = stats_new_counter("parser.SERD", "SERD engine decls", 1); STATcount = stats_new_counter("parser.STAT", "STAT engine decls", 1); ASRUcount = stats_new_counter("parser.ASRU", "ASRU decls", 1); FRUcount = stats_new_counter("parser.FRU", "FRU decls", 1); Configcount = stats_new_counter("parser.config", "config stmts", 1); Propcount = stats_new_counter("parser.prop", "prop stmts", 1); Maskcount = stats_new_counter("parser.mask", "mask stmts", 1); Nodecount = stats_new_counter("parser.node", "nodes created", 1); Namecount = stats_new_counter("parser.name", "names created", 1); Nodesize = stats_new_counter("parser.nodesize", "sizeof(struct node)", 1); stats_counter_add(Nodesize, sizeof (struct node)); } void tree_fini(void) { stats_delete(Faultcount); stats_delete(Upsetcount); stats_delete(Defectcount); stats_delete(Errorcount); stats_delete(Ereportcount); stats_delete(SERDcount); stats_delete(STATcount); stats_delete(ASRUcount); stats_delete(FRUcount); stats_delete(Configcount); stats_delete(Propcount); stats_delete(Maskcount); stats_delete(Nodecount); stats_delete(Namecount); stats_delete(Nodesize); /* free entire parse tree */ tree_free(Root); /* free up the luts we keep for decls */ lut_free(Faults, NULL, NULL); Faults = NULL; lut_free(Upsets, NULL, NULL); Upsets = NULL; lut_free(Defects, NULL, NULL); Defects = NULL; lut_free(Errors, NULL, NULL); Errors = NULL; lut_free(Ereports, NULL, NULL); Ereports = NULL; lut_free(Ereportenames, NULL, NULL); Ereportenames = NULL; lut_free(Ereportenames_discard, NULL, NULL); Ereportenames_discard = NULL; lut_free(SERDs, NULL, NULL); SERDs = NULL; lut_free(STATs, NULL, NULL); STATs = NULL; lut_free(ASRUs, NULL, NULL); ASRUs = NULL; lut_free(FRUs, NULL, NULL); FRUs = NULL; lut_free(Configs, NULL, NULL); Configs = NULL; lut_free(Usedprops, NULL, NULL); Usedprops = NULL; Props = Lastprops = NULL; Masks = Lastmasks = NULL; Problems = Lastproblems = NULL; if (Newname != NULL) { FREE(Newname); Newname = NULL; } } /*ARGSUSED*/ static int nodesize(enum nodetype t, struct node *ret) { int size = sizeof (struct node); switch (t) { case T_NAME: size += sizeof (ret->u.name) - sizeof (ret->u); break; case T_GLOBID: size += sizeof (ret->u.globid) - sizeof (ret->u); break; case T_TIMEVAL: case T_NUM: size += sizeof (ret->u.ull) - sizeof (ret->u); break; case T_QUOTE: size += sizeof (ret->u.quote) - sizeof (ret->u); break; case T_FUNC: size += sizeof (ret->u.func) - sizeof (ret->u); break; case T_FAULT: case T_UPSET: case T_DEFECT: case T_ERROR: case T_EREPORT: case T_ASRU: case T_FRU: case T_SERD: case T_STAT: case T_CONFIG: case T_PROP: case T_MASK: size += sizeof (ret->u.stmt) - sizeof (ret->u); break; case T_EVENT: size += sizeof (ret->u.event) - sizeof (ret->u); break; case T_ARROW: size += sizeof (ret->u.arrow) - sizeof (ret->u); break; default: size += sizeof (ret->u.expr) - sizeof (ret->u); break; } return (size); } struct node * newnode(enum nodetype t, const char *file, int line) { struct node *ret = NULL; int size = nodesize(t, ret); ret = alloc_xmalloc(size); stats_counter_bump(Nodecount); bzero(ret, size); ret->t = t; ret->file = (file == NULL) ? "" : file; ret->line = line; return (ret); } /*ARGSUSED*/ void tree_free(struct node *root) { if (root == NULL) return; switch (root->t) { case T_NAME: tree_free(root->u.name.child); tree_free(root->u.name.next); break; case T_FUNC: tree_free(root->u.func.arglist); break; case T_AND: case T_OR: case T_EQ: case T_NE: case T_ADD: case T_DIV: case T_MOD: case T_MUL: case T_SUB: case T_LT: case T_LE: case T_GT: case T_GE: case T_BITAND: case T_BITOR: case T_BITXOR: case T_BITNOT: case T_LSHIFT: case T_RSHIFT: case T_NVPAIR: case T_ASSIGN: case T_CONDIF: case T_CONDELSE: case T_LIST: tree_free(root->u.expr.left); tree_free(root->u.expr.right); break; case T_EVENT: tree_free(root->u.event.ename); tree_free(root->u.event.epname); tree_free(root->u.event.eexprlist); break; case T_NOT: tree_free(root->u.expr.left); break; case T_ARROW: tree_free(root->u.arrow.lhs); tree_free(root->u.arrow.nnp); tree_free(root->u.arrow.knp); tree_free(root->u.arrow.rhs); break; case T_PROP: case T_MASK: tree_free(root->u.stmt.np); break; case T_FAULT: case T_UPSET: case T_DEFECT: case T_ERROR: case T_EREPORT: case T_ASRU: case T_FRU: case T_SERD: case T_STAT: case T_CONFIG: tree_free(root->u.stmt.np); if (root->u.stmt.nvpairs) tree_free(root->u.stmt.nvpairs); if (root->u.stmt.lutp) lut_free(root->u.stmt.lutp, NULL, NULL); break; case T_TIMEVAL: case T_NUM: case T_QUOTE: case T_GLOBID: case T_NOTHING: break; default: out(O_DIE, "internal error: tree_free unexpected nodetype: %d", root->t); /*NOTREACHED*/ } alloc_xfree((char *)root, nodesize(root->t, root)); } static int tree_treecmp(struct node *np1, struct node *np2, enum nodetype t, lut_cmp cmp_func) { if (np1 == NULL || np2 == NULL) return (0); if (np1->t != np2->t) return (1); ASSERT(cmp_func != NULL); if (np1->t == t) return ((*cmp_func)(np1, np2)); switch (np1->t) { case T_NAME: if (tree_treecmp(np1->u.name.child, np2->u.name.child, t, cmp_func)) return (1); return (tree_treecmp(np1->u.name.next, np2->u.name.next, t, cmp_func)); /*NOTREACHED*/ break; case T_FUNC: return (tree_treecmp(np1->u.func.arglist, np2->u.func.arglist, t, cmp_func)); /*NOTREACHED*/ break; case T_AND: case T_OR: case T_EQ: case T_NE: case T_ADD: case T_DIV: case T_MOD: case T_MUL: case T_SUB: case T_LT: case T_LE: case T_GT: case T_GE: case T_BITAND: case T_BITOR: case T_BITXOR: case T_BITNOT: case T_LSHIFT: case T_RSHIFT: case T_NVPAIR: case T_ASSIGN: case T_CONDIF: case T_CONDELSE: case T_LIST: if (tree_treecmp(np1->u.expr.left, np2->u.expr.left, t, cmp_func)) return (1); return (tree_treecmp(np1->u.expr.right, np2->u.expr.right, t, cmp_func)); /*NOTREACHED*/ break; case T_EVENT: if (tree_treecmp(np1->u.event.ename, np2->u.event.ename, t, cmp_func)) return (1); if (tree_treecmp(np1->u.event.epname, np2->u.event.epname, t, cmp_func)) return (1); return (tree_treecmp(np1->u.event.eexprlist, np2->u.event.eexprlist, t, cmp_func)); /*NOTREACHED*/ break; case T_NOT: return (tree_treecmp(np1->u.expr.left, np2->u.expr.left, t, cmp_func)); /*NOTREACHED*/ break; case T_ARROW: if (tree_treecmp(np1->u.arrow.lhs, np2->u.arrow.lhs, t, cmp_func)) return (1); if (tree_treecmp(np1->u.arrow.nnp, np2->u.arrow.nnp, t, cmp_func)) return (1); if (tree_treecmp(np1->u.arrow.knp, np2->u.arrow.knp, t, cmp_func)) return (1); return (tree_treecmp(np1->u.arrow.rhs, np2->u.arrow.rhs, t, cmp_func)); /*NOTREACHED*/ break; case T_PROP: case T_MASK: return (tree_treecmp(np1->u.stmt.np, np2->u.stmt.np, t, cmp_func)); /*NOTREACHED*/ break; case T_FAULT: case T_UPSET: case T_DEFECT: case T_ERROR: case T_EREPORT: case T_ASRU: case T_FRU: case T_SERD: case T_STAT: if (tree_treecmp(np1->u.stmt.np, np2->u.stmt.np, t, cmp_func)) return (1); return (tree_treecmp(np1->u.stmt.nvpairs, np2->u.stmt.nvpairs, t, cmp_func)); /*NOTREACHED*/ break; case T_TIMEVAL: case T_NUM: case T_QUOTE: case T_GLOBID: case T_NOTHING: break; default: out(O_DIE, "internal error: tree_treecmp unexpected nodetype: %d", np1->t); /*NOTREACHED*/ break; } return (0); } struct node * tree_root(struct node *np) { if (np) Root = np; return (Root); } struct node * tree_nothing(void) { return (newnode(T_NOTHING, L_nofile, 0)); } struct node * tree_expr(enum nodetype t, struct node *left, struct node *right) { struct node *ret; ASSERTinfo(left != NULL || right != NULL, ptree_nodetype2str(t)); ret = newnode(t, (left) ? left->file : right->file, (left) ? left->line : right->line); ret->u.expr.left = left; ret->u.expr.right = right; check_expr(ret); return (ret); } /* * ename_compress -- convert event class name in to more space-efficient form * * this routine is called after the parser has completed an "ename", which * is that part of an event that contains the class name (like ereport.x.y.z). * after this routine gets done with the ename, two things are true: * 1. the ename uses only a single struct node * 2. ename->u.name.s contains the *complete* class name, dots and all, * entered into the string table. * * so in addition to saving space by using fewer struct nodes, this routine * allows consumers of the fault tree to assume the ename is a single * string, rather than a linked list of strings. */ static struct node * ename_compress(struct node *ename) { char *buf; char *cp; int len = 0; struct node *np; if (ename == NULL) return (ename); ASSERT(ename->t == T_NAME); if (ename->u.name.next == NULL) return (ename); /* no compression to be applied here */ for (np = ename; np != NULL; np = np->u.name.next) { ASSERT(np->t == T_NAME); len++; /* room for '.' and final '\0' */ len += strlen(np->u.name.s); } cp = buf = alloca(len); for (np = ename; np != NULL; np = np->u.name.next) { ASSERT(np->t == T_NAME); if (np != ename) *cp++ = '.'; (void) strcpy(cp, np->u.name.s); cp += strlen(cp); } ename->u.name.s = stable(buf); tree_free(ename->u.name.next); ename->u.name.next = NULL; ename->u.name.last = ename; return (ename); } struct node * tree_event(struct node *ename, struct node *epname, struct node *eexprlist) { struct node *ret; ASSERT(ename != NULL); ret = newnode(T_EVENT, ename->file, ename->line); ret->u.event.ename = ename_compress(ename); ret->u.event.epname = epname; ret->u.event.eexprlist = eexprlist; check_event(ret); return (ret); } struct node * tree_name(const char *s, enum itertype it, const char *file, int line) { struct node *ret = newnode(T_NAME, file, line); ASSERT(s != NULL); stats_counter_bump(Namecount); ret->u.name.t = N_UNSPEC; ret->u.name.s = stable(s); ret->u.name.it = it; ret->u.name.last = ret; if (it == IT_ENAME) { /* PHASE2, possible optimization: convert to table driven */ if (s == L_fault) ret->u.name.t = N_FAULT; else if (s == L_upset) ret->u.name.t = N_UPSET; else if (s == L_defect) ret->u.name.t = N_DEFECT; else if (s == L_error) ret->u.name.t = N_ERROR; else if (s == L_ereport) ret->u.name.t = N_EREPORT; else if (s == L_serd) ret->u.name.t = N_SERD; else if (s == L_stat) ret->u.name.t = N_STAT; else outfl(O_ERR, file, line, "unknown class: %s", s); } return (ret); } struct node * tree_iname(const char *s, const char *file, int line) { struct node *ret; char *ss; char *ptr; ASSERT(s != NULL && *s != '\0'); ss = STRDUP(s); ptr = &ss[strlen(ss) - 1]; if (!isdigit(*ptr)) { outfl(O_ERR, file, line, "instanced name expected (i.e. \"x0/y1\")"); FREE(ss); return (tree_name(s, IT_NONE, file, line)); } while (ptr > ss && isdigit(*(ptr - 1))) ptr--; ret = newnode(T_NAME, file, line); stats_counter_bump(Namecount); ret->u.name.child = tree_num(ptr, file, line); *ptr = '\0'; ret->u.name.t = N_UNSPEC; ret->u.name.s = stable(ss); ret->u.name.it = IT_NONE; ret->u.name.last = ret; FREE(ss); return (ret); } struct node * tree_globid(const char *s, const char *file, int line) { struct node *ret = newnode(T_GLOBID, file, line); ASSERT(s != NULL); ret->u.globid.s = stable(s); return (ret); } struct node * tree_name_append(struct node *np1, struct node *np2) { ASSERT(np1 != NULL && np2 != NULL); if (np1->t != T_NAME) outfl(O_DIE, np1->file, np1->line, "tree_name_append: internal error (np1 type %d)", np1->t); if (np2->t != T_NAME) outfl(O_DIE, np2->file, np2->line, "tree_name_append: internal error (np2 type %d)", np2->t); ASSERT(np1->u.name.last != NULL); np1->u.name.last->u.name.next = np2; np1->u.name.last = np2; return (np1); } /* * tree_name_repairdash -- repair a class name that contained a dash * * this routine is called by the parser when a dash is encountered * in a class name. the event protocol allows the dashes but our * lexer considers them a separate token (arithmetic minus). an extra * rule in the parser catches this case and calls this routine to fixup * the last component of the class name (so far) by constructing the * new stable entry for a name including the dash. */ struct node * tree_name_repairdash(struct node *np, const char *s) { int len; char *buf; ASSERT(np != NULL && s != NULL); if (np->t != T_NAME) outfl(O_DIE, np->file, np->line, "tree_name_repairdash: internal error (np type %d)", np->t); ASSERT(np->u.name.last != NULL); len = strlen(np->u.name.last->u.name.s) + 1 + strlen(s) + 1; buf = MALLOC(len); (void) snprintf(buf, len, "%s-%s", np->u.name.last->u.name.s, s); np->u.name.last->u.name.s = stable(buf); FREE(buf); return (np); } struct node * tree_name_repairdash2(const char *s, struct node *np) { int len; char *buf; ASSERT(np != NULL && s != NULL); if (np->t != T_NAME) outfl(O_DIE, np->file, np->line, "tree_name_repairdash: internal error (np type %d)", np->t); ASSERT(np->u.name.last != NULL); len = strlen(np->u.name.last->u.name.s) + 1 + strlen(s) + 1; buf = MALLOC(len); (void) snprintf(buf, len, "%s-%s", s, np->u.name.last->u.name.s); np->u.name.last->u.name.s = stable(buf); FREE(buf); return (np); } struct node * tree_name_iterator(struct node *np1, struct node *np2) { ASSERT(np1 != NULL); ASSERT(np2 != NULL); ASSERTinfo(np1->t == T_NAME, ptree_nodetype2str(np1->t)); np1->u.name.child = np2; check_name_iterator(np1); return (np1); } struct node * tree_timeval(const char *s, const char *suffix, const char *file, int line) { struct node *ret = newnode(T_TIMEVAL, file, line); const unsigned long long *ullp; ASSERT(s != NULL); ASSERT(suffix != NULL); if ((ullp = lex_s2ullp_lut_lookup(Timesuffixlut, suffix)) == NULL) { outfl(O_ERR, file, line, "unrecognized number suffix: %s", suffix); /* still construct a valid timeval node so parsing continues */ ret->u.ull = 1; } else { ret->u.ull = (unsigned long long)strtoul(s, NULL, 0) * *ullp; } return (ret); } struct node * tree_num(const char *s, const char *file, int line) { struct node *ret = newnode(T_NUM, file, line); ret->u.ull = (unsigned long long)strtoul(s, NULL, 0); return (ret); } struct node * tree_quote(const char *s, const char *file, int line) { struct node *ret = newnode(T_QUOTE, file, line); ret->u.quote.s = stable(s); return (ret); } struct node * tree_func(const char *s, struct node *np, const char *file, int line) { struct node *ret = newnode(T_FUNC, file, line); const char *ptr; ret->u.func.s = s; ret->u.func.arglist = np; check_func(ret); /* * keep track of the properties we're interested in so we can ignore the * rest */ if (strcmp(s, L_confprop) == 0 || strcmp(s, L_confprop_defined) == 0) { ptr = stable(np->u.expr.right->u.quote.s); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); } else if (strcmp(s, L_is_connected) == 0) { ptr = stable("connected"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); ptr = stable("CONNECTED"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); } else if (strcmp(s, L_is_type) == 0) { ptr = stable("type"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); ptr = stable("TYPE"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); } else if (strcmp(s, L_is_on) == 0) { ptr = stable("on"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); ptr = stable("ON"); Usedprops = lut_add(Usedprops, (void *)ptr, (void *)ptr, NULL); } return (ret); } /* * given a list from a prop or mask statement or a function argument, * convert all iterators to explicit iterators by inventing appropriate * iterator names. */ static void make_explicit(struct node *np, int eventonly) { struct node *pnp; /* component of pathname */ struct node *pnp2; int count; static size_t namesz; if (Newname == NULL) { namesz = 200; Newname = MALLOC(namesz); } if (np == NULL) return; /* all done */ switch (np->t) { case T_ASSIGN: case T_CONDIF: case T_CONDELSE: case T_NE: case T_EQ: case T_LT: case T_LE: case T_GT: case T_GE: case T_BITAND: case T_BITOR: case T_BITXOR: case T_BITNOT: case T_LSHIFT: case T_RSHIFT: case T_LIST: case T_AND: case T_OR: case T_NOT: case T_ADD: case T_SUB: case T_MUL: case T_DIV: case T_MOD: make_explicit(np->u.expr.left, eventonly); make_explicit(np->u.expr.right, eventonly); break; case T_EVENT: make_explicit(np->u.event.epname, 0); make_explicit(np->u.event.eexprlist, 1); break; case T_FUNC: make_explicit(np->u.func.arglist, eventonly); break; case T_NAME: if (eventonly) return; for (pnp = np; pnp != NULL; pnp = pnp->u.name.next) if (pnp->u.name.child == NULL) { /* * found implicit iterator. convert * it to an explicit iterator by * using the name of the component * appended with '#' and the number * of times we've seen this same * component name in this path so far. */ count = 0; for (pnp2 = np; pnp2 != NULL; pnp2 = pnp2->u.name.next) if (pnp2 == pnp) break; else if (pnp2->u.name.s == pnp->u.name.s) count++; if (namesz < strlen(pnp->u.name.s) + 100) { namesz = strlen(pnp->u.name.s) + 100; FREE(Newname); Newname = MALLOC(namesz); } /* * made up interator name is: * name#ordinal * or * name##ordinal * the first one is used for vertical * expansion, the second for horizontal. * either way, the '#' embedded in * the name makes it impossible to * collide with an actual iterator * given to us in the eversholt file. */ (void) snprintf(Newname, namesz, "%s#%s%d", pnp->u.name.s, (pnp->u.name.it == IT_HORIZONTAL) ? "#" : "", count); pnp->u.name.child = tree_name(Newname, IT_NONE, pnp->file, pnp->line); pnp->u.name.childgen = 1; } break; } } struct node * tree_pname(struct node *np) { make_explicit(np, 0); return (np); } struct node * tree_arrow(struct node *lhs, struct node *nnp, struct node *knp, struct node *rhs) { struct node *ret; ASSERT(lhs != NULL || rhs != NULL); ret = newnode(T_ARROW, (lhs) ? lhs->file : rhs->file, (lhs) ? lhs->line : rhs->line); ret->u.arrow.lhs = lhs; ret->u.arrow.nnp = nnp; ret->u.arrow.knp = knp; ret->u.arrow.rhs = rhs; make_explicit(lhs, 0); make_explicit(rhs, 0); check_arrow(ret); return (ret); } static struct lut * nvpair2lut(struct node *np, struct lut *lutp, enum nodetype t) { if (np) { if (np->t == T_NVPAIR) { ASSERTeq(np->u.expr.left->t, T_NAME, ptree_nodetype2str); check_stmt_allowed_properties(t, np, lutp); lutp = tree_s2np_lut_add(lutp, np->u.expr.left->u.name.s, np->u.expr.right); } else if (np->t == T_LIST) { lutp = nvpair2lut(np->u.expr.left, lutp, t); lutp = nvpair2lut(np->u.expr.right, lutp, t); } else outfl(O_DIE, np->file, np->line, "internal error: nvpair2lut type %s", ptree_nodetype2str(np->t)); } return (lutp); } struct lut * tree_s2np_lut_add(struct lut *root, const char *s, struct node *np) { return (lut_add(root, (void *)s, (void *)np, NULL)); } struct node * tree_s2np_lut_lookup(struct lut *root, const char *s) { return (struct node *)lut_lookup(root, (void *)s, NULL); } struct lut * tree_name2np_lut_add(struct lut *root, struct node *namep, struct node *np) { return (lut_add(root, (void *)namep, (void *)np, (lut_cmp)tree_namecmp)); } struct node * tree_name2np_lut_lookup(struct lut *root, struct node *namep) { return (struct node *) lut_lookup(root, (void *)namep, (lut_cmp)tree_namecmp); } struct node * tree_name2np_lut_lookup_name(struct lut *root, struct node *namep) { return (struct node *) lut_lookup_lhs(root, (void *)namep, (lut_cmp)tree_namecmp); } struct lut * tree_event2np_lut_add(struct lut *root, struct node *enp, struct node *np) { return (lut_add(root, (void *)enp, (void *)np, (lut_cmp)tree_eventcmp)); } struct node * tree_event2np_lut_lookup(struct lut *root, struct node *enp) { return ((struct node *) lut_lookup(root, (void *)enp, (lut_cmp)tree_eventcmp)); } struct node * tree_event2np_lut_lookup_event(struct lut *root, struct node *enp) { return ((struct node *) lut_lookup_lhs(root, (void *)enp, (lut_cmp)tree_eventcmp)); } static struct node * dodecl(enum nodetype t, const char *file, int line, struct node *np, struct node *nvpairs, struct lut **lutpp, struct stats *countp, int justpath) { struct node *ret; struct node *decl; /* allocate parse tree node */ ret = newnode(t, file, line); ret->u.stmt.np = np; ret->u.stmt.nvpairs = nvpairs; /* * the global lut pointed to by lutpp (Faults, Defects, Upsets, * Errors, Ereports, Serds, FRUs, or ASRUs) keeps the first decl. * if this isn't the first declr, we merge the * nvpairs into the first decl so we have a * merged table to look up properties from. * if this is the first time we've seen this fault, * we add it to the global lut and start lutp * off with any nvpairs from this declaration statement. */ if (justpath && (decl = tree_name2np_lut_lookup(*lutpp, np)) == NULL) { /* this is the first time name is declared */ stats_counter_bump(countp); *lutpp = tree_name2np_lut_add(*lutpp, np, ret); ret->u.stmt.lutp = nvpair2lut(nvpairs, NULL, t); } else if (!justpath && (decl = tree_event2np_lut_lookup(*lutpp, np)) == NULL) { /* this is the first time event is declared */ stats_counter_bump(countp); *lutpp = tree_event2np_lut_add(*lutpp, np, ret); ret->u.stmt.lutp = nvpair2lut(nvpairs, NULL, t); } else { /* was declared before, just add new nvpairs to its lutp */ decl->u.stmt.lutp = nvpair2lut(nvpairs, decl->u.stmt.lutp, t); } return (ret); } /*ARGSUSED*/ static void update_serd_refstmt(void *lhs, void *rhs, void *arg) { struct node *serd; ASSERT(rhs != NULL); serd = tree_s2np_lut_lookup(((struct node *)rhs)->u.stmt.lutp, L_engine); if (serd == NULL) return; ASSERT(serd->t == T_EVENT); if (arg != NULL && tree_eventcmp(serd, (struct node *)arg) != 0) return; serd = tree_event2np_lut_lookup(SERDs, serd); if (serd != NULL) serd->u.stmt.flags |= STMT_REF; } struct node * tree_decl(enum nodetype t, struct node *np, struct node *nvpairs, const char *file, int line) { struct node *decl; struct node *ret; ASSERT(np != NULL); check_type_iterator(np); switch (t) { case T_EVENT: /* determine the type of event being declared */ ASSERT(np->u.event.ename->t == T_NAME); switch (np->u.event.ename->u.name.t) { case N_FAULT: ret = dodecl(T_FAULT, file, line, np, nvpairs, &Faults, Faultcount, 0); /* increment serd statement reference */ decl = tree_event2np_lut_lookup(Faults, np); update_serd_refstmt(NULL, decl, NULL); break; case N_UPSET: ret = dodecl(T_UPSET, file, line, np, nvpairs, &Upsets, Upsetcount, 0); /* increment serd statement reference */ decl = tree_event2np_lut_lookup(Upsets, np); update_serd_refstmt(NULL, decl, NULL); break; case N_DEFECT: ret = dodecl(T_DEFECT, file, line, np, nvpairs, &Defects, Defectcount, 0); /* increment serd statement reference */ decl = tree_event2np_lut_lookup(Defects, np); update_serd_refstmt(NULL, decl, NULL); break; case N_ERROR: ret = dodecl(T_ERROR, file, line, np, nvpairs, &Errors, Errorcount, 0); break; case N_EREPORT: ret = dodecl(T_EREPORT, file, line, np, nvpairs, &Ereports, Ereportcount, 0); /* * Keep a lut of just the enames, so that the DE * can subscribe to a uniqified list of event * classes. */ Ereportenames = tree_name2np_lut_add(Ereportenames, np->u.event.ename, np); /* * Keep a lut of the enames (event classes) to * silently discard if we can't find a matching * configuration node when an ereport of of a given * class is received. Such events are declaired * with 'discard_if_config_unknown=1'. */ if (tree_s2np_lut_lookup(ret->u.stmt.lutp, L_discard_if_config_unknown)) { Ereportenames_discard = lut_add( Ereportenames_discard, (void *)np->u.event.ename->u.name.s, (void *)np->u.event.ename->u.name.s, NULL); } break; default: outfl(O_ERR, file, line, "tree_decl: internal error, event name type %s", ptree_nametype2str(np->u.event.ename->u.name.t)); } break; case T_ENGINE: /* determine the type of engine being declared */ ASSERT(np->u.event.ename->t == T_NAME); switch (np->u.event.ename->u.name.t) { case N_SERD: ret = dodecl(T_SERD, file, line, np, nvpairs, &SERDs, SERDcount, 0); lut_walk(Upsets, update_serd_refstmt, np); break; case N_STAT: ret = dodecl(T_STAT, file, line, np, nvpairs, &STATs, STATcount, 0); break; default: outfl(O_ERR, file, line, "tree_decl: internal error, engine name type %s", ptree_nametype2str(np->u.event.ename->u.name.t)); } break; case T_ASRU: ret = dodecl(T_ASRU, file, line, np, nvpairs, &ASRUs, ASRUcount, 1); break; case T_FRU: ret = dodecl(T_FRU, file, line, np, nvpairs, &FRUs, FRUcount, 1); break; case T_CONFIG: /* * config statements are different from above: they * are not merged at all (until the configuration cache * code does its own style of merging. and the properties * are a free-for-all -- we don't check for allowed or * required config properties. */ ret = newnode(T_CONFIG, file, line); ret->u.stmt.np = np; ret->u.stmt.nvpairs = nvpairs; ret->u.stmt.lutp = nvpair2lut(nvpairs, NULL, T_CONFIG); if (lut_lookup(Configs, np, (lut_cmp)tree_namecmp) == NULL) stats_counter_bump(Configcount); Configs = lut_add(Configs, (void *)np, (void *)ret, NULL); break; default: out(O_DIE, "tree_decl: internal error, type %s", ptree_nodetype2str(t)); } return (ret); } /* keep backpointers in arrows to the prop they belong to (used for scoping) */ static void set_arrow_prop(struct node *prop, struct node *np) { if (np == NULL) return; if (np->t == T_ARROW) { np->u.arrow.prop = prop; set_arrow_prop(prop, np->u.arrow.lhs); /* * no need to recurse right or handle T_LIST since * T_ARROWs always cascade left and are at the top * of the parse tree. (you can see this in the rule * for "propbody" in escparse.y.) */ } } struct node * tree_stmt(enum nodetype t, struct node *np, const char *file, int line) { struct node *ret = newnode(t, file, line); struct node *pp; int inlist = 0; ret->u.stmt.np = np; switch (t) { case T_PROP: check_proplists(t, np); check_propnames(t, np, 0, 0); check_propscope(np); set_arrow_prop(ret, np); for (pp = Props; pp; pp = pp->u.stmt.next) { if (tree_treecmp(pp, ret, T_NAME, (lut_cmp)tree_namecmp) == 0) { inlist = 1; break; } } if (inlist == 0) stats_counter_bump(Propcount); /* "Props" is a linked list of all prop statements */ if (Lastprops) Lastprops->u.stmt.next = ret; else Props = ret; Lastprops = ret; break; case T_MASK: check_proplists(t, np); check_propnames(t, np, 0, 0); check_propscope(np); set_arrow_prop(ret, np); for (pp = Masks; pp; pp = pp->u.stmt.next) { if (tree_treecmp(pp, ret, T_NAME, (lut_cmp)tree_namecmp) == 0) { inlist = 1; break; } } if (inlist == 0) stats_counter_bump(Maskcount); /* "Masks" is a linked list of all mask statements */ if (Lastmasks) Lastmasks->u.stmt.next = ret; else Masks = ret; Lastmasks = ret; stats_counter_bump(Maskcount); break; default: outfl(O_DIE, np->file, np->line, "tree_stmt: internal error (t %d)", t); } return (ret); } void tree_report() { /* * The only declarations with required properties * currently are faults and serds. Make sure the * the declarations have the required properties. */ lut_walk(Faults, (lut_cb)check_required_props, (void *)T_FAULT); lut_walk(Upsets, (lut_cb)check_required_props, (void *)T_UPSET); lut_walk(Errors, (lut_cb)check_required_props, (void *)T_ERROR); lut_walk(Ereports, (lut_cb)check_required_props, (void *)T_EREPORT); lut_walk(SERDs, (lut_cb)check_required_props, (void *)T_SERD); lut_walk(STATs, (lut_cb)check_required_props, (void *)T_STAT); /* * we do this now rather than while building the parse * tree because it is inconvenient for the user if we * require SERD engines to be declared before used in * an upset "engine" property. */ lut_walk(Faults, (lut_cb)check_refcount, (void *)T_FAULT); lut_walk(Faults, (lut_cb)check_upset_engine, (void *)T_FAULT); lut_walk(Defects, (lut_cb)check_upset_engine, (void *)T_DEFECT); lut_walk(Upsets, (lut_cb)check_upset_engine, (void *)T_UPSET); lut_walk(Upsets, (lut_cb)check_refcount, (void *)T_UPSET); lut_walk(Errors, (lut_cb)check_refcount, (void *)T_ERROR); lut_walk(Ereports, (lut_cb)check_refcount, (void *)T_EREPORT); lut_walk(SERDs, (lut_cb)check_refcount, (void *)T_SERD); /* check for cycles */ lut_walk(Errors, (lut_cb)check_cycle, (void *)0); } /* compare two T_NAMES by only looking at components, not iterators */ int tree_namecmp(struct node *np1, struct node *np2) { ASSERT(np1 != NULL); ASSERT(np2 != NULL); ASSERTinfo(np1->t == T_NAME, ptree_nodetype2str(np1->t)); ASSERTinfo(np2->t == T_NAME, ptree_nodetype2str(np1->t)); while (np1 && np2 && np1->u.name.s == np2->u.name.s) { np1 = np1->u.name.next; np2 = np2->u.name.next; } if (np1 == NULL) if (np2 == NULL) return (0); else return (-1); else if (np2 == NULL) return (1); else return (np2->u.name.s - np1->u.name.s); } int tree_eventcmp(struct node *np1, struct node *np2) { int ret; ASSERT(np1 != NULL); ASSERT(np2 != NULL); ASSERTinfo(np1->t == T_EVENT, ptree_nodetype2str(np1->t)); ASSERTinfo(np2->t == T_EVENT, ptree_nodetype2str(np2->t)); if ((ret = tree_namecmp(np1->u.event.ename, np2->u.event.ename)) == 0) { if (np1->u.event.epname == NULL && np2->u.event.epname == NULL) return (0); else if (np1->u.event.epname == NULL) return (-1); else if (np2->u.event.epname == NULL) return (1); else return tree_namecmp(np1->u.event.epname, np2->u.event.epname); } else return (ret); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * tree.h -- public definitions for tree module * * the parse tree is made up of struct node's. the struct is * a "variant record" with a type, the filename and line number * related to the node, and then type-specific node data. */ #ifndef _ESC_COMMON_TREE_H #define _ESC_COMMON_TREE_H #ifdef __cplusplus extern "C" { #endif struct node { enum nodetype { T_NOTHING, /* used to keep going on error cases */ T_NAME, /* identifiers, sometimes chained */ T_GLOBID, /* globals (e.g. $a) */ T_EVENT, /* class@path{expr} */ T_ENGINE, /* upset threshold engine (e.g. SERD) */ T_ASRU, /* ASRU declaration */ T_FRU, /* FRU declaration */ T_TIMEVAL, /* num w/time suffix (ns internally) */ T_NUM, /* num (ull internally) */ T_QUOTE, /* quoted string */ T_FUNC, /* func(arglist) */ T_NVPAIR, /* name=value pair in decl */ T_ASSIGN, /* assignment statement */ T_CONDIF, /* a and T_CONDELSE in (a ? b : c ) */ T_CONDELSE, /* lists b and c in (a ? b : c ) */ T_NOT, /* boolean ! operator */ T_AND, /* boolean && operator */ T_OR, /* boolean || operator */ T_EQ, /* boolean == operator */ T_NE, /* boolean != operator */ T_SUB, /* integer - operator */ T_ADD, /* integer + operator */ T_MUL, /* integer * operator */ T_DIV, /* integer / operator */ T_MOD, /* integer % operator */ T_LT, /* boolean < operator */ T_LE, /* boolean <= operator */ T_GT, /* boolean > operator */ T_GE, /* boolean >= operator */ T_BITAND, /* bitwise & operator */ T_BITOR, /* bitwise | operator */ T_BITXOR, /* bitwise ^ operator */ T_BITNOT, /* bitwise ~ operator */ T_LSHIFT, /* bitwise << operator */ T_RSHIFT, /* bitwise >> operator */ T_ARROW, /* lhs (N)->(K) rhs */ T_LIST, /* comma-separated list */ T_FAULT, /* fault declaration */ T_UPSET, /* upset declaration */ T_DEFECT, /* defect declaration */ T_ERROR, /* error declaration */ T_EREPORT, /* ereport declaration */ T_SERD, /* SERD engine declaration */ T_STAT, /* STAT engine declaration */ T_PROP, /* prop statement */ T_MASK, /* mask statement */ T_CONFIG /* config statement */ } t:8; /* * regardless of the type of node, filename and line number * information from the original .esc file is tracked here. */ int line:24; const char *file; /* * the variant part of a struct node... */ union { struct { /* * info kept for T_NAME, used in several ways: * * 1 for simple variable names. * example: j * * 2 for event class names, with component * names chained together via the "next" * pointers. * example: fault.fan.broken * * 3 for component pathnames, with component * names chained together via the "next" * pointers and iterators or instance numbers * attached via the "child" pointers. * example: sysboard[0]/cpu[n] * * case 3 is the most interesting. * - if child is set, there's an iterator * - if child is a T_NAME, it is x[j] or x and * iterator type tells you vertical or horizontal * - if child is a T_NUM, it is x[0] or x<0> or * x0 and iterator type tells you which one * - if cp pointer is set, then we recently * matched it to a config cache entry and one * can ignore child for now because it still * represents the *pattern* you're matching. * cp represents what you matched. ptree() * knows that if cp is set, to print that number * instead of following child. * * when T_NAME nodes are chained: * the "last" pointer takes you to the end of the * chain, but only the first component's last pointer * is kept up to date. it is used to determine * where to append newly-created T_NAME nodes (see * tree_name_append()). */ const char *s; /* the name itself */ struct node *child; struct node *next; struct node *last; /* opaque pointer used during config matching */ struct config *cp; /* * note nametype is also declared as a three bit enum * in itree.h, so if this ever needs expanding that * will need changing too. */ enum nametype { N_UNSPEC, N_FAULT, N_UPSET, N_DEFECT, N_ERROR, N_EREPORT, N_SERD, N_STAT } t:3; enum itertype { IT_NONE, IT_VERTICAL, IT_HORIZONTAL, IT_ENAME } it:2; unsigned childgen:1; /* child was auto-generated */ } name; struct { /* * info kept for T_GLOBID */ const char *s; /* the name itself */ } globid; /* * info kept for T_TIMEVAL and T_NUM * * timevals are kept in nanoseconds. */ unsigned long long ull; struct { /* * info kept for T_QUOTE */ const char *s; /* the quoted string */ } quote; struct { /* * info kept for T_FUNC */ const char *s; /* name of function */ struct node *arglist; } func; struct { /* * info kept for T_PROP and T_MASK statements * as well as declarations for: * T_FAULT * T_UPSET * T_DEFECT * T_ERROR * T_EREPORT * T_ASRU * T_FRU * T_CONFIG */ struct node *np; struct node *nvpairs; /* for declarations */ struct lut *lutp; /* for declarations */ struct node *next; /* for Props & Masks lists */ struct node *expr; /* for if statements */ unsigned char flags; /* see STMT_ flags below */ } stmt; /* used for stmt */ struct { /* * info kept for T_EVENT */ struct node *ename; /* event class name */ struct node *epname; /* component path name */ struct node *oldepname; /* unwildcarded path name */ struct node *ewname; /* wildcarded portion */ struct node *eexprlist; /* constraint expression */ struct node *declp; /* event declaration */ } event; struct { /* * info kept for T_ARROW */ struct node *lhs; /* left side of arrow */ struct node *rhs; /* right side of arrow */ struct node *nnp; /* N value */ struct node *knp; /* K value */ struct node *prop; /* arrow is part of this prop */ int needed; struct node *parent; } arrow; struct { /* * info kept for everything else (T_ADD, T_LIST, etc.) */ struct node *left; struct node *right; int temp; } expr; } u; /* * Note to save memory the nodesize() function trims the end of this * structure, so best not to add anything after this point */ }; /* flags we keep with stmts */ #define STMT_REF 0x01 /* declared item is referenced */ #define STMT_CYMARK 0x02 /* declared item is marked for cycle check */ #define STMT_CYCLE 0x04 /* cycle detected and already reported */ #define TIMEVAL_EVENTUALLY (1000000000ULL*60*60*24*365*100) /* 100 years */ void tree_init(void); void tree_fini(void); struct node *newnode(enum nodetype t, const char *file, int line); void tree_free(struct node *root); struct node *tree_root(struct node *np); struct node *tree_nothing(void); struct node *tree_expr(enum nodetype t, struct node *left, struct node *right); struct node *tree_event(struct node *ename, struct node *epname, struct node *eexprlist); struct node *tree_if(struct node *expr, struct node *stmts, const char *file, int line); struct node *tree_name(const char *s, enum itertype it, const char *file, int line); struct node *tree_iname(const char *s, const char *file, int line); struct node *tree_globid(const char *s, const char *file, int line); struct node *tree_name_append(struct node *np1, struct node *np2); struct node *tree_name_repairdash(struct node *np1, const char *s); struct node *tree_name_repairdash2(const char *s, struct node *np1); struct node *tree_name_iterator(struct node *np1, struct node *np2); struct node *tree_timeval(const char *s, const char *suffix, const char *file, int line); struct node *tree_num(const char *s, const char *file, int line); struct node *tree_quote(const char *s, const char *file, int line); struct node *tree_func(const char *s, struct node *np, const char *file, int line); struct node *tree_pname(struct node *np); struct node *tree_arrow(struct node *lhs, struct node *nnp, struct node *knp, struct node *rhs); struct lut *tree_s2np_lut_add(struct lut *root, const char *s, struct node *np); struct node *tree_s2np_lut_lookup(struct lut *root, const char *s); struct lut *tree_name2np_lut_add(struct lut *root, struct node *namep, struct node *np); struct node *tree_name2np_lut_lookup(struct lut *root, struct node *namep); struct node *tree_name2np_lut_lookup_name(struct lut *root, struct node *namep); struct lut *tree_event2np_lut_add(struct lut *root, struct node *enp, struct node *np); struct node *tree_event2np_lut_lookup(struct lut *root, struct node *enp); struct node *tree_event2np_lut_lookup_event(struct lut *root, struct node *enp); struct node *tree_decl(enum nodetype t, struct node *enp, struct node *nvpairs, const char *file, int line); struct node *tree_stmt(enum nodetype t, struct node *np, const char *file, int line); void tree_report(); int tree_namecmp(struct node *np1, struct node *np2); int tree_eventcmp(struct node *np1, struct node *np2); extern struct lut *Faults; extern struct lut *Upsets; extern struct lut *Defects; extern struct lut *Errors; extern struct lut *Ereports; extern struct lut *Ereportenames; extern struct lut *Ereportenames_discard; extern struct lut *SERDs; extern struct lut *STATs; extern struct lut *ASRUs; extern struct lut *FRUs; extern struct lut *Configs; extern struct node *Props; extern struct node *Lastprops; extern struct node *Masks; extern struct node *Lastmasks; extern struct node *Problems; extern struct node *Lastproblems; #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_TREE_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * verion.h -- define current version numbers for eversholt * */ #ifndef _ESC_COMMON_VERSION_H #define _ESC_COMMON_VERSION_H #ifdef __cplusplus extern "C" { #endif #define VERSION_MAJOR 1 #define VERSION_MINOR 16 #ifdef __cplusplus } #endif #endif /* _ESC_COMMON_VERSION_H */