/* IBM_PROLOG_BEGIN_TAG */ /* This is an automatically generated prolog. */ /* */ /* perf720 src/perf/perfagent/usr/samples/perfagent/server/lfiltd.c 1.8 */ /* */ /* */ /* */ /* OBJECT CODE ONLY SOURCE MATERIALS */ /* */ /* COPYRIGHT International Business Machines Corp. 1992,1993 */ /* All Rights Reserved */ /* */ /* The source code for this program is not published or otherwise */ /* divested of its trade secrets, irrespective of what has been */ /* deposited with the U.S. Copyright Office. */ /* */ /* IBM_PROLOG_END_TAG */ static char *Sccs_id = "@(#)23 1.8 src/perf/perfagent/usr/samples/perfagent/server/lfiltd.c, perfagent, perf720 12/3/96 06:50:21"; /* * COMPONENT_NAME: (PERFAGENT) - Performance Agent * * FUNCTIONS: op_mult(), op_div(), op_rem(), op_add(), op_sub(), * op_top(), op_bot(), op_cnt(), log_check(), doexit(), must_exit(), * free_Res(), free_all(), doerror(), input(), unput(), yywrap(), * add_entry(), strccvt(), add_const(), add_var(), put_oper(), * add_oper(), step_up(), step_down(), shine_up(), add_wild(), * dump_it(), verify_name(), check_names(), add_to_set(), do_calc(), * feeding(), main() * * ORIGINS: 30 * * (C) COPYRIGHT International Business Machines Corp. 1992, 1993 * All Rights Reserved * Licensed Materials - Property of IBM * * US Government Users Restricted Rights - Use, duplication or * disclosure restricted by GSA ADP Schedule Contract with IBM Corp. * * NOTICE TO USERS OF THE SOURCE CODE EXAMPLES THE SOURCE CODE EXAMPLES PROVIDED BY IBM ARE ONLY INTENDED TO ASSIST IN THE DEVELOPMENT OF A WORKING SOFTWARE PROGRAM. THE SOURCE CODE EXAMPLES DO NOT FUNCTION AS WRITTEN: ADDITIONAL CODE IS REQUIRED. IN ADDITION, THE SOURCE CODE EXAMPLES MAY NOT COMPILE AND/OR BIND SUCCESSFULLY AS WRITTEN. INTERNATIONAL BUSINESS MACHINES CORPORATION PROVIDES THE SOURCE CODE EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS, "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOURCE CODE EXAMPLES, BOTH INDIVIDUALLY AND AS ONE OR MORE GROUPS, IS WITH YOU. SHOULD ANY PART OF THE SOURCE CODE EXAMPLES PROVE DEFECTIVE, YOU (AND NOT IBM OR AN AUTHORIZED RISC System/6000* WORKSTATION DEALER) ASSUME THE ENTIRE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. IBM does not warrant that the contents of the source code examples, whether individually or as one or more groups, will meet your requirements or that the source code examples are error-free. IBM may make improvements and/or changes in the source code examples at any time. Changes may be made periodically to the information in the source code examples; these changes may be reported, for the sample device drivers included herein, in new editions of the examples. References in the source code examples to IBM products, programs, or services do not imply that IBM intends to make these available in all countries in which IBM operates. Any reference to an IBM licensed program in the source code examples is not intended to state or imply that only IBM's licensed program may be used. Any functionally equivalent program may be used. * RISC System/6000 is a trademark of International Business Machines Corporation. */ /* This sample program demonstrates how a program using the Spmi API can, at the same time, read data values from the Spmi and add its own data values to the default set of statistics defined by Spmi. The program used lex to define the semantics of a configuration file, which by default is in /usr/samples/perfagent/server/lfilter.cf. The configuration file defines new statistics from existing Spmi statistics, using the following general format: target = expression description target: Unqualified name of non-existing variable. Must start with alpha and contain only alpha-numeric characters and percent sign. expression: {variable|wildcard|const} operator {variable|wildcard|const} ... variable: Fully qualified xmperf variable name with slashes replaced by under- scores; valid names have at least one underscore. The first name compo- nent must start with an alpha character, subsequent ones may also begin with a percent sign. All must contain only alpha-numeric characters and percent sign. The referenced variable must already exist (can NOT be defined in this configuration file). wildcard: Fully qualified xmperf variable name with slashes replaced by under- scores; valid names have at least one underscore. The first name compo- nent must start with an alpha character, subsequent ones may also begin with a percent sign. All must contain only alpha-numeric characters and percent sign or must be a wildcard. The wildcard character must appear in place of a context name, must only appear once, and must be one of the characters '+', '*', '#', '>', '<'. operator: One of {*, /, %, +, -} const: [[digits].digits] description: Text describing the defined target variable. Must be enclosed in double quotes. The use of "wildcards is a way of referring to multiple instances of a given &stat. with one name but, more important, it makes your expression independent of the actual configuration of the system it is used on. For example, the expression: allreads = Disk_+_rblk could evaluate to different expressions on different machines, such as: allreads = ((Disk/cd0/rblk + Disk/hdisk1/rblk) + Disk/hdisk0/rblk) allreads = Disk/hdisk0/rblk Examples of defining new statistics: running = CPU_cpu0_kern + CPU_cpu0_user "CPU running" notbusy = CPU_cpu0_wait + CPU_cpu0_idle "CPU not running" packsize = NetIf_tr0_ooctet / NetIf_tr0_opacket "Average packet size" localpct = (NetIf_lo0_ipacket + NetIf_lo0_opacket) * 100 \ / (NetIf_+_ipacket + NetIf_+_opacket) \ "Percent of network packets on loopback interface" These statistics would show up as: DDS/IBM/Custom/running DDS/IBM/Custom/notbusy DDS/IBM/Custom/packsize DDS/IBM/Custom/localpct */ #include #include #include #include #include #include #include #ifdef _AIX #include #endif #include #include "lfiltd.h" #if sun #include #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #ifdef _SOLARIS int yylex(void); #endif /* _SOLARIS */ extern char yytext[]; extern char SpmiErrmsg[]; extern int SpmiErrno; int yychar; long pos, cur = 0; char *dat; parse_state state; var_state vstate; int brc1 = 0, brc2 = 0; int curcount = 0; int totcount = 0; int arraycount; int trace = 0; int loggings = 0; FILE *lf; struct itimerval t_value; /* time to set */ struct itimerval o_value; /* time to expiration */ char log1[128] = lfilter_log; char log2[128] = lfilter_log; char *log; struct ResStr *tab[20] = {NULL}; /* level table used to build the */ /* multi-level network of nodes */ /* for a value. */ struct LVarStr *vptr = NULL; /* anchor of base nodes, each of */ /* which defines a new value */ struct LVarStr *lvptr = NULL; boolean name_error = FALSE; struct SpmiStatSet *curssp = NULL; int interval = LINTERVAL; struct dat { float val[1]; }; static SpmiShare *dataarea = NULL; /* Shared memory pointer */ static struct dat *d = NULL; /* Pointer to stats data area */ struct SpmiRawStat *FiltStats; static cx_create FiltContext[] = { {"DDS/IBM", "IBM-defined Dynamic Data Suppliers", 2, 0, NULL, 0, NULL, 0, NULL, SiNoInst}, {"DDS/IBM/Custom", "Custom values defined by \"lfiltd\"", 180, 0, NULL, 0, NULL, 0, NULL, SiNoInst}, }; /* Define function called strmake() to be used when porting to systems that do not know the strdup() function. */ #ifdef _AIX #define strmake strdup #else #ifdef _NO_PROTO char *strmake(str) char *str; #else char *strmake(char *str) #endif { char *p; p = (char *)malloc(strlen(str)+1); strcpy(p, str); return (p); } #endif /* Define functions to perform basic arithmetic on float values. These functions are invoked as required from the structures that are used to define the new statistics values from arithmetic expressions. All functions are defined for function prototyping and old-style function declarations. */ #ifdef _NO_PROTO float op_mult(a, b) float a; float b; { return(a * b); } float op_div(a, b) float a; float b; { if (b) return(a / b); return(0.0); } float op_rem(a, b) float a; float b; { long aa = a, bb = b; if (bb) return(aa % bb); return(0.0); } float op_add(a, b) float a; float b; { return(a + b); } float op_sub(a, b) float a; float b; { return(a - b); } float op_top(a, b) float a; float b; { if (a > b) return(a); return(b); } float op_bot(a, b) float a; float b; { if (a < b) return(a); return(b); } float op_cnt(a, b) float a; float b; { return(++a); } #else float op_mult(float a, float b) { return(a * b); } float op_div(float a, float b) { if (b) return(a / b); return(0.0); } float op_rem(float a, float b) { long aa = a, bb = b; if (bb) return(aa % bb); return(0.0); } float op_add(float a, float b) { return(a + b); } float op_sub(float a, float b) { return(a - b); } float op_top(float a, float b) { if (a > b) return(a); return(b); } float op_bot(float a, float b) { if (a < b) return(a); return(b); } float op_cnt(float a, float b) { return(++a); } #endif /* This function is used to flush the log file. The log file is always used and will by default flip-flop between /tmp/lfilter.log1 and /tmp/lfilter.log2. */ void log_check() { long pos; pos = ftell(lf); fclose(lf); if (pos >= MAX_FFILE_SIZE) { if (!(strcmp(log, log1))) log = log2; else log = log1; lf = fopen(log, "w"); } else lf = fopen(log, "a"); } /* This function is called to exit the program. It does the following: 1. Calls SpmiExit to release all references to shared memory and to undefine its statistics. 2. Writes the date and time of the last shared memory update to the log. 3. Closes the log file and exits. */ #ifdef _NO_PROTO void doexit(rc) int rc; #else void doexit(int rc) #endif { char t1[40], *t; struct timeval tid; gettimeofday(&tid, NULL); t = (char *)ctime((time_t *)&tid.tv_sec); strcpy(t1, t); if (dataarea) { t = (char *)ctime((time_t *)&tid.tv_sec); fprintf(lf, "Last shared memory update: %s\n", t); } SpmiExit(); fprintf(lf, "Exiting at: %s\n", t1); fprintf(stderr, "lfiltd: Exiting at: %s\n", t1); fclose(lf); exit(rc); } /* Function that is invoked in case the program receives a signal. It writes a message to the log and then calls doexit() to exit. */ #ifdef _NO_PROTO void must_exit(sig) int sig; #else void must_exit(int sig) #endif { fprintf(lf, "Signal %d received\n", sig); fprintf(stderr, "Signal %d received\n", sig); doexit(-9); } /* Function to delete a node in the network that defines a new value */ #ifdef _NO_PROTO void free_Res(rp) struct ResStr *rp; #else void free_Res(struct ResStr *rp) #endif { if (rp->var1) free(rp->var1); if (rp->var2) free(rp->var2); free(rp); } /* Function to free all nodes from and including a the node given by the argument. */ #ifdef _NO_PROTO void free_all(rp) struct ResStr *rp; #else void free_all(struct ResStr *rp) #endif { if (rp->type1 == temp) free_all((struct ResStr *)rp->source1); if (rp->type2 == temp) free_all((struct ResStr *)rp->source2); free_Res(rp); } /* Function that is called when encountering errors during parsing of the configuration file. It outputs a message to the log and then cleanc out the node(s) as required and resets the state switches of the parser. */ #ifdef _NO_PROTO void doerror(text) char *text; #else void doerror(char *text) #endif { struct LVarStr *tvptr; if (dat[cur-1] != '\n') { fprintf(lf, "%s \"%s\"\n", text, yytext); while((dat[cur]) && (cur < pos)) { if ((dat[cur] == '\\') && (dat[cur+1] == '\n')) cur += 2; else cur++; if (dat[cur] == '\n') { cur++; break; } } } else fprintf(lf, "%s\n", text); if (lvptr) { tvptr = lvptr; lvptr = tvptr->prev; if (lvptr) lvptr->next = NULL; else vptr = NULL; if (tvptr->newvar) free(tvptr->newvar); free_all(tvptr->rsp); free(tvptr); } state = nostate; vstate = novar; brc1 = brc2 = 0; } /* Function required by lex; used to pass one character at a time to the code generated by lex. */ int input() { if (cur >= pos) return(0); return(dat[cur++]); } /* Function required by lex; used to return one character at a time from the code generated by lex. */ #ifdef _NO_PROTO int unput(c) int c; #else int unput(int c) #endif { if (cur) dat[--cur] = c; return(0); } /* Null function required by lex. */ int yywrap() { return(1); } /* Function to add the base node of a new variable to the list of defined variables anchored in vptr. */ int add_entry() { struct LVarStr *tvptr; if (!(tvptr = (struct LVarStr *)calloc(1, sizeof(struct LVarStr)))) { fprintf(lf, "Out of memory -- exiting\n"); return(-1); } if (!vptr) lvptr = vptr = tvptr; else { lvptr->next = tvptr; tvptr->prev = lvptr; lvptr = tvptr; } if (!(lvptr->rsp = (struct ResStr *)calloc(1, sizeof(struct ResStr)))) { fprintf(lf, "Out of memory -- exiting\n"); return(-1); } tab[0] = lvptr->rsp; return(0); } /* Function to convert underscore characters in variable names from the configuration file to slashes. The underscore character is used in place of slashes when naming variables to avoid clashes with the sign for division, "/". */ #ifdef _NO_PROTO char *strccvt(s) u_char *s; #else char *strccvt(char *s) #endif { char localc[128], *t = localc; while(*t = *s++) { if (*t == '_') *t++ = '/'; else t++; } t = (char *)strmake(localc); if (!t) fprintf(lf, "Out of memory -- exiting\n"); return(t); } /* Function to add a constant value as either the first or second operand in a given node. The node is identified by an index into the "tab" table where a node is identified. The index is in the global variable bcr2. */ #ifdef _NO_PROTO int add_const(value) float value; #else int add_const(float value) #endif { float localv = value; if (tab[brc2]->type1) { /* First operand has been added, now see how to handle second */ if (tab[brc2]->type2) { /* Second is already added, so this is an error */ fprintf(lf, "Invalid call to add_const\n"); return(-1); } else { /* Second operand wasn't here so go add it */ memcpy((char *)&tab[brc2]->source2, &localv, sizeof(localv)); tab[brc2]->type2 = constant; } } else { /* We have to add the first operand here */ memcpy((char *)&tab[brc2]->source1, &localv, sizeof(localv)); tab[brc2]->type1 = constant; } return(0); } /* Function to add a statistic name as either the first or second operand in a given node. The node is identified by an index into the "tab" table where a node is identified. The index is in the global variable bcr2. */ #ifdef _NO_PROTO int add_var(name) char *name; #else int add_var(char *name) #endif { if (tab[brc2]->type1) { /* First operand has been added, now see how to handle second */ if (!tab[brc2]->type2) { /* Second operand wasn't here so go add it */ if (!(tab[brc2]->var2 = (char *)strmake(name))) return(-1); tab[brc2]->type2 = variable; } else { /* Second is already added, so this is an error */ fprintf(lf, "Invalid call to add_var\n"); return(-1); } } else { /* We have to add the first operand here */ if (!(tab[brc2]->var1 = (char *)strmake(name))) return(-1); tab[brc2]->type1 = variable; } return(0); } /* Function to insert the operator in a node. */ #ifdef _NO_PROTO void put_oper(rp, opr) struct ResStr *rp; int opr; #else void put_oper(struct ResStr *rp, int opr) #endif { switch(opr) { case 'T': rp->op = op_top; rp->prec = 2; break; case 'B': rp->op = op_bot; rp->prec = 2; break; case '#': rp->op = op_cnt; rp->prec = 2; break; case '*': rp->op = op_mult; rp->prec = 4; break; case '/': rp->op = op_div; rp->prec = 4; break; case '%': rp->op = op_rem; rp->prec = 4; break; case '+': rp->op = op_add; rp->prec = 6; break; case '-': rp->op = op_sub; rp->prec = 6; break; } } /* Function to add an operator to a given node. The node is identified by an index into the "tab" table where a node is identified. The index is in the global variable bcr2. If two operands are already present, a new level is created and the precedence level of the new and previous operator is used to place the nodes in the network so that the expression is evaluated in order of operator precedence. */ #ifdef _NO_PROTO int add_oper(opr) int opr; #else int add_oper(int opr) #endif { if (tab[brc2]->type2) { /* Oops! two operands are already present so we must split the table entry and go to the next level */ struct ResStr *rp1 = tab[brc2], *rp2;; brc2++; if (!(tab[brc2] = (struct ResStr *)calloc(1, sizeof(struct ResStr)))) { fprintf(lf, "Out of memory -- exiting\n"); doexit(9); } rp2 = tab[brc2]; put_oper(rp2, opr); if ((long)rp2->prec < (long)rp1->prec) { rp2->type1 = rp1->type2; rp2->var1 = rp1->var2; rp2->source1 = rp1->source2; rp1->var2 = NULL; rp1->type2 = temp; rp1->source2 = rp2; } else { *rp2 = *rp1; /* structure copy */ put_oper(rp1, opr); rp1->var1 = NULL; rp1->type1 = temp; rp1->source1 = rp2; rp1->var2 = NULL; rp1->type2 = notype; rp1->source2 = NULL; brc2--; } } else { put_oper(tab[brc2], opr); } return(0); } /* Function used to increase the node level by one. The function is called when we see a left parentheses or when evaluating wildcards (which implies parentheses around the evaluated wildcard). */ void step_up() { if (!(tab[brc2 + 1] = (struct ResStr *)calloc(1, sizeof(struct ResStr)))) { fprintf(lf, "Out of memory -- exiting\n"); doexit(9); } if (!tab[brc2]->type1) { tab[brc2]->var1 = NULL; tab[brc2]->type1 = constant; tab[brc2]->source1 = NULL; tab[brc2]->op = op_add; } brc1++; brc2++; } /* Function used to decrease the node level by one. The function is called when we see a right parentheses or when we finished the evaluating of wildcards (which implies parentheses around the evaluated wildcard). */ #ifdef _NO_PROTO void step_down(k) int k; #else void step_down(int k) #endif { int i; struct ResStr *rp1, *rp2;; while(brc2 > brc1) { rp2 = tab[brc2]; brc2--; rp1 = tab[brc2]; rp1->var2 = NULL; rp1->type2 = temp; rp1->source2 = rp2; } while(brc1 > k) { rp2 = tab[brc2]; brc2--; rp1 = tab[brc2]; rp1->var2 = NULL; rp1->type2 = temp; rp1->source2 = rp2; brc1--; } } /* This function is called to finish processing of a network created to define a new variable. It will get rid of any half-finished nodes by pushing their contents down into lower levels, thereby reducing the number of levels used to define the value. */ #ifdef _NO_PROTO int shine_up(prp) struct ResStr **prp; #else int shine_up(struct ResStr **prp) #endif { struct ResStr *rp = *prp; float tmp1; memcpy((char *)&tmp1, (char *)&rp->source1, sizeof(tmp1)); if ((rp->type1 == constant) && (rp->op == op_add) && (tmp1 == 0.0)) { if (!rp->type2) { doerror("No expression found"); return(-1); } if (rp->type2 == temp) { *prp = (struct ResStr *)rp->source2; free_Res(rp); rp = *prp; shine_up(prp); return(0); } } if (rp->type1 == temp) shine_up((struct ResStr **)&rp->source1); if (rp->type2 == temp) shine_up((struct ResStr **)&rp->source2); return(0); } /* This function adds one or more instances of a wildcard to the network defining a new value. This is done by traversing the Spmi data hierarchy and making a linked list of all instances. The functions add_var() and add_oper() are then called to create the nodes required to evaluate the wildcard expression. */ #ifdef _NO_PROTO int add_wild(opr) int opr; #else int add_wild(int opr) #endif { struct wild { char path[128]; struct wild *next; }; struct wild *wa = NULL, *w; char path[128]; char *t, *s; SpmiCxHdl cxh; struct SpmiCx *cxp; struct SpmiCxLink *cxlink, *ocxlink; int cc = 0; int rc = 0; float k = 0; if (!(s = strccvt(yytext))) return(-1); if (!(t = (char *)strchr(s, opr))) { if (opr == 'T') t = (char *)strchr(s, '>'); else if (opr == 'B') t = (char *)strchr(s, '<'); } *(t-1) = '\0'; t++; strcpy(path, s); cxh = SpmiPathGetCx((char *)path, NULL); if (!cxh) { if (!(cxh = SpmiPathGetCx((char *)path, NULL))) { if (strlen(SpmiErrmsg)) fprintf(lf, "%s\n", SpmiErrmsg); fprintf(lf, "SpmiPathGetCx can\'t find context for \"%s\"\n", path); return(-1); } } if (!(cxlink = SpmiFirstCx(cxh))) cxlink = SpmiFirstCx(cxh); while (cxlink) { if (!(w = (struct wild *)calloc(1, sizeof(struct wild)))) { fprintf(lf, "Out of memory -- exiting\n"); return(-1); } if (!(cxp = SpmiGetCx(cxlink->context))) break; if (opr == '#') k++; else { w->next = wa; wa = w; strcpy(w->path, s); strcat(w->path, "/"); strcat(w->path, cxp->name); strcat(w->path, t); cc++; } ocxlink = cxlink; if (!(cxlink = SpmiNextCx(cxlink))) cxlink = SpmiNextCx(ocxlink); } if (opr == '#') { if (add_const(k)) rc = -1; free(s); return(rc); } /* we now have a table of all instances with full path names; we go through the table and add variables and operators as required by using the functions add_var() and add_oper() */ w = wa; while(w) { cc--; if (add_var((char *)w->path)) rc = -1; else { if (cc) if (add_oper(opr)) rc = -1; } wa = w; w = w->next; free(wa); } free(s); return(rc); } /* Function used to dump the network of nodes defining a new variable. It is invoked for each defined new value if the command line argument "-p" is set to 5 or above. The output goes to the log file and can be used to validate the evaluation of expressions because it inserts parentheses to illustrate the exact evaluation sequence. */ #ifdef _NO_PROTO void dump_it(rp, level) struct ResStr *rp; int level; #else void dump_it(struct ResStr *rp, int level) #endif { float tmp; if ((rp) && (!lvptr->dumped)) { if (!level) { if (lvptr->newvar) { if (lvptr->descr) fprintf(lf, "\n%s \"%s\" = ", lvptr->newvar, lvptr->descr); else fprintf(lf, "\n%s = ", lvptr->newvar); } else fprintf(lf, "%08x: = ", (long)rp); } if (rp->type1 == temp) { fprintf(lf, "("); dump_it((struct ResStr *)rp->source1, level + 1); fprintf(lf, ")"); } else if (rp->type1 == variable) fprintf(lf, "%s", rp->var1); else if (rp->type1 == constant) { memcpy((char *)&tmp, (char *)&rp->source1, sizeof(tmp)); fprintf(lf, "%f", tmp); } else fprintf(lf, "******** "); if (rp->op == op_mult) fprintf(lf, " * "); else if (rp->op == op_div) fprintf(lf, " / "); else if (rp->op == op_rem) fprintf(lf, " % "); else if (rp->op == op_add) fprintf(lf, " + "); else if (rp->op == op_sub) fprintf(lf, " - "); else if (rp->op == op_top) fprintf(lf, " MAX "); else if (rp->op == op_bot) fprintf(lf, " MIN "); else if (rp->op == op_cnt) fprintf(lf, " # "); else { if (!level) { fprintf(lf, "\n"); return; } fprintf(lf, " ??? "); } if (rp->type2 == temp) { fprintf(lf, "("); dump_it((struct ResStr *)rp->source2, level + 1); fprintf(lf, ")"); } else if (rp->type2 == variable) fprintf(lf, "%s", rp->var2); else if (rp->type2 == constant) { memcpy((char *)&tmp, (char *)&rp->source2, sizeof(tmp)); fprintf(lf, "%f", tmp); } else fprintf(lf, "******** "); if (!level) fprintf(lf, "\n\n"); if (!level) lvptr->dumped = TRUE; } } /* Function used to verify whether a name used in an expression exists in the Spmi data hierarchy. The result of the test is placed in the global variable "name_error". It is called only from check_names(). */ #ifdef _NO_PROTO void verify_name(s) char *s; #else void verify_name(char *s) #endif { int i; char tmp[256], *t, er[256], *navn; SpmiCxHdl cxh; struct SpmiStat *stp; struct SpmiStatLink *statlink, *ostatlink; strcpy(tmp, s); i = strlen(tmp) - 1; while(tmp[i] != '/') i--; tmp[i] = '\0'; cxh = SpmiPathGetCx(tmp, NULL); if (!cxh) { if (!(cxh = SpmiPathGetCx(tmp, NULL))) { tmp[i] = '/'; if (strlen(SpmiErrmsg)) fprintf(lf, "%s\n", SpmiErrmsg); fprintf(lf, "SpmiPathGetCx cannot find context for \"%s\"\n", tmp); name_error = TRUE; return; } } tmp[i] = '/'; if (((struct SpmiCx *)cxh)->inst_freq >= SiContInst) { SpmiInstantiate(cxh); } if (!(statlink = SpmiFirstStat(cxh))) statlink = SpmiFirstStat(cxh); while (statlink) { if (!(stp = SpmiGetStat(statlink->stat))) break; navn = stp->name; if (!strcmp(navn, tmp+i+1)) return; ostatlink = statlink; if (!(statlink = SpmiNextStat(statlink))) statlink = SpmiNextStat(ostatlink); } fprintf(lf, "Value name \"%s\" not found\n", tmp); name_error = TRUE; } /* Function used to check all variables in an expression. It uses the verify_name() function to look-up the names with Spmi and proceeds through the network of nodes that define a new variable by recursively invoking itself. */ #ifdef _NO_PROTO int check_names(rp, i) struct ResStr *rp; int i; #else int check_names(struct ResStr *rp, int i) #endif { int n = i; if (rp->type1 == variable) { verify_name(rp->var1); n++; } if (rp->type2 == variable) { verify_name(rp->var2); n++; } if (rp->type1 == temp) n = check_names((struct ResStr *)rp->source1, n); if (rp->type2 == temp) n = check_names((struct ResStr *)rp->source2, n); return(n); } /* This function invokes itself recursively to traverse the network defining a new value and add all the Spmi statistics to a StatSet so that the data values required to evaluate the defined new value will be extracted from the Spmi each time an SpmiGetStatSet() call is issued. */ #ifdef _NO_PROTO void add_to_set(rp) struct ResStr *rp; #else void add_to_set(struct ResStr *rp) #endif { char tmp[256]; if (rp->type1 == variable) { strcpy(tmp, rp->var1); if (!(rp->source1 = SpmiPathAddSetStat(curssp, tmp, NULL))) { if (!(rp->source1 = SpmiPathAddSetStat(curssp, tmp, NULL))) { name_error = TRUE; return; } } curcount++; /* free(rp->var1); rp->var1 = NULL; */ } if (rp->type2 == variable) { strcpy(tmp, rp->var2); if (!(rp->source2 = SpmiPathAddSetStat(curssp, tmp, NULL))) { if (!(rp->source2 = SpmiPathAddSetStat(curssp, tmp, NULL))) { name_error = TRUE; return; } } curcount++; /* free(rp->var2); rp->var2 = NULL; */ } if (rp->type1 == temp) add_to_set((struct ResStr *)rp->source1); if (rp->type2 == temp) add_to_set((struct ResStr *)rp->source2); } /* Whenever a node in the network must be evaluated, this function is called to do the evaluation. Whenever an operand is of type "temp", the function invokes itself recursively, thereby traversion the entire network of a new variable. Evaluation takes place in a sequence that ensures that operator precedence is respected. */ #ifdef _NO_PROTO float do_calc(rp) struct ResStr *rp; #else float do_calc(struct ResStr *rp) #endif { float res, res1, res2; boolean pr = FALSE; switch(rp->type1) { case variable: res1 = SpmiGetValue(curssp, (struct SpmiStatVals *)rp->source1); if (res1 < -1.1) { if (++rp->loggings < 3) { pr = TRUE; fprintf(lf, "Negative 1st value:"); loggings++; if (rp->var1) fprintf(lf, " %s=%f, ", rp->var1, res1); else fprintf(lf, " ...=%f, ", res1); } res1 = 0.0; } break; case constant: memcpy((char *)&res1, (char *)&rp->source1, sizeof(res1)); break; case temp: res1 = do_calc((struct ResStr *)rp->source1); break; default: if (++rp->loggings < 3) { fprintf(lf, "Missing type....\n"); loggings++; } return(0.0); } if (!rp->op) return(res1); switch(rp->type2) { case variable: res2 = SpmiGetValue(curssp, (struct SpmiStatVals *)rp->source2); if (res2 < -1.1) { if (++rp->loggings < 3) { pr = TRUE; fprintf(lf, "Negative 2nd value:"); loggings++; if (rp->var2) fprintf(lf, "%s=%f, ", rp->var2, res2); else fprintf(lf, "...=%f, ", res2); } res2 = 0.0; } break; case constant: memcpy((char *)&res2, (char *)&rp->source2, sizeof(res1)); break; case temp: res2 = do_calc((struct ResStr *)rp->source2); break; default: if (++rp->loggings < 3) { fprintf(lf, "Missing type....\n"); loggings++; } return(0.0); } if (trace == 9) { res = rp->op(res1, res2); if (rp->var1) fprintf(lf, " %s=%f, ", rp->var1, res1); else fprintf(lf, " ...=%f, ", res1); if (rp->var2) fprintf(lf, "%s=%f, ", rp->var2, res2); else fprintf(lf, "...=%f, ", res2); fprintf(lf, " = %f\n", res); loggings++; return(res); } else { if (pr) fprintf(lf, "\n"); return(rp->op(res1, res2)); } } /* This function is called by an interval timer and is used to issue an SpmiGetStatSet() call to extract the values required to evaluate the defined new values. It invokes do_calc() for each of the new values defined. */ #ifdef _NO_PROTO void feeding() #else void feeding() #endif { int i; float f; long v; static int k = 0, iv = 0; struct LVarStr *tvptr; struct SpmiStatSet *thisssp = curssp; if (dataarea->SiShGoAway) { fprintf(lf, "Server requested that we die\n"); fprintf(stderr, "Server requested that we die\n"); doexit(0); } signal(SIGALRM, SIG_IGN); if (loggings > 20) { log_check(); loggings = 0; } gettimeofday(&dataarea->SiShT, NULL); tvptr = vptr; if (SpmiGetStatSet(curssp, TRUE)) { if (trace) { fprintf(lf, "%s\n", SpmiErrmsg); } if (SpmiErrno == SiLocked) { #ifdef _NO_PROTO signal(SIGALRM, (void(*)())feeding); #else signal(SIGALRM, (void(*)(int))feeding); #endif /* _NO_PROTO */ return; } fprintf(stderr, "%s\n", SpmiErrmsg); doexit(0); } while(tvptr) { if (thisssp != tvptr->ssp) { SpmiGetStatSet(thisssp, TRUE); thisssp = tvptr->ssp; } if (tvptr->type == newvalue) d->val[tvptr->ix] = do_calc(tvptr->rsp); if (trace == 9) { fprintf(lf, "%s = %f\n", tvptr->newvar, d->val[tvptr->ix]); loggings++; } tvptr = tvptr->next; } #ifdef _NO_PROTO signal(SIGALRM, (void(*)())feeding); #else signal(SIGALRM, (void(*)(int))feeding); #endif /* _NO_PROTO */ } #ifdef _NO_PROTO int main(argc, argv) int argc; char **argv; #else int main(int argc, char **argv) #endif { struct ResStr *base; struct ResStr *curr; uid_t uid; int ff, c; int p, i, n; float wrkf; int wrki; int newcount; char fil[128]; char *lp, *r, *s; char *t; extern char *optarg; struct timeval tid; boolean other; struct LVarStr *tvptr; strcpy(fil, lfilter_inp); /* Parsing of command line and reading of configuration file into memory */ while ((c = getopt(argc, argv, "hf:l:p:i:")) != EOF) { switch (c) { case 'f': if (strlen(optarg) < 2) break; strcpy(fil, optarg); break; case 'l': if (strlen(optarg) < 2) break; strcpy(log1, optarg); break; case 'p': trace = atoi(optarg); if (trace > 9) trace = 9; if (trace < 0) trace = 0; break; case 'i': i = atoi(optarg); if (i > 300) i = interval; if (i < 1) i = interval; interval = i * 1000; break; default: printf("usage:\t%s\t-f filter file name\n", argv[0]); printf("\t\t-l log file name\n"); printf("\t\t-p detail level of logging (0-9)\n"); printf("\t\t-i update interval in seconds (1-300)\n"); if (c == 'h') exit(0); printf("\t\t...continuing anyway...\n\n"); break; } } uid = geteuid(); if (uid) { fprintf(stderr,"%s: Root authority req'd, effective UID is %d\n", argv[0], uid); exit(9); } strcat(log1, "1"); strcat(log2, "2"); log = log1; lf = fopen(log, "a"); log_check(); gettimeofday(&tid, NULL); s = (char *)ctime((time_t *)&tid.tv_sec); fprintf(lf, "\n============= %s started with UID= %d at: %s =============\n", uid, s, argv[0]); if ((ff = open(fil, O_RDONLY, 0)) <= 0) { fprintf(lf, "Can\'t open filter file \"%s\"\n", fil); fprintf(stderr, "%s: Can\'t open filter file \"%s\"\n", argv[0], fil); fclose(lf); exit(9); } pos = lseek(ff, (off_t)0, SEEK_END); if (pos < 5) { fprintf(lf, "Filter file \"%s\" is empty\n", fil); fprintf(stderr, "%s: Filter file \"%s\" is empty\n", argv[0], fil); fclose(lf); close(ff); exit(8); } i = lseek(ff, (off_t)0, SEEK_SET); dat = (char *)calloc(1, pos + 2); if (!dat) { fprintf(lf,"Unable to grab memory for filter file \"%s\"\n", fil); fprintf(stderr,"%s: Unable to grab memory for filter file \"%s\"\n", argv[0], fil); fclose(lf); close(ff); exit(7); } pos = read(ff, dat, pos); close(ff); /* Attach to Spmi */ if (SpmiInit(30)) { fprintf(lf, "Unable to initialize Spmi interface\n"); fprintf(stderr, "%s: Unable to initialize Spmi interface\n", argv[0]); if (SpmiErrmsg[0]) { fprintf(lf, "%s\n", SpmiErrmsg); fprintf(stderr, "%s\n", SpmiErrmsg); } exit(8); } /* Make sure we detect serious signals that would otherwise kill us off */ #ifdef _NO_PROTO signal(SIGINT, (void(*)())must_exit); signal(SIGTERM, (void(*)())must_exit); signal(SIGSEGV, (void(*)())must_exit); signal(SIGQUIT, (void(*)())must_exit); #else signal(SIGINT, (void(*)(int))must_exit); signal(SIGTERM, (void(*)(int))must_exit); signal(SIGSEGV, (void(*)(int))must_exit); signal(SIGQUIT, (void(*)(int))must_exit); #endif /* _NO_PROTO */ /* Here begins the parsing of the configuration file. It is done by code generated by lex from the file lfiltd.lex and is invoked by successive calls of the function yylex(). */ while ((yychar = yylex()) > 0) { if (trace == 9) fprintf(lf, "state = %d, vstate = %d, type = %3d, string = \"%s\"\n", state, vstate, yychar, yytext); switch(yychar) { case COMMENT: { if (trace > 7) fprintf(lf, "COMMENT %s\n", yytext); break; } case EOL: { if (trace > 7) fprintf(lf, "EOL %s\n", yytext); if (brc1) { doerror("Missing closing parenteses"); break; } if ((lvptr) && (!lvptr->ready)) { shine_up(&lvptr->rsp); if (trace >= 5) dump_it(lvptr->rsp, 0); name_error = FALSE; if ((lvptr) && (lvptr->type == newvalue)) { newcount = check_names(lvptr->rsp, 0); if (name_error) doerror("Errors found in name lookup"); if (!curssp) { /* Not current StatSet; create one */ if (!(curssp = SpmiCreateStatSet())) { if (!(curssp = SpmiCreateStatSet())) { doerror("Can\'t create StatSet -- exiting"); doexit(27); } } curcount = 0; } if (!name_error) { lvptr->ssp = curssp; add_to_set(lvptr->rsp); if (name_error) { doerror("Unable to add to StatSet -- exiting"); doexit(28); } } if (lvptr) { totcount++; lvptr->ready = TRUE; } } } state = nostate; vstate = novar; brc1 = brc2 = 0; break; } case ASSIGN: { if (trace > 7) fprintf(lf, "ASSIGN %s\n", yytext); if (state != assign1) { doerror("Unexpected assignment operator"); break; } state = assign2; lvptr->type = newvalue; break; } case OPER: if (trace > 7) fprintf(lf, "OPER %s\n", yytext); { if ((vstate == var) && ((state == assign2) || (state == assign2))) { if (add_oper(yytext[0])) doexit(9); vstate = oper; break; } doerror("Unexpected operator"); break; } case LEFTBR: { if (trace > 7) fprintf(lf, "LEFTBR %s\n", yytext); if (state != assign2) { doerror("Unexpected character"); break; } if (vstate == var) { doerror("Character out of context"); break; } step_up(); break; } case RGHTBR: { if (trace > 7) fprintf(lf, "RGHTBR %s\n", yytext); if (state != assign2) { doerror("Unexpected character"); break; } if (!brc1) { doerror("Unmatched"); break; } if (vstate != var) { doerror("Character out of context"); break; } step_down(brc1 - 1); break; } case NUMBER: case FLOAT1: case FLOAT2: case FLOAT3: { if (trace > 7) fprintf(lf, "NUMBER %s\n", yytext); if (state != assign2) { doerror("Unexpected numeric constant"); break; } if (vstate == var) { doerror("Numeric constant out of context"); break; } if (yychar == FLOAT1) { wrki = atoi(yytext+1); wrkf = wrki; while(wrkf >= 1.0) wrkf = wrkf / 10; } else if (yychar == FLOAT3) { t = (char *)strchr((char *)yytext, '.') + 1; wrki = atoi(t); wrkf = wrki; while(wrkf >= 1.0) wrkf = wrkf / 10; wrki = atoi(yytext); wrkf = wrkf + wrki; } else { wrki = atoi(yytext); wrkf = wrki; } if (add_const(wrkf)) doexit(9); vstate = var; break; } case TARGET: { if (trace > 7) fprintf(lf, "TARGET %s\n", yytext); /* Initial processing for new assignment of variable to add */ if (state == nostate) { state = assign1; if (add_entry()) doexit(9); if (!(lvptr->newvar = strccvt(yytext))) { doerror("Out of memory"); doexit(9); } if (strlen(lvptr->newvar) >= EXCEPT_NAME_LEN) lvptr->newvar[EXCEPT_NAME_LEN -1] = '\0'; break; } /* fall through */ } case VARBL: { if (trace > 7) fprintf(lf, "VARBL %s\n", yytext); if (state != assign2) { doerror("Variable out of context"); break; } if (vstate == var) { doerror("Variable with no preceeding operator"); break; } if (add_var(strccvt(yytext))) doexit(9); vstate = var; break; } case WCCNT: case WCTOP: case WCBOT: case WCMUL: case WCADD: { if (trace > 7) fprintf(lf, "WCxxx %s\n", yytext); if (state != assign2) { doerror("Variable out of context"); break; } if (vstate == var) { doerror("Variable with no preceeding operator"); break; } if (yychar == WCCNT) { if (add_wild('#')) doexit(9); vstate = var; break; } step_up(); if (yychar == WCADD) { if (add_wild('+')) doexit(9); } else if (yychar == WCMUL) { if (add_wild('*')) doexit(9); } else if (yychar == WCTOP) { if (add_wild('T')) doexit(9); } else if (yychar == WCBOT) { if (add_wild('B')) doexit(9); } else doexit(9); step_down(brc1 - 1); vstate = var; break; } case DESCR: { if (trace > 7) fprintf(lf, "DESCR %s\n", yytext); if (state == nostate) { doerror("Variable description out of context"); break; } lvptr->descr = (char *)strmake((char *)yytext + 1); lvptr->descr[strlen(lvptr->descr) - 1] = '\0'; if (strlen(lvptr->descr) >= EXCEPT_DESC_LEN) lvptr->descr[EXCEPT_DESC_LEN -1] = '\0'; break; } default: break; } } /* All new values have now been defined. We go through the table and insert them into a table of Stat structures, which we allocate. */ FiltStats = (struct SpmiRawStat *)calloc(totcount+1, sizeof(struct SpmiRawStat)); if (!FiltStats) { lvptr = vptr; doerror("Unable to allocate memory for Stats -- exiting"); doexit(30); } if (!vptr) { lvptr = vptr; doerror("No statistics have been defined -- exiting"); doexit(31); } else { struct SpmiRawStat *fsp = FiltStats; arraycount = 0; tvptr = vptr; while(tvptr) { if (tvptr->type == newvalue) { strcpy(fsp->name, tvptr->newvar); if (tvptr->descr) strcpy(fsp->description, tvptr->descr); else strcpy(fsp->description, tvptr->newvar); fsp->min = 0; fsp->max = 100; fsp->value_type = SiQuantity; #ifdef mips fsp->data_type = SiLong; fsp->size = sizeof(long); #else fsp->data_type = SiFloat; fsp->size = sizeof(float); #endif fsp->offset = fsp->size * arraycount; fsp->type = SiFloat; tvptr->ix = arraycount++; fsp->asnno = arraycount; fsp++; } tvptr = tvptr->next; } } /* Well, if we did add values, "arraycount" is non-zero, and we should make sure we update the context with the count */ if (arraycount) { FiltContext[1].num_stats = arraycount; FiltContext[1].stats = FiltStats; /* Now initialize the Dynamic Data Supplier interface */ #ifdef _AIX dataarea = SpmiDdsInit(FiltContext, 2, NULL, 0, "/etc/LFilterDDS.shm"); #else dataarea = SpmiDdsInit(FiltContext, 2, NULL, 0, "/etc/LFilterDDS.shm", 16 * 1024); #endif if (!dataarea) { fprintf(lf, "%s (%s)\n", SpmiErrmsg, FiltContext[1].path); doexit(33); } d = (struct dat *)&dataarea->SiShArea[0]; } log_check(); /* Now we should be finished with the parsing and table building and ready to go for the start-feed sequences to the daemon. */ tvptr = vptr; curssp = NULL; if (trace) fprintf(lf, "Interval = %d\n", interval); while(tvptr) { if ((tvptr->ready) && (tvptr->ssp != curssp)) { t_value.it_interval.tv_sec = interval/1000; if (t_value.it_interval.tv_sec < 1) t_value.it_interval.tv_sec = 1; t_value.it_interval.tv_usec = 0; t_value.it_value.tv_sec = t_value.it_interval.tv_sec; t_value.it_value.tv_usec = 0; if (setitimer(ITIMER_REAL, &t_value, &o_value) < 0) { printf("error no %d while setting interval timer\n", errno); must_exit(8); } curssp = tvptr->ssp; #ifdef _NO_PROTO signal(SIGALRM, (void(*)())feeding); #else signal(SIGALRM, (void(*)(int))feeding); #endif /* _NO_PROTO */ break; } tvptr = tvptr->next; } while (TRUE) { gettimeofday(&dataarea->SiShT, NULL); pause(); } }