mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
This commit is based on the s390-tools-1.39.0 version. Changes on top of s390-tools-1.39.0: - Add MIT license to all source files - Add LICENSE file - Transform REAMDE to README.md (markdown) - Add AUTHORS.md file - Add CONTRIBUTING.md file - Move changelog from README to CHANGELOG.md file Reviewed-by: Stefan Haberland <sth@linux.vnet.ibm.com> Signed-off-by: Michael Holzheu <holzheu@linux.vnet.ibm.com>
551 lines
11 KiB
C
551 lines
11 KiB
C
/*
|
|
* cpuplugd - Linux for System z Hotplug Daemon
|
|
*
|
|
* Term parsing
|
|
*
|
|
* Copyright IBM Corp. 2007, 2017
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include "cpuplugd.h"
|
|
|
|
static enum op_prio op_prio_table[] =
|
|
{
|
|
[OP_NEG] = OP_PRIO_ADD,
|
|
[OP_GREATER] = OP_PRIO_CMP,
|
|
[OP_LESSER] = OP_PRIO_CMP,
|
|
[OP_PLUS] = OP_PRIO_ADD,
|
|
[OP_MINUS] = OP_PRIO_ADD,
|
|
[OP_MULT] = OP_PRIO_MULT,
|
|
[OP_DIV] = OP_PRIO_MULT,
|
|
[OP_AND] = OP_PRIO_AND,
|
|
[OP_OR] = OP_PRIO_OR,
|
|
};
|
|
|
|
static void free_term(struct term *fn)
|
|
{
|
|
if (!fn)
|
|
return;
|
|
switch (fn->op) {
|
|
case OP_SYMBOL_LOADAVG:
|
|
case OP_SYMBOL_RUNABLE:
|
|
case OP_SYMBOL_CPUS:
|
|
case OP_SYMBOL_USER:
|
|
case OP_SYMBOL_NICE:
|
|
case OP_SYMBOL_SYSTEM:
|
|
case OP_SYMBOL_IDLE:
|
|
case OP_SYMBOL_IOWAIT:
|
|
case OP_SYMBOL_IRQ:
|
|
case OP_SYMBOL_SOFTIRQ:
|
|
case OP_SYMBOL_STEAL:
|
|
case OP_SYMBOL_GUEST:
|
|
case OP_SYMBOL_GUEST_NICE:
|
|
case OP_CONST:
|
|
free(fn);
|
|
break;
|
|
case OP_NEG:
|
|
case OP_NOT:
|
|
free_term(fn->left);
|
|
free(fn);
|
|
break;
|
|
case OP_GREATER:
|
|
case OP_LESSER:
|
|
case OP_PLUS:
|
|
case OP_MINUS:
|
|
case OP_MULT:
|
|
case OP_DIV:
|
|
case OP_AND:
|
|
free_term(fn->left);
|
|
free_term(fn->right);
|
|
free(fn);
|
|
break;
|
|
case OP_OR:
|
|
free_term(fn->left);
|
|
free_term(fn->right);
|
|
free(fn);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void print_term(struct term *fn)
|
|
{
|
|
switch (fn->op) {
|
|
case OP_SYMBOL_LOADAVG:
|
|
printf("loadavg");
|
|
break;
|
|
case OP_SYMBOL_RUNABLE:
|
|
printf("runnable_proc");
|
|
break;
|
|
case OP_SYMBOL_CPUS:
|
|
printf("onumcpus");
|
|
break;
|
|
case OP_SYMBOL_USER:
|
|
printf("user");
|
|
break;
|
|
case OP_SYMBOL_NICE:
|
|
printf("nice");
|
|
break;
|
|
case OP_SYMBOL_SYSTEM:
|
|
printf("system");
|
|
break;
|
|
case OP_SYMBOL_IDLE:
|
|
printf("idle");
|
|
break;
|
|
case OP_SYMBOL_IOWAIT:
|
|
printf("iowait");
|
|
break;
|
|
case OP_SYMBOL_IRQ:
|
|
printf("irq");
|
|
break;
|
|
case OP_SYMBOL_SOFTIRQ:
|
|
printf("softirq");
|
|
break;
|
|
case OP_SYMBOL_STEAL:
|
|
printf("steal");
|
|
break;
|
|
case OP_SYMBOL_GUEST:
|
|
printf("guest");
|
|
break;
|
|
case OP_SYMBOL_GUEST_NICE:
|
|
printf("guest_nice");
|
|
break;
|
|
case OP_SYMBOL_SWAPRATE:
|
|
printf("swaprate");
|
|
break;
|
|
case OP_SYMBOL_FREEMEM:
|
|
printf("freemem");
|
|
break;
|
|
case OP_SYMBOL_APCR:
|
|
printf("apcr");
|
|
break;
|
|
case OP_SYMBOL_MEMINFO:
|
|
printf("meminfo.%s[%u]", fn->proc_name, fn->index);
|
|
break;
|
|
case OP_SYMBOL_VMSTAT:
|
|
printf("vmstat.%s[%u]", fn->proc_name, fn->index);
|
|
break;
|
|
case OP_SYMBOL_CPUSTAT:
|
|
printf("cpustat.%s[%u]", fn->proc_name, fn->index);
|
|
break;
|
|
case OP_SYMBOL_TIME:
|
|
printf("time[%u]", fn->index);
|
|
break;
|
|
case OP_CONST:
|
|
printf("%f", fn->value);
|
|
break;
|
|
case OP_NEG:
|
|
printf("-(");
|
|
print_term(fn->left);
|
|
printf(")");
|
|
break;
|
|
case OP_NOT:
|
|
printf("!(");
|
|
print_term(fn->left);
|
|
printf(")");
|
|
break;
|
|
case OP_PLUS:
|
|
case OP_MINUS:
|
|
case OP_MULT:
|
|
case OP_DIV:
|
|
case OP_AND:
|
|
case OP_OR:
|
|
case OP_GREATER:
|
|
case OP_LESSER:
|
|
printf("(");
|
|
print_term(fn->left);
|
|
switch (fn->op) {
|
|
case OP_AND:
|
|
printf(") & (");
|
|
break;
|
|
case OP_OR:
|
|
printf(") | (");
|
|
break;
|
|
case OP_GREATER:
|
|
printf(") > (");
|
|
break;
|
|
case OP_LESSER:
|
|
printf(") < (");
|
|
break;
|
|
case OP_PLUS:
|
|
printf(") + (");
|
|
break;
|
|
case OP_MINUS:
|
|
printf(") - (");
|
|
break;
|
|
case OP_MULT:
|
|
printf(") * (");
|
|
break;
|
|
case OP_DIV:
|
|
printf(") / (");
|
|
break;
|
|
// TODO OP_CONST, OP_SYMBOL_LOADAVG, ... possible here???
|
|
case OP_CONST:
|
|
printf("%f", fn->value);
|
|
break;
|
|
case OP_SYMBOL_LOADAVG:
|
|
case OP_SYMBOL_RUNABLE:
|
|
case OP_SYMBOL_CPUS:
|
|
case OP_SYMBOL_USER:
|
|
case OP_SYMBOL_NICE:
|
|
case OP_SYMBOL_SYSTEM:
|
|
case OP_SYMBOL_IDLE:
|
|
case OP_SYMBOL_IOWAIT:
|
|
case OP_SYMBOL_IRQ:
|
|
case OP_SYMBOL_SOFTIRQ:
|
|
case OP_SYMBOL_STEAL:
|
|
case OP_SYMBOL_GUEST:
|
|
case OP_SYMBOL_GUEST_NICE:
|
|
case OP_SYMBOL_APCR:
|
|
case OP_SYMBOL_SWAPRATE:
|
|
case OP_SYMBOL_FREEMEM:
|
|
case OP_SYMBOL_MEMINFO: // TODO use default: ???
|
|
case OP_SYMBOL_VMSTAT: // TODO use default: ???
|
|
case OP_SYMBOL_CPUSTAT: // TODO use default: ???
|
|
case OP_SYMBOL_TIME: // TODO use default: ???
|
|
case OP_NEG:
|
|
case OP_NOT:
|
|
case VAR_LOAD:
|
|
case VAR_RUN:
|
|
case VAR_ONLINE:
|
|
break;
|
|
}
|
|
print_term(fn->right);
|
|
printf(")");
|
|
break;
|
|
case VAR_LOAD:
|
|
case VAR_RUN:
|
|
case VAR_ONLINE:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static struct term *parse_var_term(char **p)
|
|
{
|
|
char *s, *var_rvalue;
|
|
struct term *fn;
|
|
unsigned int length;
|
|
char var_name[MAX_VARNAME + 1];
|
|
|
|
s = *p;
|
|
length = 0;
|
|
fn = NULL;
|
|
while (isalnum(*s) || *s == '_') {
|
|
var_name[length] = *s;
|
|
length++;
|
|
s++;
|
|
if (length > MAX_VARNAME)
|
|
cpuplugd_exit("Variable name too long (max. length is "
|
|
"%i chars): %s\n", MAX_VARNAME, *p);
|
|
}
|
|
var_name[length] = '\0';
|
|
var_rvalue = get_var_rvalue(var_name);
|
|
if (var_rvalue) {
|
|
fn = parse_term(&var_rvalue, OP_PRIO_NONE);
|
|
if (var_rvalue[0] != '\n')
|
|
cpuplugd_exit("parsing error at %s, position: %s\n",
|
|
var_name, var_rvalue);
|
|
*p = s;
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
struct term *parse_term(char **p, enum op_prio prio)
|
|
{
|
|
struct term *fn, *new;
|
|
enum operation op;
|
|
char *s, *endptr;
|
|
double value;
|
|
unsigned int length, i, index;
|
|
|
|
s = *p;
|
|
fn = NULL;
|
|
if (*s == '-') {
|
|
s++;
|
|
fn = malloc(sizeof(struct term));
|
|
if (fn == NULL)
|
|
goto out_error;
|
|
if (isdigit(*s)) {
|
|
value = 0;
|
|
length = 0;
|
|
sscanf(s, "%lf%n", &value, &length);
|
|
fn->op = OP_CONST;
|
|
fn->value = -value;
|
|
s += length;
|
|
} else {
|
|
fn->op = OP_NEG;
|
|
fn->left = parse_term(&s, prio);
|
|
if (fn->left == NULL)
|
|
goto out_error;
|
|
}
|
|
} else if (*s == '!') {
|
|
s++;
|
|
fn = malloc(sizeof(struct term));
|
|
if (fn == NULL)
|
|
goto out_error;
|
|
fn->op = OP_NOT;
|
|
fn->left = parse_term(&s, prio);
|
|
if (fn->left == NULL)
|
|
goto out_error;
|
|
} else if (isdigit(*s)) {
|
|
value = 0;
|
|
length = 0;
|
|
sscanf(s, "%lf%n", &value, &length);
|
|
for (i = 0; i < length; i++)
|
|
s++;
|
|
fn = malloc(sizeof(struct term));
|
|
if (fn == NULL)
|
|
goto out_error;
|
|
fn->op = OP_CONST;
|
|
fn->value = value;
|
|
} else if (*s == '(') {
|
|
s++;
|
|
fn = parse_term(&s, OP_PRIO_NONE);
|
|
if (fn == NULL || *s != ')')
|
|
goto out_error;
|
|
s++;
|
|
} else {
|
|
/* Check for variable name */
|
|
fn = parse_var_term(&s);
|
|
if (fn == NULL) {
|
|
for (i = 0; i < sym_names_count; i++)
|
|
if (strncmp(s, sym_names[i].name,
|
|
strlen(sym_names[i].name)) == 0)
|
|
break;
|
|
if (i >= sym_names_count)
|
|
/* Term doesn't make sense. */
|
|
goto out_error;
|
|
/*
|
|
* Parse meminfo/vmstat/cpustat with optional history
|
|
* index [x]
|
|
*/
|
|
fn = malloc(sizeof(struct term));
|
|
if (fn == NULL)
|
|
goto out_error;
|
|
fn->op = sym_names[i].symop;
|
|
s += strlen(sym_names[i].name);
|
|
length = 0;
|
|
if (fn->op == OP_SYMBOL_MEMINFO ||
|
|
fn->op == OP_SYMBOL_VMSTAT ||
|
|
fn->op == OP_SYMBOL_CPUSTAT) {
|
|
while (isalpha(s[length]) || s[length] == '_')
|
|
length++;
|
|
fn->proc_name = malloc(length + 1);
|
|
if (fn->proc_name == NULL)
|
|
goto out_error;
|
|
strncpy(fn->proc_name, s, length);
|
|
fn->proc_name[length] = '\0';
|
|
}
|
|
if (fn->op == OP_SYMBOL_MEMINFO ||
|
|
fn->op == OP_SYMBOL_VMSTAT ||
|
|
fn->op == OP_SYMBOL_CPUSTAT ||
|
|
fn->op == OP_SYMBOL_TIME) {
|
|
if (s[length] == '[') {
|
|
length++;
|
|
if (!isdigit(s[length]))
|
|
goto out_error;
|
|
index = strtol(s + length, &endptr, 10);
|
|
length = endptr - s;
|
|
if (s[length] != ']')
|
|
goto out_error;
|
|
fn->index = index;
|
|
if (history_max < index)
|
|
history_max = index;
|
|
length++;
|
|
}
|
|
s += length;
|
|
}
|
|
}
|
|
}
|
|
while (1) {
|
|
switch (*s) {
|
|
case '>':
|
|
op = OP_GREATER;
|
|
break;
|
|
case '<':
|
|
op = OP_LESSER;
|
|
break;
|
|
case '+':
|
|
op = OP_PLUS;
|
|
break;
|
|
case '-':
|
|
op = OP_MINUS;
|
|
break;
|
|
case '*':
|
|
op = OP_MULT;
|
|
break;
|
|
case '/':
|
|
op = OP_DIV;
|
|
break;
|
|
case '|':
|
|
op = OP_OR;
|
|
break;
|
|
case '&':
|
|
op = OP_AND;
|
|
break;
|
|
default:
|
|
goto out;
|
|
}
|
|
if (prio >= op_prio_table[op])
|
|
break;
|
|
s++;
|
|
new = malloc(sizeof(struct term));
|
|
new->op = op;
|
|
new->left = fn;
|
|
if (new == NULL)
|
|
goto out_error;
|
|
new->right = parse_term(&s, op_prio_table[op]);
|
|
if (new->right == NULL) {
|
|
free(new);
|
|
goto out_error;
|
|
}
|
|
fn = new;
|
|
}
|
|
out:
|
|
*p = s;
|
|
return fn;
|
|
out_error:
|
|
if (fn)
|
|
free_term(fn);
|
|
return NULL;
|
|
}
|
|
|
|
static double get_value(struct term *fn)
|
|
{
|
|
double value = 0;
|
|
char *procinfo;
|
|
unsigned int history_index;
|
|
|
|
if (fn->index <= history_current)
|
|
history_index = history_current - fn->index;
|
|
else
|
|
history_index = history_max + 1 - (fn->index - history_current);
|
|
|
|
switch (fn->op) {
|
|
case OP_SYMBOL_MEMINFO:
|
|
procinfo = meminfo + history_index * meminfo_size;
|
|
value = get_proc_value(procinfo, fn->proc_name, ':');
|
|
break;
|
|
case OP_SYMBOL_VMSTAT:
|
|
procinfo = vmstat + history_index * vmstat_size;
|
|
value = get_proc_value(procinfo, fn->proc_name, ' ');
|
|
break;
|
|
case OP_SYMBOL_CPUSTAT:
|
|
procinfo = cpustat + history_index * cpustat_size;
|
|
value = get_proc_value(procinfo, fn->proc_name, ' ');
|
|
break;
|
|
case OP_SYMBOL_TIME:
|
|
value = timestamps[history_index];
|
|
break;
|
|
default:
|
|
cpuplugd_exit("Invalid term specified: %i\n", fn->op);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
double eval_double(struct term *fn, struct symbols *symbols)
|
|
{
|
|
double a, b, sum;
|
|
|
|
switch (fn->op) {
|
|
case OP_SYMBOL_LOADAVG:
|
|
return symbols->loadavg;
|
|
case OP_SYMBOL_RUNABLE:
|
|
return symbols->runnable_proc;
|
|
case OP_SYMBOL_CPUS:
|
|
return symbols->onumcpus;
|
|
case OP_SYMBOL_USER:
|
|
return symbols->user;
|
|
case OP_SYMBOL_NICE:
|
|
return symbols->nice;
|
|
case OP_SYMBOL_SYSTEM:
|
|
return symbols->system;
|
|
case OP_SYMBOL_IDLE:
|
|
return symbols->idle;
|
|
case OP_SYMBOL_IOWAIT:
|
|
return symbols->iowait;
|
|
case OP_SYMBOL_IRQ:
|
|
return symbols->irq;
|
|
case OP_SYMBOL_SOFTIRQ:
|
|
return symbols->softirq;
|
|
case OP_SYMBOL_STEAL:
|
|
return symbols->steal;
|
|
case OP_SYMBOL_GUEST:
|
|
return symbols->guest;
|
|
case OP_SYMBOL_GUEST_NICE:
|
|
return symbols->guest_nice;
|
|
case OP_SYMBOL_FREEMEM:
|
|
return symbols->freemem;
|
|
case OP_SYMBOL_APCR:
|
|
return symbols->apcr;
|
|
case OP_SYMBOL_SWAPRATE:
|
|
return symbols->swaprate;
|
|
case OP_SYMBOL_MEMINFO:
|
|
case OP_SYMBOL_VMSTAT:
|
|
case OP_SYMBOL_CPUSTAT:
|
|
case OP_SYMBOL_TIME:
|
|
return get_value(fn);
|
|
case OP_CONST:
|
|
return fn->value;
|
|
case OP_NEG:
|
|
return -eval_double(fn->left, symbols);
|
|
case OP_PLUS:
|
|
return eval_double(fn->left, symbols) +
|
|
eval_double(fn->right, symbols);
|
|
case OP_MINUS:
|
|
return eval_double(fn->left, symbols) -
|
|
eval_double(fn->right, symbols);
|
|
case OP_MULT:
|
|
a = eval_double(fn->left, symbols);
|
|
b = eval_double(fn->right, symbols);
|
|
sum = a*b;
|
|
return sum;
|
|
/*return eval_double(fn->left, symbols) *
|
|
eval_double(fn->right, symbols);*/
|
|
case OP_DIV:
|
|
a = eval_double(fn->left, symbols);
|
|
b = eval_double(fn->right, symbols);
|
|
sum = a/b;
|
|
return sum;
|
|
/*return eval_double(fn->left, symbols) /
|
|
eval_double(fn->right, symbols); */
|
|
case OP_NOT:
|
|
case OP_AND:
|
|
case OP_OR:
|
|
case OP_GREATER:
|
|
case OP_LESSER:
|
|
case VAR_LOAD:
|
|
case VAR_RUN:
|
|
case VAR_ONLINE:
|
|
cpuplugd_exit("Invalid term specified: %i\n", fn->op);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int eval_term(struct term *fn, struct symbols *symbols)
|
|
{
|
|
if (fn == NULL || symbols == NULL)
|
|
return 0.0;
|
|
switch (fn->op) {
|
|
case OP_NOT:
|
|
return !eval_term(fn->left, symbols);
|
|
case OP_OR:
|
|
return eval_term(fn->left, symbols) == 1 ||
|
|
eval_term(fn->right, symbols) == 1;
|
|
case OP_AND:
|
|
return eval_term(fn->left, symbols) == 1 &&
|
|
eval_term(fn->right, symbols) == 1;
|
|
case OP_GREATER:
|
|
return eval_double(fn->left, symbols) >
|
|
eval_double(fn->right, symbols);
|
|
case OP_LESSER:
|
|
return eval_double(fn->left, symbols) <
|
|
eval_double(fn->right, symbols);
|
|
default:
|
|
return eval_double(fn, symbols) != 0.0;
|
|
}
|
|
}
|